From 82edea95ee9e21e35be55d4a0123434815dfddaf Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Mon, 17 Aug 2020 16:01:14 +0200 Subject: [PATCH 1/2] [unity] Updated officially supported Unity versions in changelog and readme files. --- CHANGELOG.md | 2 +- spine-unity/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d003e19a1..72aca3f2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -140,7 +140,7 @@ ### Unity * **Breaking changes** - * **Officially supported Unity versions are 2017.1-2019.2**. + * **Officially supported Unity versions are 2017.1-2019.4**. * **Spine `.asmdef` files are again active by default**. They have previously been deactivated to `.txt` extension which is now no longer necessary. * **Removed PoseSkeleton() and PoseWithAnimation()** extension methods to prevent issues where animations are not mixed out. Problem was that these methods did not set AnimationState, leaving incorrect state at e.g. attachments enabled at slots when starting subsequent animations. As a replacement you can use `AnimationState.ClearTrack(0);` followed by `var entry = AnimationState.SetAnimation(0, animation, loop); entry.TrackTime = time` to achieve similar behaviour. * **The `Shadow alpha cutoff` shader parameter is now respecting slot-color alpha** values at all Spine shaders. A fragment's texture color alpha is multiplied with slot-color alpha before the result is tested against the `Shadow alpha cutoff` threshold. diff --git a/spine-unity/README.md b/spine-unity/README.md index 1ddea55df..78cb53059 100644 --- a/spine-unity/README.md +++ b/spine-unity/README.md @@ -22,7 +22,7 @@ Unity's physics components do not support dynamically assigned vertices so they ## Unity version -spine-unity is compatible with Unity 2017.1-2019.3. +spine-unity is compatible with Unity 2017.1-2019.4. ## Usage From 402ef55141f29948d0ec28a02ac17d32ef50b714 Mon Sep 17 00:00:00 2001 From: badlogic Date: Wed, 26 Aug 2020 15:29:37 +0200 Subject: [PATCH 2/2] [ue4] Closes #1707 and related issues UspineSkeletonDataAsset would only retain mapping from one spine::Atlas to a single spine::SkeletonData internally. If two SkeletonAnimationComponents reference the same skeleton data asset, but different (or invalid atlases), the old skeleton data gets destroyed. If the atlas and skeleton data don't match, then a previously created animation component may now hold on to a reference to destroyed skeleton data, triggering a crash. --- .../Private/SpineSkeletonDataAsset.cpp | 68 ++++++++++--------- .../Public/SpineSkeletonDataAsset.h | 14 +++- spine-ue4/README.md | 2 +- spine-ue4/SpineUE4.uproject | 2 +- 4 files changed, 48 insertions(+), 38 deletions(-) diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonDataAsset.cpp b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonDataAsset.cpp index 022333bce..c5418348e 100644 --- a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonDataAsset.cpp +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonDataAsset.cpp @@ -80,15 +80,17 @@ void USpineSkeletonDataAsset::Serialize (FArchive& Ar) { #endif +void USpineSkeletonDataAsset::ClearNativeData() { + for (auto &pair : atlasToNativeData) { + if (pair.Value.skeletonData) delete pair.Value.skeletonData; + if (pair.Value.animationStateData) delete pair.Value.animationStateData; + } + atlasToNativeData.Empty(); +} + void USpineSkeletonDataAsset::BeginDestroy () { - if (this->skeletonData) { - delete this->skeletonData; - this->skeletonData = nullptr; - } - if (this->animationStateData) { - delete this->animationStateData; - this->animationStateData = nullptr; - } + ClearNativeData(); + Super::BeginDestroy(); } @@ -128,10 +130,7 @@ void USpineSkeletonDataAsset::SetRawData(TArray &Data) { this->rawData.Empty(); this->rawData.Append(Data); - if (skeletonData) { - delete skeletonData; - skeletonData = nullptr; - } + ClearNativeData(); LoadInfo(); } @@ -273,15 +272,18 @@ void USpineSkeletonDataAsset::LoadInfo() { } SkeletonData* USpineSkeletonDataAsset::GetSkeletonData (Atlas* Atlas) { - if (!skeletonData || lastAtlas != Atlas) { - if (skeletonData) { - delete skeletonData; - skeletonData = nullptr; - } + SkeletonData* skeletonData = nullptr; + AnimationStateData* animationStateData = nullptr; + if (atlasToNativeData.Contains(Atlas)) { + skeletonData = atlasToNativeData[Atlas].skeletonData; + animationStateData = atlasToNativeData[Atlas].animationStateData; + } + + if (!skeletonData) { int dataLen = rawData.Num(); if (skeletonDataFileName.GetPlainNameString().Contains(TEXT(".json"))) { SkeletonJson* json = new (__FILE__, __LINE__) SkeletonJson(Atlas); - if (checkJson((const char*)rawData.GetData())) this->skeletonData = json->readSkeletonData((const char*)rawData.GetData()); + if (checkJson((const char*)rawData.GetData())) skeletonData = json->readSkeletonData((const char*)rawData.GetData()); if (!skeletonData) { #if WITH_EDITORONLY_DATA FMessageDialog::Debugf(FText::FromString(FString("Couldn't load skeleton data and/or atlas. Please ensure the version of your exported data matches your runtime version.\n\n") + skeletonDataFileName.GetPlainNameString() + FString("\n\n") + UTF8_TO_TCHAR(json->getError().buffer()))); @@ -291,7 +293,7 @@ SkeletonData* USpineSkeletonDataAsset::GetSkeletonData (Atlas* Atlas) { delete json; } else { SkeletonBinary* binary = new (__FILE__, __LINE__) SkeletonBinary(Atlas); - if (checkBinary((const char*)rawData.GetData(), (int)rawData.Num())) this->skeletonData = binary->readSkeletonData((const unsigned char*)rawData.GetData(), (int)rawData.Num()); + if (checkBinary((const char*)rawData.GetData(), (int)rawData.Num())) skeletonData = binary->readSkeletonData((const unsigned char*)rawData.GetData(), (int)rawData.Num()); if (!skeletonData) { #if WITH_EDITORONLY_DATA FMessageDialog::Debugf(FText::FromString(FString("Couldn't load skeleton data and/or atlas. Please ensure the version of your exported data matches your runtime version.\n\n") + skeletonDataFileName.GetPlainNameString() + FString("\n\n") + UTF8_TO_TCHAR(binary->getError().buffer()))); @@ -300,21 +302,18 @@ SkeletonData* USpineSkeletonDataAsset::GetSkeletonData (Atlas* Atlas) { } delete binary; } - if (animationStateData) { - delete animationStateData; - animationStateData = nullptr; - GetAnimationStateData(Atlas); + + if (skeletonData) { + animationStateData = new (__FILE__, __LINE__) AnimationStateData(skeletonData); + SetMixes(animationStateData); + atlasToNativeData.Add(Atlas, { skeletonData, animationStateData }); } - lastAtlas = Atlas; } - return this->skeletonData; + + return skeletonData; } -AnimationStateData* USpineSkeletonDataAsset::GetAnimationStateData(Atlas* atlas) { - if (!animationStateData) { - SkeletonData* data = GetSkeletonData(atlas); - animationStateData = new (__FILE__, __LINE__) AnimationStateData(data); - } +void USpineSkeletonDataAsset::SetMixes(AnimationStateData* animationStateData) { for (auto& data : MixData) { if (!data.From.IsEmpty() && !data.To.IsEmpty()) { const char* fromChar = TCHAR_TO_UTF8(*data.From); @@ -323,7 +322,10 @@ AnimationStateData* USpineSkeletonDataAsset::GetAnimationStateData(Atlas* atlas) } } animationStateData->setDefaultMix(DefaultMix); - return this->animationStateData; +} + +AnimationStateData* USpineSkeletonDataAsset::GetAnimationStateData(Atlas* atlas) { + return atlasToNativeData.Contains(atlas) ? atlasToNativeData[atlas].animationStateData : nullptr; } void USpineSkeletonDataAsset::SetMix(const FString& from, const FString& to, float mix) { @@ -332,8 +334,8 @@ void USpineSkeletonDataAsset::SetMix(const FString& from, const FString& to, flo data.To = to; data.Mix = mix; this->MixData.Add(data); - if (lastAtlas) { - GetAnimationStateData(lastAtlas); + for (auto &pair : atlasToNativeData) { + SetMixes(pair.Value.animationStateData); } } diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonDataAsset.h b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonDataAsset.h index 03d51f183..fc840666c 100644 --- a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonDataAsset.h +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonDataAsset.h @@ -92,9 +92,17 @@ protected: UPROPERTY() FName skeletonDataFileName; - spine::SkeletonData* skeletonData; - spine::AnimationStateData* animationStateData; - spine::Atlas* lastAtlas; + // These are created at runtime + struct NativeSkeletonData { + spine::SkeletonData* skeletonData; + spine::AnimationStateData* animationStateData; + }; + + TMap atlasToNativeData; + + void ClearNativeData(); + + void SetMixes(spine::AnimationStateData* animationStateData); #if WITH_EDITORONLY_DATA public: diff --git a/spine-ue4/README.md b/spine-ue4/README.md index 4ecb1a302..aef6300df 100644 --- a/spine-ue4/README.md +++ b/spine-ue4/README.md @@ -33,7 +33,7 @@ See the [Spine Runtimes documentation](http://esotericsoftware.com/spine-documen ## Example ### [Please see the spine-ue4 guide for full documentation](http://esotericsoftware.com/spine-ue4) -The Spine UE4 example works on all platforms supported by Unreal Engine. The samples require Unreal Engine 4.23+. +The Spine UE4 example works on all platforms supported by Unreal Engine. The samples require Unreal Engine 4.25+. 1. Copy the `spine-cpp` folder from this repositories root directory to your `Plugins/SpinePlugin/Sources/SpinePlugin/Public/` directory. 2. Open the SpineUE4.uproject file with Unreal Editor diff --git a/spine-ue4/SpineUE4.uproject b/spine-ue4/SpineUE4.uproject index 51b0b4850..bdda1211f 100644 --- a/spine-ue4/SpineUE4.uproject +++ b/spine-ue4/SpineUE4.uproject @@ -1,6 +1,6 @@ { "FileVersion": 3, - "EngineAssociation": "4.24", + "EngineAssociation": "4.25", "Category": "", "Description": "", "Modules": [