From c9722319de5bcc5671ebd15c36bf16776dea14c4 Mon Sep 17 00:00:00 2001 From: Serhii Yolkin Date: Fri, 1 Sep 2017 11:56:18 +0200 Subject: [PATCH] [unity] SkeletonAnimator and Animation optimizations (#977) * + SkeletonAnimator: optimizations, remove per-frame GC allocations on Unity 2017.1 + Minor ScaleTimeline optimization * [unity] Check + formatting SkeletonAnimator 2017.1 --- spine-csharp/src/Animation.cs | 8 +- .../Assets/spine-unity/SkeletonAnimator.cs | 86 +++++++++++++++---- 2 files changed, 71 insertions(+), 23 deletions(-) diff --git a/spine-csharp/src/Animation.cs b/spine-csharp/src/Animation.cs index e344f7319..2c8228949 100644 --- a/spine-csharp/src/Animation.cs +++ b/spine-csharp/src/Animation.cs @@ -436,11 +436,11 @@ namespace Spine { } // Mixing out uses sign of setup or current pose, else use sign of key. if (direction == MixDirection.Out) { - x = Math.Abs(x) * Math.Sign(bx); - y = Math.Abs(y) * Math.Sign(by); + x = (x >= 0 ? x : -x) * (bx >= 0 ? 1 : -1); + y = (y >= 0 ? y : -y) * (by >= 0 ? 1 : -1); } else { - bx = Math.Abs(bx) * Math.Sign(x); - by = Math.Abs(by) * Math.Sign(y); + bx = (bx >= 0 ? bx : -bx) * (x >= 0 ? 1 : -1); + by = (by >= 0 ? by : -by) * (y >= 0 ? 1 : -1); } bone.scaleX = bx + (x - bx) * alpha; bone.scaleY = by + (y - by) * alpha; diff --git a/spine-unity/Assets/spine-unity/SkeletonAnimator.cs b/spine-unity/Assets/spine-unity/SkeletonAnimator.cs index 0337eedc4..558f2177e 100644 --- a/spine-unity/Assets/spine-unity/SkeletonAnimator.cs +++ b/spine-unity/Assets/spine-unity/SkeletonAnimator.cs @@ -36,7 +36,7 @@ using System.Collections.Generic; namespace Spine.Unity { [RequireComponent(typeof(Animator))] public class SkeletonAnimator : SkeletonRenderer, ISkeletonAnimation { - + [SerializeField] protected MecanimTranslator translator; public MecanimTranslator Translator { get { return translator; } } @@ -108,9 +108,13 @@ namespace Spine.Unity { public enum MixMode { AlwaysMix, MixNext, SpineStyle } - readonly Dictionary animationTable = new Dictionary(); - readonly Dictionary clipNameHashCodeTable = new Dictionary(); + readonly Dictionary animationTable = new Dictionary(IntEqualityComparer.Instance); + readonly Dictionary clipNameHashCodeTable = new Dictionary(AnimationClipEqualityComparer.Instance); readonly List previousAnimations = new List(); + #if UNITY_2017_1_OR_NEWER + readonly List clipInfoCache = new List(); + readonly List nextClipInfoCache = new List(); + #endif Animator animator; public Animator Animator { get { return this.animator; } } @@ -144,16 +148,19 @@ namespace Spine.Unity { AnimatorStateInfo nextStateInfo = animator.GetNextAnimatorStateInfo(layer); bool hasNext = nextStateInfo.fullPathHash != 0; - AnimatorClipInfo[] clipInfo = animator.GetCurrentAnimatorClipInfo(layer); - AnimatorClipInfo[] nextClipInfo = animator.GetNextAnimatorClipInfo(layer); - for (int c = 0; c < clipInfo.Length; c++) { + int clipInfoCount, nextClipInfoCount; + IList clipInfo, nextClipInfo; + GetAnimatorClipInfos(layer, out clipInfoCount, out nextClipInfoCount, out clipInfo, out nextClipInfo); + + for (int c = 0; c < clipInfoCount; c++) { var info = clipInfo[c]; float weight = info.weight * layerWeight; if (weight == 0) continue; previousAnimations.Add(animationTable[NameHashCode(info.clip)]); } + if (hasNext) { - for (int c = 0; c < nextClipInfo.Length; c++) { + for (int c = 0; c < nextClipInfoCount; c++) { var info = nextClipInfo[c]; float weight = info.weight * layerWeight; if (weight == 0) continue; previousAnimations.Add(animationTable[NameHashCode(info.clip)]); @@ -169,22 +176,20 @@ namespace Spine.Unity { AnimatorStateInfo nextStateInfo = animator.GetNextAnimatorStateInfo(layer); bool hasNext = nextStateInfo.fullPathHash != 0; - AnimatorClipInfo[] clipInfo = animator.GetCurrentAnimatorClipInfo(layer); - AnimatorClipInfo[] nextClipInfo = animator.GetNextAnimatorClipInfo(layer); - //UNITY 4 - //bool hasNext = nextStateInfo.nameHash != 0; - //var clipInfo = animator.GetCurrentAnimationClipState(i); - //var nextClipInfo = animator.GetNextAnimationClipState(i); + + int clipInfoCount, nextClipInfoCount; + IList clipInfo, nextClipInfo; + GetAnimatorClipInfos(layer, out clipInfoCount, out nextClipInfoCount, out clipInfo, out nextClipInfo); MixMode mode = layerMixModes[layer]; if (mode == MixMode.AlwaysMix) { // Always use Mix instead of Applying the first non-zero weighted clip. - for (int c = 0; c < clipInfo.Length; c++) { + for (int c = 0; c < clipInfoCount; c++) { var info = clipInfo[c]; float weight = info.weight * layerWeight; if (weight == 0) continue; animationTable[NameHashCode(info.clip)].Apply(skeleton, 0, AnimationTime(stateInfo.normalizedTime, info.clip.length, stateInfo.loop, stateInfo.speed < 0), stateInfo.loop, null, weight, MixPose.Current, MixDirection.In); } if (hasNext) { - for (int c = 0; c < nextClipInfo.Length; c++) { + for (int c = 0; c < nextClipInfoCount; c++) { var info = nextClipInfo[c]; float weight = info.weight * layerWeight; if (weight == 0) continue; animationTable[NameHashCode(info.clip)].Apply(skeleton, 0, AnimationTime(nextStateInfo.normalizedTime , info.clip.length,nextStateInfo.speed < 0), nextStateInfo.loop, null, weight, MixPose.Current, MixDirection.In); } @@ -192,13 +197,13 @@ namespace Spine.Unity { } else { // case MixNext || SpineStyle // Apply first non-zero weighted clip int c = 0; - for (; c < clipInfo.Length; c++) { + for (; c < clipInfoCount; c++) { var info = clipInfo[c]; float weight = info.weight * layerWeight; if (weight == 0) continue; animationTable[NameHashCode(info.clip)].Apply(skeleton, 0, AnimationTime(stateInfo.normalizedTime, info.clip.length, stateInfo.loop, stateInfo.speed < 0), stateInfo.loop, null, 1f, MixPose.Current, MixDirection.In); break; } // Mix the rest - for (; c < clipInfo.Length; c++) { + for (; c < clipInfoCount; c++) { var info = clipInfo[c]; float weight = info.weight * layerWeight; if (weight == 0) continue; animationTable[NameHashCode(info.clip)].Apply(skeleton, 0, AnimationTime(stateInfo.normalizedTime, info.clip.length, stateInfo.loop, stateInfo.speed < 0), stateInfo.loop, null, weight, MixPose.Current, MixDirection.In); } @@ -207,14 +212,14 @@ namespace Spine.Unity { if (hasNext) { // Apply next clip directly instead of mixing (ie: no crossfade, ignores mecanim transition weights) if (mode == MixMode.SpineStyle) { - for (; c < nextClipInfo.Length; c++) { + for (; c < nextClipInfoCount; c++) { var info = nextClipInfo[c]; float weight = info.weight * layerWeight; if (weight == 0) continue; animationTable[NameHashCode(info.clip)].Apply(skeleton, 0, AnimationTime(nextStateInfo.normalizedTime , info.clip.length,nextStateInfo.speed < 0), nextStateInfo.loop, null, 1f, MixPose.Current, MixDirection.In); break; } } // Mix the rest - for (; c < nextClipInfo.Length; c++) { + for (; c < nextClipInfoCount; c++) { var info = nextClipInfo[c]; float weight = info.weight * layerWeight; if (weight == 0) continue; animationTable[NameHashCode(info.clip)].Apply(skeleton, 0, AnimationTime(nextStateInfo.normalizedTime , info.clip.length,nextStateInfo.speed < 0), nextStateInfo.loop, null, weight, MixPose.Current, MixDirection.In); } @@ -239,6 +244,31 @@ namespace Spine.Unity { return normalizedTime * clipLength; } + void GetAnimatorClipInfos ( + int layer, + out int clipInfoCount, + out int nextClipInfoCount, + out IList clipInfo, + out IList nextClipInfo) { + #if UNITY_2017_1_OR_NEWER + clipInfoCount = animator.GetCurrentAnimatorClipInfoCount(layer); + nextClipInfoCount = animator.GetNextAnimatorClipInfoCount(layer); + if (clipInfoCache.Capacity < clipInfoCount) clipInfoCache.Capacity = clipInfoCount; + if (nextClipInfoCache.Capacity < nextClipInfoCount) nextClipInfoCache.Capacity = nextClipInfoCount; + animator.GetCurrentAnimatorClipInfo(layer, clipInfoCache); + animator.GetNextAnimatorClipInfo(layer, nextClipInfoCache); + + clipInfo = clipInfoCache; + nextClipInfo = nextClipInfoCache; + #else + clipInfo = animator.GetCurrentAnimatorClipInfo(layer); + nextClipInfo = animator.GetNextAnimatorClipInfo(layer); + + clipInfoCount = clipInfo.Count; + nextClipInfoCount = nextClipInfo.Count; + #endif + } + int NameHashCode (AnimationClip clip) { int clipNameHashCode; if (!clipNameHashCodeTable.TryGetValue(clip, out clipNameHashCode)) { @@ -247,6 +277,24 @@ namespace Spine.Unity { } return clipNameHashCode; } + + class AnimationClipEqualityComparer : IEqualityComparer { + internal static readonly IEqualityComparer Instance = new AnimationClipEqualityComparer(); + + public bool Equals (AnimationClip x, AnimationClip y) { + return x.GetInstanceID() == y.GetInstanceID(); + } + + public int GetHashCode (AnimationClip o) { + return o.GetInstanceID(); + } + } + + class IntEqualityComparer : IEqualityComparer { + internal static readonly IEqualityComparer Instance = new IntEqualityComparer(); + public bool Equals (int x, int y) { return x == y; } + public int GetHashCode(int o) { return o; } + } } }