Engine: Unity
Spine version: 3.7.87
Spine runtime: version: 3.7.xx
Hi, I'm having a very similar bug to: IndexOutOfRangeException in Spine.Animation.BinarySearch
Except it's color track, and happens usually when I come out of the game pause mode. When I pause the game, I set all spine animations and their track's time scale value to 0f, so they stop. On resume, I set them back to what they're supposed to be doing.
It happens here:
/// <param name="target">After the first and before the last entry.</param>
internal static int BinarySearch (float[] values, float target, int step) {
int low = 0;
int high = values.Length / step - 2;
if (high == 0) return step;
int current = (int)((uint)high >> 1);
while (true) {
if (values[(current + 1) * step] <= target)
low = current + 1;
else
high = current;
if (low == high) return (low + 1) * step;
current = (int)((uint)(low + high) >> 1);
}
}
I've managed to find out that it happens when values.Length = 5, and step = 5 (i.e. the same). This makes "high" resolve to -1, which makes current -2147483648 something.
What could be causing step to = 5? Would it be safe to just include:
if (high < 0) return values.Length - 1;
? If I try to return "step", it leads to another OOB error, assuming this is because step is equal to the max length of the animation
I'm on my way home to investigate the animation that's causing the issue, just wondered if there's a chance I could get a bit of insight into why "step" would equal "values.Length", and if it would have something to do with timeScale resuming from 0
edit It seems like step would always = 5 in the ColorTimeline, as it equals "ENTRIES" in Animation.cs
public class ColorTimeline : CurveTimeline, ISlotTimeline {
public const int ENTRIES = 5;
But then why would values.Length = 5 too if this causes "high" to equal -1?
I managed to temporarily solve this by editing the problematic animation (more details below)- However, this is not an ideal solution and this still seems like unexpected behaviour and I'm struggling to figure out why this error happens - and it can lead to more problems for me as not all my animations have the last frame keyed as im about to explain:
This is my problematic animation: https://imgur.com/a/KizyHQr
It's very simple, but the last frame doesn't have a color keyed to it. When setting timeScale to 0, waiting for a bit, then resuming timeScale back to 1, this is where the error occurs. I have no idea why.
Keying the last frame prevents this issue. If you would like a copy of the spine file I've emailed it to support form
Please could I have a bit more informaiton on why this may happen and how to prevent it?
Further investigation into Animation.cs
override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend, MixDirection direction) {
Slot slot = skeleton.slots.Items[slotIndex];
time is showing as "NaN", not a number, after resuming from pause. I'm checking why this is now
/// <summary>
/// Uses <see cref="TrackEntry.TrackTime"/> to compute the animation time between <see cref="TrackEntry.AnimationStart"/>. and
/// <see cref="TrackEntry.AnimationEnd"/>. When the track time is 0, the animation time is equal to the animation start time.
/// </summary>
public float AnimationTime {
get {
if (loop) {
float duration = animationEnd - animationStart;
if (duration == 0) return animationStart;
return (trackTime % duration) + animationStart;
}
return Math.Min(trackTime + animationStart, animationEnd);
}
}
Sources from AnimationTime being "NaN". The animation is looped, so its something in this? Any reason why this may be setting to NaN with just one keyframe on the Color track?
Above stems from trackTime being "infinity". animationStart = 0 and animationEnd = 0.3333
Above stems from trackTime being set to infinity possibly somewhere in here, although I'm having trouble understanding the code
TrackEntry next = current.next;
if (next != null) {
// When the next entry's delay is passed, change to the next entry, preserving leftover time.
float nextTime = current.trackLast - next.delay;
if (nextTime >= 0) {
next.delay = 0;
next.trackTime = (nextTime / current.timeScale + delta) * next.timeScale;
current.trackTime += currentDelta;
SetCurrent(i, next, true);
while (next.mixingFrom != null) {
next.mixTime += delta;
next = next.mixingFrom;
}
continue;
}