[unity] Fixed UnscaledTime setting being ignored with threaded animation enabled. Closes #2987.

This commit is contained in:
Harald Csaszar 2025-11-28 17:18:04 +01:00
parent 88b374a9a1
commit 27f5750445
4 changed files with 41 additions and 19 deletions

View File

@ -94,9 +94,22 @@ namespace Spine.Unity {
#endif #endif
#if USE_THREADED_ANIMATION_UPDATE #if USE_THREADED_ANIMATION_UPDATE
#region Threaded update system #region Threaded update system
protected static float externalDeltaTime = 0f;
protected static float unscaledDeltaTime = 0f;
[SerializeField] protected SettingsTriState threadedAnimation = SettingsTriState.UseGlobalSetting; [SerializeField] protected SettingsTriState threadedAnimation = SettingsTriState.UseGlobalSetting;
protected bool isUpdatedExternally = false; protected bool isUpdatedExternally = false;
public static float ExternalDeltaTime {
get { return externalDeltaTime; }
set { externalDeltaTime = value; }
}
public static float ExternalUnscaledDeltaTime {
get { return unscaledDeltaTime; }
set { unscaledDeltaTime = value; }
}
public virtual float UsedExternalDeltaTime { get { return ExternalDeltaTime; } }
public bool IsUpdatedExternally { public bool IsUpdatedExternally {
get { return isUpdatedExternally; } get { return isUpdatedExternally; }
set { isUpdatedExternally = value; } set { isUpdatedExternally = value; }
@ -381,6 +394,11 @@ namespace Spine.Unity {
public virtual void MainThreadAfterUpdateInternal () { } public virtual void MainThreadAfterUpdateInternal () { }
#if USE_THREADED_ANIMATION_UPDATE
public virtual void UpdateExternal (int currentFrameCount, bool calledFromOnlyMainThread = true) {
UpdateInternal(UsedExternalDeltaTime, currentFrameCount, calledFromOnlyMainThread);
}
#endif
public virtual void UpdateInternal (float deltaTime, int currentFrameCount, bool calledFromOnlyMainThread = true) { public virtual void UpdateInternal (float deltaTime, int currentFrameCount, bool calledFromOnlyMainThread = true) {
if (skipUpdate) if (skipUpdate)
return; return;
@ -395,9 +413,10 @@ namespace Spine.Unity {
ApplyAnimation(calledFromOnlyMainThread); ApplyAnimation(calledFromOnlyMainThread);
} }
#if USE_THREADED_ANIMATION_UPDATE
/// <summary>Progresses the AnimationState according to the given deltaTime, and applies it to the Skeleton. /// <summary>Progresses the AnimationState according to the given deltaTime, and applies it to the Skeleton.
/// Use Time.deltaTime to update manually. Use deltaTime 0 to update without progressing the time.</summary> /// Use Time.deltaTime to update manually. Use deltaTime 0 to update without progressing the time.</summary>
public virtual CoroutineIterator UpdateInternalSplit (CoroutineIterator coroutineIterator, float deltaTime, public virtual CoroutineIterator UpdateInternalSplit (CoroutineIterator coroutineIterator,
int currentFrameCount) { int currentFrameCount) {
if (coroutineIterator.IsDone) if (coroutineIterator.IsDone)
@ -410,7 +429,7 @@ namespace Spine.Unity {
if (skipUpdate) if (skipUpdate)
return CoroutineIterator.Done; return CoroutineIterator.Done;
frameOfLastUpdate = currentFrameCount; frameOfLastUpdate = currentFrameCount;
UpdateAnimationStatus(deltaTime); UpdateAnimationStatus(UsedExternalDeltaTime);
skeletonRenderer.ApplyTransformMovementToPhysics(); skeletonRenderer.ApplyTransformMovementToPhysics();
if (skeletonRenderer.UpdateMode == UpdateMode.OnlyAnimationStatus) if (skeletonRenderer.UpdateMode == UpdateMode.OnlyAnimationStatus)
@ -426,6 +445,7 @@ namespace Spine.Unity {
return CoroutineIterator.Done; return CoroutineIterator.Done;
} }
} }
#endif
/// <summary>Progresses the AnimationState according to the given deltaTime, and applies it to the Skeleton. /// <summary>Progresses the AnimationState according to the given deltaTime, and applies it to the Skeleton.
/// Use Time.deltaTime to update manually. Use deltaTime 0 to update without progressing the time.</summary> /// Use Time.deltaTime to update manually. Use deltaTime 0 to update without progressing the time.</summary>

View File

@ -388,6 +388,13 @@ namespace Spine.Unity {
} }
} }
#if USE_THREADED_ANIMATION_UPDATE
public override float UsedExternalDeltaTime {
get {
return unscaledTime ? ExternalUnscaledDeltaTime : ExternalDeltaTime;
}
}
#endif
protected override float DeltaTime { protected override float DeltaTime {
get { get {
return unscaledTime ? Time.unscaledDeltaTime : Time.deltaTime; return unscaledTime ? Time.unscaledDeltaTime : Time.deltaTime;

View File

@ -125,7 +125,6 @@ namespace Spine.Unity {
public int rangeStart; public int rangeStart;
public int rangeEndExclusive; public int rangeEndExclusive;
public int frameCount; public int frameCount;
public float deltaTime;
public UpdateTiming updateTiming; public UpdateTiming updateTiming;
} }
@ -306,15 +305,16 @@ namespace Spine.Unity {
numExceptionsSet = 0; numExceptionsSet = 0;
int rangePerThread = Mathf.CeilToInt((float)skeletons.Count / (float)numThreads); int rangePerThread = Mathf.CeilToInt((float)skeletons.Count / (float)numThreads);
int skeletonEnd = skeletons.Count; int skeletonEnd = skeletons.Count;
int endIndexThreaded = Math.Min(skeletonEnd, rangePerThread * numAsyncThreads); int endIndexThreaded = Math.Min(skeletonEnd, rangePerThread * numAsyncThreads);
SkeletonAnimationBase.ExternalDeltaTime = Time.deltaTime;
SkeletonAnimationBase.ExternalUnscaledDeltaTime = Time.unscaledDeltaTime;
MainThreadBeforeUpdate(skeletons, skeletonEnd); MainThreadBeforeUpdate(skeletons, skeletonEnd);
#if RUN_ALL_ON_MAIN_THREAD #if RUN_ALL_ON_MAIN_THREAD
for (int r = 0; r < skeletons.Count; ++r) { for (int r = 0; r < skeletons.Count; ++r) {
skeletons[r].UpdateInternal(Time.deltaTime, Time.frameCount, calledFromOnlyMainThread: true); skeletons[r].UpdateExternal(Time.frameCount, calledFromOnlyMainThread: true);
} }
#else #else
if (!mainThreadUpdateCallbacks) if (!mainThreadUpdateCallbacks)
@ -338,7 +338,6 @@ namespace Spine.Unity {
var range = new SkeletonUpdateRange() { var range = new SkeletonUpdateRange() {
rangeStart = start, rangeStart = start,
rangeEndExclusive = end, rangeEndExclusive = end,
deltaTime = Time.deltaTime,
frameCount = Time.frameCount, frameCount = Time.frameCount,
updateTiming = timing updateTiming = timing
}; };
@ -384,7 +383,6 @@ namespace Spine.Unity {
var range = new SkeletonUpdateRange() { var range = new SkeletonUpdateRange() {
rangeStart = start, rangeStart = start,
rangeEndExclusive = end, rangeEndExclusive = end,
deltaTime = Time.deltaTime,
frameCount = Time.frameCount, frameCount = Time.frameCount,
updateTiming = timing updateTiming = timing
}; };
@ -406,7 +404,6 @@ namespace Spine.Unity {
var range = new SkeletonUpdateRange() { var range = new SkeletonUpdateRange() {
rangeStart = start, rangeStart = start,
rangeEndExclusive = end, rangeEndExclusive = end,
deltaTime = Time.deltaTime,
frameCount = Time.frameCount, frameCount = Time.frameCount,
updateTiming = timing updateTiming = timing
}; };
@ -426,7 +423,7 @@ namespace Spine.Unity {
Thread.MemoryBarrier(); Thread.MemoryBarrier();
// process main thread callback part // process main thread callback part
anyWorkLeft = UpdateSkeletonsMainThreadSplit(skeletons, endIndexThreaded, Time.deltaTime, Time.frameCount); anyWorkLeft = UpdateSkeletonsMainThreadSplit(skeletons, endIndexThreaded, Time.frameCount);
isFirstIteration = false; isFirstIteration = false;
} while (anyWorkLeft && ++timeoutCounter < TimeoutIterationCount); } while (anyWorkLeft && ++timeoutCounter < TimeoutIterationCount);
@ -517,7 +514,6 @@ namespace Spine.Unity {
var range = new SkeletonUpdateRange() { var range = new SkeletonUpdateRange() {
rangeStart = start, rangeStart = start,
rangeEndExclusive = end, rangeEndExclusive = end,
deltaTime = Time.deltaTime,
frameCount = Time.frameCount, frameCount = Time.frameCount,
updateTiming = UpdateTiming.InLateUpdate updateTiming = UpdateTiming.InLateUpdate
}; };
@ -671,7 +667,6 @@ namespace Spine.Unity {
#if SPINE_ENABLE_THREAD_PROFILING #if SPINE_ENABLE_THREAD_PROFILING
instance.profilerSamplerUpdate[threadIndex].Begin(); instance.profilerSamplerUpdate[threadIndex].Begin();
#endif #endif
float deltaTime = range.deltaTime;
int frameCount = range.frameCount; int frameCount = range.frameCount;
int start = range.rangeStart; int start = range.rangeStart;
int end = range.rangeEndExclusive; int end = range.rangeEndExclusive;
@ -681,7 +676,7 @@ namespace Spine.Unity {
for (int r = start; r < end; ++r) { for (int r = start; r < end; ++r) {
try { try {
skeletonAnimations[r].UpdateInternal(deltaTime, frameCount, calledFromOnlyMainThread: false); skeletonAnimations[r].UpdateExternal(frameCount, calledFromOnlyMainThread: false);
} catch (Exception exc) { } catch (Exception exc) {
instance.DeferredLogException(exc, skeletonAnimations[r], threadIndex); instance.DeferredLogException(exc, skeletonAnimations[r], threadIndex);
} }
@ -713,7 +708,6 @@ namespace Spine.Unity {
// avoid allocation, unfortunately this is really necessary // avoid allocation, unfortunately this is really necessary
static Action<SkeletonUpdateRange, int> cachedUpdateSkeletonsAsyncSplitImpl = UpdateSkeletonsAsyncSplitImpl; static Action<SkeletonUpdateRange, int> cachedUpdateSkeletonsAsyncSplitImpl = UpdateSkeletonsAsyncSplitImpl;
static void UpdateSkeletonsAsyncSplitImpl (SkeletonUpdateRange range, int threadIndex) { static void UpdateSkeletonsAsyncSplitImpl (SkeletonUpdateRange range, int threadIndex) {
float deltaTime = range.deltaTime;
int frameCount = range.frameCount; int frameCount = range.frameCount;
int start = range.rangeStart; int start = range.rangeStart;
int end = range.rangeEndExclusive; int end = range.rangeEndExclusive;
@ -731,7 +725,7 @@ namespace Spine.Unity {
try { try {
SkeletonAnimationBase targetSkeletonAnimation = skeletonAnimations[r]; SkeletonAnimationBase targetSkeletonAnimation = skeletonAnimations[r];
if (!splitUpdateMethod[r].IsDone) { if (!splitUpdateMethod[r].IsDone) {
splitUpdateMethod[r] = targetSkeletonAnimation.UpdateInternalSplit(splitUpdateMethod[r], deltaTime, frameCount); splitUpdateMethod[r] = targetSkeletonAnimation.UpdateInternalSplit(splitUpdateMethod[r], frameCount);
} }
} catch (Exception exc) { } catch (Exception exc) {
instance.DeferredLogException(exc, skeletonAnimations[r], threadIndex); instance.DeferredLogException(exc, skeletonAnimations[r], threadIndex);
@ -745,7 +739,7 @@ namespace Spine.Unity {
} }
bool UpdateSkeletonsMainThreadSplit (List<SkeletonAnimationBase> skeletons, int endIndexThreaded, bool UpdateSkeletonsMainThreadSplit (List<SkeletonAnimationBase> skeletons, int endIndexThreaded,
float deltaTime, int frameCount) { int frameCount) {
bool anyWorkLeft = false; bool anyWorkLeft = false;
for (int r = 0; r < endIndexThreaded; ++r) { for (int r = 0; r < endIndexThreaded; ++r) {
@ -756,7 +750,7 @@ namespace Spine.Unity {
} else { } else {
if (!splitUpdateMethod[r].IsDone) { if (!splitUpdateMethod[r].IsDone) {
anyWorkLeft = true; anyWorkLeft = true;
splitUpdateMethod[r] = targetSkeletonAnimation.UpdateInternalSplit(splitUpdateMethod[r], deltaTime, frameCount); splitUpdateMethod[r] = targetSkeletonAnimation.UpdateInternalSplit(splitUpdateMethod[r], frameCount);
} }
} }
} catch (Exception exc) { } catch (Exception exc) {
@ -770,9 +764,10 @@ namespace Spine.Unity {
void UpdateSkeletonsSynchronous (List<SkeletonAnimationBase> skeletons, SkeletonUpdateRange range) { void UpdateSkeletonsSynchronous (List<SkeletonAnimationBase> skeletons, SkeletonUpdateRange range) {
int start = range.rangeStart; int start = range.rangeStart;
int end = range.rangeEndExclusive; int end = range.rangeEndExclusive;
int frameCount = range.frameCount;
for (int r = start; r < end; ++r) { for (int r = start; r < end; ++r) {
skeletons[r].UpdateInternal(range.deltaTime, range.frameCount, calledFromOnlyMainThread: true); skeletons[r].UpdateExternal(frameCount, calledFromOnlyMainThread: true);
} }
} }

View File

@ -2,7 +2,7 @@
"name": "com.esotericsoftware.spine.spine-unity", "name": "com.esotericsoftware.spine.spine-unity",
"displayName": "spine-unity Runtime", "displayName": "spine-unity Runtime",
"description": "This plugin provides the spine-unity runtime core and examples. Spine Examples can be installed via the Samples tab.", "description": "This plugin provides the spine-unity runtime core and examples. Spine Examples can be installed via the Samples tab.",
"version": "4.3.31", "version": "4.3.32",
"unity": "2018.3", "unity": "2018.3",
"author": { "author": {
"name": "Esoteric Software", "name": "Esoteric Software",