mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-03-05 18:26:52 +08:00
[ue4] Port of SkeletonAnimationComponent to SpineWidget. Tick needs to be explicitely called by user in blueprint.
This commit is contained in:
parent
cdaef06850
commit
39195fd710
@ -157,11 +157,6 @@ int32 SSpineWidget::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeo
|
|||||||
self->UpdateMesh(LayerId, OutDrawElements, AllottedGeometry, widget->skeleton);
|
self->UpdateMesh(LayerId, OutDrawElements, AllottedGeometry, widget->skeleton);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*if (renderData.VertexData.Num() > 0 && renderData.IndexData.Num() > 0 && renderData.RenderingResourceHandle.IsValid()) {
|
|
||||||
FSlateDrawElement::MakeCustomVerts(OutDrawElements, LayerId, renderData.RenderingResourceHandle, renderData.VertexData,
|
|
||||||
renderData.IndexData, nullptr, 0, 0);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
return LayerId;
|
return LayerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -36,6 +36,10 @@
|
|||||||
|
|
||||||
#define LOCTEXT_NAMESPACE "Spine"
|
#define LOCTEXT_NAMESPACE "Spine"
|
||||||
|
|
||||||
|
using namespace spine;
|
||||||
|
|
||||||
|
void callback(AnimationState* state, spine::EventType type, TrackEntry* entry, Event* event);
|
||||||
|
|
||||||
USpineWidget::USpineWidget(const FObjectInitializer& ObjectInitializer): Super(ObjectInitializer) {
|
USpineWidget::USpineWidget(const FObjectInitializer& ObjectInitializer): Super(ObjectInitializer) {
|
||||||
static ConstructorHelpers::FObjectFinder<UMaterialInterface> NormalMaterialRef(TEXT("/SpinePlugin/UI_SpineUnlitNormalMaterial"));
|
static ConstructorHelpers::FObjectFinder<UMaterialInterface> NormalMaterialRef(TEXT("/SpinePlugin/UI_SpineUnlitNormalMaterial"));
|
||||||
NormalBlendMaterial = NormalMaterialRef.Object;
|
NormalBlendMaterial = NormalMaterialRef.Object;
|
||||||
@ -52,6 +56,8 @@ USpineWidget::USpineWidget(const FObjectInitializer& ObjectInitializer): Super(O
|
|||||||
TextureParameterName = FName(TEXT("SpriteTexture"));
|
TextureParameterName = FName(TEXT("SpriteTexture"));
|
||||||
|
|
||||||
worldVertices.ensureCapacity(1024 * 2);
|
worldVertices.ensureCapacity(1024 * 2);
|
||||||
|
|
||||||
|
bAutoPlaying = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void USpineWidget::SynchronizeProperties() {
|
void USpineWidget::SynchronizeProperties() {
|
||||||
@ -60,11 +66,12 @@ void USpineWidget::SynchronizeProperties() {
|
|||||||
if (slateWidget.IsValid()) {
|
if (slateWidget.IsValid()) {
|
||||||
CheckState();
|
CheckState();
|
||||||
if (skeleton) {
|
if (skeleton) {
|
||||||
InternalTick(0);
|
Tick(0, false);
|
||||||
slateWidget->SetData(this);
|
slateWidget->SetData(this);
|
||||||
} else {
|
} else {
|
||||||
slateWidget->SetData(nullptr);
|
slateWidget->SetData(nullptr);
|
||||||
}
|
}
|
||||||
|
trackEntries.Empty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,10 +91,12 @@ const FText USpineWidget::GetPaletteCategory() {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void USpineWidget::InternalTick(float DeltaTime, bool CallDelegates, bool Preview) {
|
void USpineWidget::Tick(float DeltaTime, bool CallDelegates) {
|
||||||
CheckState();
|
CheckState();
|
||||||
|
|
||||||
if (skeleton) {
|
if (state && bAutoPlaying) {
|
||||||
|
state->update(DeltaTime);
|
||||||
|
state->apply(*skeleton);
|
||||||
if (CallDelegates) BeforeUpdateWorldTransform.Broadcast(this);
|
if (CallDelegates) BeforeUpdateWorldTransform.Broadcast(this);
|
||||||
skeleton->updateWorldTransform();
|
skeleton->updateWorldTransform();
|
||||||
if (CallDelegates) AfterUpdateWorldTransform.Broadcast(this);
|
if (CallDelegates) AfterUpdateWorldTransform.Broadcast(this);
|
||||||
@ -115,8 +124,15 @@ void USpineWidget::CheckState() {
|
|||||||
DisposeState();
|
DisposeState();
|
||||||
|
|
||||||
if (Atlas && SkeletonData) {
|
if (Atlas && SkeletonData) {
|
||||||
spine::SkeletonData* data = SkeletonData->GetSkeletonData(Atlas->GetAtlas());
|
spine::SkeletonData *data = SkeletonData->GetSkeletonData(Atlas->GetAtlas());
|
||||||
if (data) skeleton = new (__FILE__, __LINE__) spine::Skeleton(data);
|
if (data) {
|
||||||
|
skeleton = new (__FILE__, __LINE__) Skeleton(data);
|
||||||
|
AnimationStateData* stateData = SkeletonData->GetAnimationStateData(Atlas->GetAtlas());
|
||||||
|
state = new (__FILE__, __LINE__) AnimationState(stateData);
|
||||||
|
state->setRendererObject((void*)this);
|
||||||
|
state->setListener(callback);
|
||||||
|
trackEntries.Empty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lastAtlas = Atlas;
|
lastAtlas = Atlas;
|
||||||
@ -126,10 +142,17 @@ void USpineWidget::CheckState() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void USpineWidget::DisposeState() {
|
void USpineWidget::DisposeState() {
|
||||||
|
if (state) {
|
||||||
|
delete state;
|
||||||
|
state = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
if (skeleton) {
|
if (skeleton) {
|
||||||
delete skeleton;
|
delete skeleton;
|
||||||
skeleton = nullptr;
|
skeleton = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trackEntries.Empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void USpineWidget::FinishDestroy() {
|
void USpineWidget::FinishDestroy() {
|
||||||
@ -278,4 +301,129 @@ float USpineWidget::GetAnimationDuration(FString AnimationName) {
|
|||||||
else return animation->getDuration();
|
else return animation->getDuration();
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void USpineWidget::SetAutoPlay(bool bInAutoPlays)
|
||||||
|
{
|
||||||
|
bAutoPlaying = bInAutoPlays;
|
||||||
|
}
|
||||||
|
|
||||||
|
void USpineWidget::SetPlaybackTime(float InPlaybackTime, bool bCallDelegates)
|
||||||
|
{
|
||||||
|
CheckState();
|
||||||
|
|
||||||
|
if (state && state->getCurrent(0)) {
|
||||||
|
spine::Animation* CurrentAnimation = state->getCurrent(0)->getAnimation();
|
||||||
|
const float CurrentTime = state->getCurrent(0)->getTrackTime();
|
||||||
|
InPlaybackTime = FMath::Clamp(InPlaybackTime, 0.0f, CurrentAnimation->getDuration());
|
||||||
|
const float DeltaTime = InPlaybackTime - CurrentTime;
|
||||||
|
state->update(DeltaTime);
|
||||||
|
state->apply(*skeleton);
|
||||||
|
|
||||||
|
//Call delegates and perform the world transform
|
||||||
|
if (bCallDelegates)
|
||||||
|
{
|
||||||
|
BeforeUpdateWorldTransform.Broadcast(this);
|
||||||
|
}
|
||||||
|
skeleton->updateWorldTransform();
|
||||||
|
if (bCallDelegates)
|
||||||
|
{
|
||||||
|
AfterUpdateWorldTransform.Broadcast(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void USpineWidget::SetTimeScale(float timeScale) {
|
||||||
|
CheckState();
|
||||||
|
if (state) state->setTimeScale(timeScale);
|
||||||
|
}
|
||||||
|
|
||||||
|
float USpineWidget::GetTimeScale() {
|
||||||
|
CheckState();
|
||||||
|
if (state) return state->getTimeScale();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
UTrackEntry* USpineWidget::SetAnimation(int trackIndex, FString animationName, bool loop) {
|
||||||
|
CheckState();
|
||||||
|
if (state && skeleton->getData()->findAnimation(TCHAR_TO_UTF8(*animationName))) {
|
||||||
|
state->disableQueue();
|
||||||
|
TrackEntry* entry = state->setAnimation(trackIndex, TCHAR_TO_UTF8(*animationName), loop);
|
||||||
|
state->enableQueue();
|
||||||
|
UTrackEntry* uEntry = NewObject<UTrackEntry>();
|
||||||
|
uEntry->SetTrackEntry(entry);
|
||||||
|
trackEntries.Add(uEntry);
|
||||||
|
return uEntry;
|
||||||
|
}
|
||||||
|
else return NewObject<UTrackEntry>();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
UTrackEntry* USpineWidget::AddAnimation(int trackIndex, FString animationName, bool loop, float delay) {
|
||||||
|
CheckState();
|
||||||
|
if (state && skeleton->getData()->findAnimation(TCHAR_TO_UTF8(*animationName))) {
|
||||||
|
state->disableQueue();
|
||||||
|
TrackEntry* entry = state->addAnimation(trackIndex, TCHAR_TO_UTF8(*animationName), loop, delay);
|
||||||
|
state->enableQueue();
|
||||||
|
UTrackEntry* uEntry = NewObject<UTrackEntry>();
|
||||||
|
uEntry->SetTrackEntry(entry);
|
||||||
|
trackEntries.Add(uEntry);
|
||||||
|
return uEntry;
|
||||||
|
}
|
||||||
|
else return NewObject<UTrackEntry>();
|
||||||
|
}
|
||||||
|
|
||||||
|
UTrackEntry* USpineWidget::SetEmptyAnimation(int trackIndex, float mixDuration) {
|
||||||
|
CheckState();
|
||||||
|
if (state) {
|
||||||
|
TrackEntry* entry = state->setEmptyAnimation(trackIndex, mixDuration);
|
||||||
|
UTrackEntry* uEntry = NewObject<UTrackEntry>();
|
||||||
|
uEntry->SetTrackEntry(entry);
|
||||||
|
trackEntries.Add(uEntry);
|
||||||
|
return uEntry;
|
||||||
|
}
|
||||||
|
else return NewObject<UTrackEntry>();
|
||||||
|
}
|
||||||
|
|
||||||
|
UTrackEntry* USpineWidget::AddEmptyAnimation(int trackIndex, float mixDuration, float delay) {
|
||||||
|
CheckState();
|
||||||
|
if (state) {
|
||||||
|
TrackEntry* entry = state->addEmptyAnimation(trackIndex, mixDuration, delay);
|
||||||
|
UTrackEntry* uEntry = NewObject<UTrackEntry>();
|
||||||
|
uEntry->SetTrackEntry(entry);
|
||||||
|
trackEntries.Add(uEntry);
|
||||||
|
return uEntry;
|
||||||
|
}
|
||||||
|
else return NewObject<UTrackEntry>();
|
||||||
|
}
|
||||||
|
|
||||||
|
UTrackEntry* USpineWidget::GetCurrent(int trackIndex) {
|
||||||
|
CheckState();
|
||||||
|
if (state) {
|
||||||
|
TrackEntry* entry = state->getCurrent(trackIndex);
|
||||||
|
if (entry->getRendererObject()) {
|
||||||
|
return (UTrackEntry*)entry->getRendererObject();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
UTrackEntry* uEntry = NewObject<UTrackEntry>();
|
||||||
|
uEntry->SetTrackEntry(entry);
|
||||||
|
trackEntries.Add(uEntry);
|
||||||
|
return uEntry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else return NewObject<UTrackEntry>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void USpineWidget::ClearTracks() {
|
||||||
|
CheckState();
|
||||||
|
if (state) {
|
||||||
|
state->clearTracks();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void USpineWidget::ClearTrack(int trackIndex) {
|
||||||
|
CheckState();
|
||||||
|
if (state) {
|
||||||
|
state->clearTrack(trackIndex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -146,19 +146,79 @@ public:
|
|||||||
UPROPERTY(BlueprintAssignable, Category = "Components|Spine|Skeleton")
|
UPROPERTY(BlueprintAssignable, Category = "Components|Spine|Skeleton")
|
||||||
FSpineWidgetAfterUpdateWorldTransformDelegate AfterUpdateWorldTransform;
|
FSpineWidgetAfterUpdateWorldTransformDelegate AfterUpdateWorldTransform;
|
||||||
|
|
||||||
|
/* Manages if this skeleton should update automatically or is paused. */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Animation")
|
||||||
|
void SetAutoPlay(bool bInAutoPlays);
|
||||||
|
|
||||||
|
/* Directly set the time of the current animation, will clamp to animation range. */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Animation")
|
||||||
|
void SetPlaybackTime(float InPlaybackTime, bool bCallDelegates = true);
|
||||||
|
|
||||||
|
// Blueprint functions
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Animation")
|
||||||
|
void SetTimeScale(float timeScale);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Animation")
|
||||||
|
float GetTimeScale();
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Animation")
|
||||||
|
UTrackEntry* SetAnimation(int trackIndex, FString animationName, bool loop);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Animation")
|
||||||
|
UTrackEntry* AddAnimation(int trackIndex, FString animationName, bool loop, float delay);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Animation")
|
||||||
|
UTrackEntry* SetEmptyAnimation(int trackIndex, float mixDuration);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Animation")
|
||||||
|
UTrackEntry* AddEmptyAnimation(int trackIndex, float mixDuration, float delay);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Animation")
|
||||||
|
UTrackEntry* GetCurrent(int trackIndex);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Animation")
|
||||||
|
void ClearTracks();
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Animation")
|
||||||
|
void ClearTrack(int trackIndex);
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintAssignable, Category = "Components|Spine|Animation")
|
||||||
|
FSpineAnimationStartDelegate AnimationStart;
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintAssignable, Category = "Components|Spine|Animation")
|
||||||
|
FSpineAnimationInterruptDelegate AnimationInterrupt;
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintAssignable, Category = "Components|Spine|Animation")
|
||||||
|
FSpineAnimationEventDelegate AnimationEvent;
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintAssignable, Category = "Components|Spine|Animation")
|
||||||
|
FSpineAnimationCompleteDelegate AnimationComplete;
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintAssignable, Category = "Components|Spine|Animation")
|
||||||
|
FSpineAnimationEndDelegate AnimationEnd;
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintAssignable, Category = "Components|Spine|Animation")
|
||||||
|
FSpineAnimationDisposeDelegate AnimationDispose;
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Animation")
|
||||||
|
void Tick(float DeltaTime, bool CallDelegates = true);
|
||||||
|
|
||||||
virtual void FinishDestroy() override;
|
virtual void FinishDestroy() override;
|
||||||
|
|
||||||
|
// 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:
|
protected:
|
||||||
friend class SSpineWidget;
|
friend class SSpineWidget;
|
||||||
|
|
||||||
virtual TSharedRef<SWidget> RebuildWidget() override;
|
virtual TSharedRef<SWidget> RebuildWidget() override;
|
||||||
virtual void CheckState();
|
virtual void CheckState();
|
||||||
virtual void InternalTick(float DeltaTime, bool CallDelegates = true, bool Preview = false);
|
|
||||||
virtual void DisposeState();
|
virtual void DisposeState();
|
||||||
|
|
||||||
TSharedPtr<SSpineWidget> slateWidget;
|
TSharedPtr<SSpineWidget> slateWidget;
|
||||||
|
|
||||||
spine::Skeleton* skeleton;
|
spine::Skeleton* skeleton;
|
||||||
|
spine::AnimationState* state;
|
||||||
USpineAtlasAsset* lastAtlas = nullptr;
|
USpineAtlasAsset* lastAtlas = nullptr;
|
||||||
spine::Atlas* lastSpineAtlas = nullptr;
|
spine::Atlas* lastSpineAtlas = nullptr;
|
||||||
USpineSkeletonDataAsset* lastData = nullptr;
|
USpineSkeletonDataAsset* lastData = nullptr;
|
||||||
@ -182,4 +242,14 @@ protected:
|
|||||||
|
|
||||||
spine::Vector<float> worldVertices;
|
spine::Vector<float> worldVertices;
|
||||||
spine::SkeletonClipping clipper;
|
spine::SkeletonClipping clipper;
|
||||||
|
|
||||||
|
// keep track of track entries so they won't get GCed while
|
||||||
|
// in transit within a blueprint
|
||||||
|
UPROPERTY()
|
||||||
|
TSet<UTrackEntry*> trackEntries;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/* If the animation should update automatically. */
|
||||||
|
UPROPERTY()
|
||||||
|
bool bAutoPlaying;
|
||||||
};
|
};
|
||||||
Loading…
x
Reference in New Issue
Block a user