diff --git a/spine-ue4/Content/Maps/example.umap b/spine-ue4/Content/Maps/example.umap index c23f2cc55..16fd5f484 100644 Binary files a/spine-ue4/Content/Maps/example.umap and b/spine-ue4/Content/Maps/example.umap differ diff --git a/spine-ue4/Content/SpineBoy/SpineboyBlueprint.uasset b/spine-ue4/Content/SpineBoy/SpineboyBlueprint.uasset index c5029671a..fc09d402f 100644 Binary files a/spine-ue4/Content/SpineBoy/SpineboyBlueprint.uasset and b/spine-ue4/Content/SpineBoy/SpineboyBlueprint.uasset differ diff --git a/spine-ue4/Content/SpineBoy/Textures/spineboy.uasset b/spine-ue4/Content/SpineBoy/Textures/spineboy.uasset index 85afea48f..7439aa3c4 100644 Binary files a/spine-ue4/Content/SpineBoy/Textures/spineboy.uasset and b/spine-ue4/Content/SpineBoy/Textures/spineboy.uasset differ diff --git a/spine-ue4/Content/SpineBoy/spineboy.uasset b/spine-ue4/Content/SpineBoy/spineboy.uasset index b7ed2fab3..b4d85df43 100644 Binary files a/spine-ue4/Content/SpineBoy/spineboy.uasset and b/spine-ue4/Content/SpineBoy/spineboy.uasset differ diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonAnimationComponent.cpp b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonAnimationComponent.cpp index 381eb7ff8..a850bb707 100644 --- a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonAnimationComponent.cpp +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonAnimationComponent.cpp @@ -9,14 +9,37 @@ void UTrackEntry::SetTrackEntry(spTrackEntry* entry) { void callback(spAnimationState* state, spEventType type, spTrackEntry* entry, spEvent* event) { USpineSkeletonAnimationComponent* component = (USpineSkeletonAnimationComponent*)state->rendererObject; - - if (type == SP_ANIMATION_COMPLETE) { - if (entry->rendererObject) { - UTrackEntry* uEntry = (UTrackEntry*)entry->rendererObject; - component->AnimationStartEvent.Broadcast(uEntry); - uEntry->AnimationStartEvent.Broadcast(uEntry); + + if (entry->rendererObject) { + UTrackEntry* uEntry = (UTrackEntry*)entry->rendererObject; + if (type == SP_ANIMATION_START) { + component->AnimationStart.Broadcast(uEntry); + uEntry->AnimationStart.Broadcast(uEntry); } - } + else if (type == SP_ANIMATION_INTERRUPT) { + component->AnimationInterrupt.Broadcast(uEntry); + uEntry->AnimationInterrupt.Broadcast(uEntry); + } else if (type == SP_ANIMATION_EVENT) { + FSpineEvent evt; + evt.SetEvent(event); + component->AnimationEvent.Broadcast(uEntry, evt); + uEntry->AnimationEvent.Broadcast(uEntry, evt); + } + else if (type == SP_ANIMATION_COMPLETE) { + component->AnimationComplete.Broadcast(uEntry); + uEntry->AnimationComplete.Broadcast(uEntry); + } + else if (type == SP_ANIMATION_END) { + component->AnimationEnd.Broadcast(uEntry); + uEntry->AnimationEnd.Broadcast(uEntry); + } + else if (type == SP_ANIMATION_DISPOSE) { + component->AnimationDispose.Broadcast(uEntry); + uEntry->AnimationDispose.Broadcast(uEntry); + uEntry->SetTrackEntry(nullptr); + component->GCTrackEntry(uEntry); + } + } } USpineSkeletonAnimationComponent::USpineSkeletonAnimationComponent () { @@ -28,6 +51,7 @@ USpineSkeletonAnimationComponent::USpineSkeletonAnimationComponent () { void USpineSkeletonAnimationComponent::BeginPlay() { Super::BeginPlay(); + trackEntries.Empty(); } void USpineSkeletonAnimationComponent::TickComponent (float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) { @@ -53,6 +77,7 @@ void USpineSkeletonAnimationComponent::CheckState () { state = spAnimationState_create(stateData); state->rendererObject = (void*)this; state->listener = callback; + trackEntries.Empty(); } lastAtlas = Atlas; @@ -75,6 +100,8 @@ void USpineSkeletonAnimationComponent::DisposeState () { spSkeleton_dispose(skeleton); skeleton = nullptr; } + + trackEntries.Empty(); } void USpineSkeletonAnimationComponent::FinishDestroy () { @@ -88,6 +115,7 @@ UTrackEntry* USpineSkeletonAnimationComponent::SetAnimation (int trackIndex, FSt spTrackEntry* entry = spAnimationState_setAnimationByName(state, trackIndex, TCHAR_TO_UTF8(*animationName), loop ? 1 : 0); UTrackEntry* uEntry = NewObject(); uEntry->SetTrackEntry(entry); + trackEntries.Add(uEntry); return uEntry; } else return NewObject(); @@ -99,6 +127,7 @@ UTrackEntry* USpineSkeletonAnimationComponent::AddAnimation (int trackIndex, FSt spTrackEntry* entry = spAnimationState_addAnimationByName(state, trackIndex, TCHAR_TO_UTF8(*animationName), loop ? 1 : 0, delay); UTrackEntry* uEntry = NewObject(); uEntry->SetTrackEntry(entry); + trackEntries.Add(uEntry); return uEntry; } else return NewObject(); } @@ -109,6 +138,7 @@ UTrackEntry* USpineSkeletonAnimationComponent::SetEmptyAnimation (int trackIndex spTrackEntry* entry = spAnimationState_setEmptyAnimation(state, trackIndex, mixDuration); UTrackEntry* uEntry = NewObject(); uEntry->SetTrackEntry(entry); + trackEntries.Add(uEntry); return uEntry; } else return NewObject(); } @@ -119,6 +149,7 @@ UTrackEntry* USpineSkeletonAnimationComponent::AddEmptyAnimation (int trackIndex spTrackEntry* entry = spAnimationState_addEmptyAnimation(state, trackIndex, mixDuration, delay); UTrackEntry* uEntry = NewObject(); uEntry->SetTrackEntry(entry); + trackEntries.Add(uEntry); return uEntry; } else return NewObject(); } diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonAnimationComponent.h b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonAnimationComponent.h index ad7a7c50a..05c1aac2e 100644 --- a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonAnimationComponent.h +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonAnimationComponent.h @@ -7,7 +7,43 @@ #include "spine/spine.h" #include "SpineSkeletonAnimationComponent.generated.h" -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSpineAnimationStartEvent, UTrackEntry*, entry); +USTRUCT(BlueprintType, Category="Spine") +struct SPINEPLUGIN_API FSpineEvent { + GENERATED_BODY(); + +public: + void SetEvent(spEvent* event) { + Name = FString(UTF8_TO_TCHAR(event->data->name)); + if (event->stringValue) { + StringValue = FString(UTF8_TO_TCHAR(event->stringValue)); + } + this->IntValue = event->intValue; + this->FloatValue = event->floatValue; + this->Time = event->time; + } + + UPROPERTY(BlueprintReadonly) + FString Name; + + UPROPERTY(BlueprintReadOnly) + FString StringValue; + + UPROPERTY(BlueprintReadOnly) + int IntValue; + + UPROPERTY(BlueprintReadOnly) + float FloatValue; + + UPROPERTY(BlueprintReadOnly) + float Time; +}; + +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSpineAnimationStartDelegate, UTrackEntry*, entry); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FSpineAnimationEventDelegate, UTrackEntry*, entry, FSpineEvent, evt); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSpineAnimationInterruptDelegate, UTrackEntry*, entry); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSpineAnimationCompleteDelegate, UTrackEntry*, entry); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSpineAnimationEndDelegate, UTrackEntry*, entry); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSpineAnimationDisposeDelegate, UTrackEntry*, entry); UCLASS(ClassGroup=(Spine), meta=(BlueprintSpawnableComponent), BlueprintType) class SPINEPLUGIN_API UTrackEntry: public UObject { @@ -15,17 +51,124 @@ class SPINEPLUGIN_API UTrackEntry: public UObject { public: - UTrackEntry () { } - - spTrackEntry* entry = nullptr; + UTrackEntry () { } - void SetTrackEntry(spTrackEntry* entry); + void SetTrackEntry (spTrackEntry* entry); + spTrackEntry* GetTrackEntry(); UFUNCTION(BlueprintCallable, Category="Components|Spine") - int GetTrackIndex() { return entry ? entry->trackIndex : 0; } + int GetTrackIndex () { return entry ? entry->trackIndex : 0; } + + UFUNCTION(BlueprintCallable, Category = "Components|Spine") + bool GetLoop () { return entry ? entry->loop != 0 : false; } + UFUNCTION(BlueprintCallable, Category = "Components|Spine") + void SetLoop (bool loop) { if (entry) entry->loop = loop ? 1 : 0; } + + UFUNCTION(BlueprintCallable, Category = "Components|Spine") + float GetEventThreshold () { return entry ? entry->eventThreshold : 0; } + UFUNCTION(BlueprintCallable, Category = "Components|Spine") + void SetEventThreshold(float eventThreshold) { if (entry) entry->eventThreshold = eventThreshold; } + + UFUNCTION(BlueprintCallable, Category = "Components|Spine") + float GetAttachmentThreshold() { return entry ? entry->attachmentThreshold : 0; } + UFUNCTION(BlueprintCallable, Category = "Components|Spine") + void SetAttachmentThreshold(float attachmentThreshold) { if (entry) entry->attachmentThreshold = attachmentThreshold; } + + UFUNCTION(BlueprintCallable, Category = "Components|Spine") + float GetDrawOrderThreshold() { return entry ? entry->drawOrderThreshold : 0; } + UFUNCTION(BlueprintCallable, Category = "Components|Spine") + void SetDrawOrderThreshold(float drawOrderThreshold) { if (entry) entry->drawOrderThreshold = drawOrderThreshold; } + + UFUNCTION(BlueprintCallable, Category = "Components|Spine") + float GetAnimationStart() { return entry ? entry->animationStart : 0; } + UFUNCTION(BlueprintCallable, Category = "Components|Spine") + void SetAnimationStart(float animationStart) { if (entry) entry->animationStart = animationStart; } + + UFUNCTION(BlueprintCallable, Category = "Components|Spine") + float GetAnimationEnd() { return entry ? entry->animationEnd : 0; } + UFUNCTION(BlueprintCallable, Category = "Components|Spine") + void SetAnimationEnd(float animationEnd) { if (entry) entry->animationEnd = animationEnd; } + + UFUNCTION(BlueprintCallable, Category = "Components|Spine") + float GetAnimationLast() { return entry ? entry->animationLast : 0; } + UFUNCTION(BlueprintCallable, Category = "Components|Spine") + void SetAnimationLast(float animationLast) { if (entry) entry->animationLast = animationLast; } + + UFUNCTION(BlueprintCallable, Category = "Components|Spine") + float GetNextAnimationLast() { return entry ? entry->nextAnimationLast : 0; } + UFUNCTION(BlueprintCallable, Category = "Components|Spine") + void SetNextAnimationLast(float nextAnimationLast) { if (entry) entry->nextAnimationLast = nextAnimationLast; } + + UFUNCTION(BlueprintCallable, Category = "Components|Spine") + float GetDelay() { return entry ? entry->delay : 0; } + UFUNCTION(BlueprintCallable, Category = "Components|Spine") + void SetDelay(float delay) { if (entry) entry->delay = delay; } + + UFUNCTION(BlueprintCallable, Category = "Components|Spine") + float GetTrackTime() { return entry ? entry->trackTime : 0; } + UFUNCTION(BlueprintCallable, Category = "Components|Spine") + void SetTrackTime(float trackTime) { if (entry) entry->trackTime = trackTime; } + + UFUNCTION(BlueprintCallable, Category = "Components|Spine") + float GetTrackLast() { return entry ? entry->trackLast : 0; } + UFUNCTION(BlueprintCallable, Category = "Components|Spine") + void SetTrackLast(float trackLast) { if (entry) entry->trackLast = trackLast; } + + UFUNCTION(BlueprintCallable, Category = "Components|Spine") + float GetNextTrackLast() { return entry ? entry->nextTrackLast : 0; } + UFUNCTION(BlueprintCallable, Category = "Components|Spine") + void SetNextTrackLast(float nextTrackLast) { if (entry) entry->nextTrackLast = nextTrackLast; } + + UFUNCTION(BlueprintCallable, Category = "Components|Spine") + float GetTrackEnd() { return entry ? entry->trackEnd : 0; } + UFUNCTION(BlueprintCallable, Category = "Components|Spine") + void SetTrackEnd(float trackEnd) { if (entry) entry->trackEnd = trackEnd; } + + UFUNCTION(BlueprintCallable, Category = "Components|Spine") + float GetTimeScale() { return entry ? entry->timeScale : 0; } + UFUNCTION(BlueprintCallable, Category = "Components|Spine") + void SetTimeScale(float timeScale) { if (entry) entry->timeScale = timeScale; } + + UFUNCTION(BlueprintCallable, Category = "Components|Spine") + float GetAlpha() { return entry ? entry->alpha : 0; } + UFUNCTION(BlueprintCallable, Category = "Components|Spine") + void SetAlpha(float alpha) { if (entry) entry->alpha = alpha; } + + UFUNCTION(BlueprintCallable, Category = "Components|Spine") + float GetMixTime() { return entry ? entry->mixTime : 0; } + UFUNCTION(BlueprintCallable, Category = "Components|Spine") + void SetMixTime(float mixTime) { if (entry) entry->mixTime = mixTime; } + + UFUNCTION(BlueprintCallable, Category = "Components|Spine") + float GetMixDuration() { return entry ? entry->mixDuration : 0; } + UFUNCTION(BlueprintCallable, Category = "Components|Spine") + void SetMixDuration(float mixDuration) { if (entry) entry->mixDuration = mixDuration; } + + UFUNCTION(BlueprintCallable, Category = "Components|Spine") + float GetMixAlpha() { return entry ? entry->mixAlpha : 0; } + UFUNCTION(BlueprintCallable, Category = "Components|Spine") + void SetMixAlpha(float mixAlpha) { if (entry) entry->mixAlpha = mixAlpha; } UPROPERTY(BlueprintAssignable, Category = "Components|Spine") - FSpineAnimationStartEvent AnimationStartEvent; + FSpineAnimationStartDelegate AnimationStart; + + UPROPERTY(BlueprintAssignable, Category = "Components|Spine") + FSpineAnimationInterruptDelegate AnimationInterrupt; + + UPROPERTY(BlueprintAssignable, Category = "Components|Spine") + FSpineAnimationEventDelegate AnimationEvent; + + UPROPERTY(BlueprintAssignable, Category = "Components|Spine") + FSpineAnimationCompleteDelegate AnimationComplete; + + UPROPERTY(BlueprintAssignable, Category = "Components|Spine") + FSpineAnimationEndDelegate AnimationEnd; + + UPROPERTY(BlueprintAssignable, Category = "Components|Spine") + FSpineAnimationDisposeDelegate AnimationDispose; + +protected: + spTrackEntry* entry = nullptr; }; class USpineAtlasAsset; @@ -66,12 +209,35 @@ public: void ClearTrack (int trackIndex); UPROPERTY(BlueprintAssignable, Category = "Components|Spine") - FSpineAnimationStartEvent AnimationStartEvent; + FSpineAnimationStartDelegate AnimationStart; + + UPROPERTY(BlueprintAssignable, Category = "Components|Spine") + FSpineAnimationInterruptDelegate AnimationInterrupt; + + UPROPERTY(BlueprintAssignable, Category = "Components|Spine") + FSpineAnimationEventDelegate AnimationEvent; + + UPROPERTY(BlueprintAssignable, Category = "Components|Spine") + FSpineAnimationCompleteDelegate AnimationComplete; + + UPROPERTY(BlueprintAssignable, Category = "Components|Spine") + FSpineAnimationEndDelegate AnimationEnd; + + UPROPERTY(BlueprintAssignable, Category = "Components|Spine") + FSpineAnimationDisposeDelegate AnimationDispose; + // used in C event callback. Needs to be public as we can't call + // protected methods from plain old C function. + void GCTrackEntry(UTrackEntry* entry) { trackEntries.Remove(entry); } protected: virtual void CheckState () override; virtual void DisposeState () override; spAnimationStateData* stateData; - spAnimationState* state; + spAnimationState* state; + + // keep track of track entries so they won't get GCed while + // in transit within a blueprint + UPROPERTY() + TSet trackEntries; };