[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
This commit is contained in:
Serhii Yolkin 2017-09-01 11:56:18 +02:00 committed by John
parent ad1279a71f
commit c9722319de
2 changed files with 71 additions and 23 deletions

View File

@ -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;

View File

@ -108,9 +108,13 @@ namespace Spine.Unity {
public enum MixMode { AlwaysMix, MixNext, SpineStyle }
readonly Dictionary<int, Spine.Animation> animationTable = new Dictionary<int, Spine.Animation>();
readonly Dictionary<AnimationClip, int> clipNameHashCodeTable = new Dictionary<AnimationClip, int>();
readonly Dictionary<int, Spine.Animation> animationTable = new Dictionary<int, Spine.Animation>(IntEqualityComparer.Instance);
readonly Dictionary<AnimationClip, int> clipNameHashCodeTable = new Dictionary<AnimationClip, int>(AnimationClipEqualityComparer.Instance);
readonly List<Animation> previousAnimations = new List<Animation>();
#if UNITY_2017_1_OR_NEWER
readonly List<AnimatorClipInfo> clipInfoCache = new List<AnimatorClipInfo>();
readonly List<AnimatorClipInfo> nextClipInfoCache = new List<AnimatorClipInfo>();
#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<AnimatorClipInfo> 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<AnimatorClipInfo> 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<AnimatorClipInfo> clipInfo,
out IList<AnimatorClipInfo> 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<AnimationClip> {
internal static readonly IEqualityComparer<AnimationClip> 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<int> {
internal static readonly IEqualityComparer<int> Instance = new IntEqualityComparer();
public bool Equals (int x, int y) { return x == y; }
public int GetHashCode(int o) { return o; }
}
}
}