SkeletonMecanim.cs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596
  1. /******************************************************************************
  2. * Spine Runtimes License Agreement
  3. * Last updated January 1, 2020. Replaces all prior versions.
  4. *
  5. * Copyright (c) 2013-2020, Esoteric Software LLC
  6. *
  7. * Integration of the Spine Runtimes into software or otherwise creating
  8. * derivative works of the Spine Runtimes is permitted under the terms and
  9. * conditions of Section 2 of the Spine Editor License Agreement:
  10. * http://esotericsoftware.com/spine-editor-license
  11. *
  12. * Otherwise, it is permitted to integrate the Spine Runtimes into software
  13. * or otherwise create derivative works of the Spine Runtimes (collectively,
  14. * "Products"), provided that each user of the Products must obtain their own
  15. * Spine Editor license and redistribution of the Products in any form must
  16. * include this license and copyright notice.
  17. *
  18. * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
  19. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  20. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  21. * DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
  22. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  23. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
  24. * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
  25. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  27. * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28. *****************************************************************************/
  29. using UnityEngine;
  30. using System.Collections.Generic;
  31. namespace Spine.Unity {
  32. [RequireComponent(typeof(Animator))]
  33. public class SkeletonMecanim : SkeletonRenderer, ISkeletonAnimation {
  34. [SerializeField] protected MecanimTranslator translator;
  35. public MecanimTranslator Translator { get { return translator; } }
  36. private bool wasUpdatedAfterInit = true;
  37. #region Bone Callbacks (ISkeletonAnimation)
  38. protected event UpdateBonesDelegate _UpdateLocal;
  39. protected event UpdateBonesDelegate _UpdateWorld;
  40. protected event UpdateBonesDelegate _UpdateComplete;
  41. /// <summary>
  42. /// Occurs after the animations are applied and before world space values are resolved.
  43. /// Use this callback when you want to set bone local values.</summary>
  44. public event UpdateBonesDelegate UpdateLocal { add { _UpdateLocal += value; } remove { _UpdateLocal -= value; } }
  45. /// <summary>
  46. /// Occurs after the Skeleton's bone world space values are resolved (including all constraints).
  47. /// Using this callback will cause the world space values to be solved an extra time.
  48. /// Use this callback if want to use bone world space values, and also set bone local values.</summary>
  49. public event UpdateBonesDelegate UpdateWorld { add { _UpdateWorld += value; } remove { _UpdateWorld -= value; } }
  50. /// <summary>
  51. /// Occurs after the Skeleton's bone world space values are resolved (including all constraints).
  52. /// Use this callback if you want to use bone world space values, but don't intend to modify bone local values.
  53. /// This callback can also be used when setting world position and the bone matrix.</summary>
  54. public event UpdateBonesDelegate UpdateComplete { add { _UpdateComplete += value; } remove { _UpdateComplete -= value; } }
  55. #endregion
  56. public override void Initialize (bool overwrite) {
  57. if (valid && !overwrite)
  58. return;
  59. base.Initialize(overwrite);
  60. if (!valid)
  61. return;
  62. if (translator == null) translator = new MecanimTranslator();
  63. translator.Initialize(GetComponent<Animator>(), this.skeletonDataAsset);
  64. wasUpdatedAfterInit = false;
  65. }
  66. public void Update () {
  67. if (!valid) return;
  68. #if UNITY_EDITOR
  69. var translatorAnimator = translator.Animator;
  70. if (translatorAnimator != null && !translatorAnimator.isInitialized)
  71. translatorAnimator.Rebind();
  72. if (Application.isPlaying) {
  73. translator.Apply(skeleton);
  74. } else {
  75. if (translatorAnimator != null && translatorAnimator.isInitialized &&
  76. translatorAnimator.isActiveAndEnabled && translatorAnimator.runtimeAnimatorController != null) {
  77. // Note: Rebind is required to prevent warning "Animator is not playing an AnimatorController" with prefabs
  78. translatorAnimator.Rebind();
  79. translator.Apply(skeleton);
  80. }
  81. }
  82. #else
  83. translator.Apply(skeleton);
  84. #endif
  85. // UpdateWorldTransform and Bone Callbacks
  86. {
  87. if (_UpdateLocal != null)
  88. _UpdateLocal(this);
  89. skeleton.UpdateWorldTransform();
  90. if (_UpdateWorld != null) {
  91. _UpdateWorld(this);
  92. skeleton.UpdateWorldTransform();
  93. }
  94. if (_UpdateComplete != null)
  95. _UpdateComplete(this);
  96. }
  97. wasUpdatedAfterInit = true;
  98. }
  99. public override void LateUpdate () {
  100. // instantiation can happen from Update() after this component, leading to a missing Update() call.
  101. if (!wasUpdatedAfterInit) Update();
  102. base.LateUpdate();
  103. }
  104. [System.Serializable]
  105. public class MecanimTranslator {
  106. #region Inspector
  107. public bool autoReset = true;
  108. public MixMode[] layerMixModes = new MixMode[0];
  109. public MixBlend[] layerBlendModes = new MixBlend[0];
  110. #endregion
  111. public delegate void OnClipAppliedDelegate (Spine.Animation clip, int layerIndex, float weight,
  112. float time, float lastTime, bool playsBackward);
  113. protected event OnClipAppliedDelegate _OnClipApplied;
  114. public event OnClipAppliedDelegate OnClipApplied { add { _OnClipApplied += value; } remove { _OnClipApplied -= value; } }
  115. public enum MixMode { AlwaysMix, MixNext, Hard }
  116. readonly Dictionary<int, Spine.Animation> animationTable = new Dictionary<int, Spine.Animation>(IntEqualityComparer.Instance);
  117. readonly Dictionary<AnimationClip, int> clipNameHashCodeTable = new Dictionary<AnimationClip, int>(AnimationClipEqualityComparer.Instance);
  118. readonly List<Animation> previousAnimations = new List<Animation>();
  119. protected class ClipInfos {
  120. public bool isInterruptionActive = false;
  121. public bool isLastFrameOfInterruption = false;
  122. public int clipInfoCount = 0;
  123. public int nextClipInfoCount = 0;
  124. public int interruptingClipInfoCount = 0;
  125. public readonly List<AnimatorClipInfo> clipInfos = new List<AnimatorClipInfo>();
  126. public readonly List<AnimatorClipInfo> nextClipInfos = new List<AnimatorClipInfo>();
  127. public readonly List<AnimatorClipInfo> interruptingClipInfos = new List<AnimatorClipInfo>();
  128. public AnimatorStateInfo stateInfo;
  129. public AnimatorStateInfo nextStateInfo;
  130. public AnimatorStateInfo interruptingStateInfo;
  131. public float interruptingClipTimeAddition = 0;
  132. }
  133. protected ClipInfos[] layerClipInfos = new ClipInfos[0];
  134. Animator animator;
  135. public Animator Animator { get { return this.animator; } }
  136. public int MecanimLayerCount {
  137. get {
  138. if (!animator)
  139. return 0;
  140. return animator.layerCount;
  141. }
  142. }
  143. public string[] MecanimLayerNames {
  144. get {
  145. if (!animator)
  146. return new string[0];
  147. string[] layerNames = new string[animator.layerCount];
  148. for (int i = 0; i < animator.layerCount; ++i) {
  149. layerNames[i] = animator.GetLayerName(i);
  150. }
  151. return layerNames;
  152. }
  153. }
  154. public void Initialize(Animator animator, SkeletonDataAsset skeletonDataAsset) {
  155. this.animator = animator;
  156. previousAnimations.Clear();
  157. animationTable.Clear();
  158. var data = skeletonDataAsset.GetSkeletonData(true);
  159. foreach (var a in data.Animations)
  160. animationTable.Add(a.Name.GetHashCode(), a);
  161. clipNameHashCodeTable.Clear();
  162. ClearClipInfosForLayers();
  163. }
  164. private bool ApplyAnimation (Skeleton skeleton, AnimatorClipInfo info, AnimatorStateInfo stateInfo,
  165. int layerIndex, float layerWeight, MixBlend layerBlendMode, bool useWeight1 = false) {
  166. float weight = info.weight * layerWeight;
  167. if (weight == 0)
  168. return false;
  169. var clip = GetAnimation(info.clip);
  170. if (clip == null)
  171. return false;
  172. var time = AnimationTime(stateInfo.normalizedTime, info.clip.length,
  173. info.clip.isLooping, stateInfo.speed < 0);
  174. weight = useWeight1 ? 1.0f : weight;
  175. clip.Apply(skeleton, 0, time, info.clip.isLooping, null,
  176. weight, layerBlendMode, MixDirection.In);
  177. if (_OnClipApplied != null)
  178. OnClipAppliedCallback(clip, stateInfo, layerIndex, time, info.clip.isLooping, weight);
  179. return true;
  180. }
  181. private bool ApplyInterruptionAnimation (Skeleton skeleton,
  182. bool interpolateWeightTo1, AnimatorClipInfo info, AnimatorStateInfo stateInfo,
  183. int layerIndex, float layerWeight, MixBlend layerBlendMode, float interruptingClipTimeAddition,
  184. bool useWeight1 = false) {
  185. float clipWeight = interpolateWeightTo1 ? (info.weight + 1.0f) * 0.5f : info.weight;
  186. float weight = clipWeight * layerWeight;
  187. if (weight == 0)
  188. return false;
  189. var clip = GetAnimation(info.clip);
  190. if (clip == null)
  191. return false;
  192. var time = AnimationTime(stateInfo.normalizedTime + interruptingClipTimeAddition,
  193. info.clip.length, stateInfo.speed < 0);
  194. weight = useWeight1 ? 1.0f : weight;
  195. clip.Apply(skeleton, 0, time, info.clip.isLooping, null,
  196. weight, layerBlendMode, MixDirection.In);
  197. if (_OnClipApplied != null) {
  198. OnClipAppliedCallback(clip, stateInfo, layerIndex, time, info.clip.isLooping, weight);
  199. }
  200. return true;
  201. }
  202. private void OnClipAppliedCallback (Spine.Animation clip, AnimatorStateInfo stateInfo,
  203. int layerIndex, float time, bool isLooping, float weight) {
  204. float clipDuration = clip.duration == 0 ? 1 : clip.duration;
  205. float speedFactor = stateInfo.speedMultiplier * stateInfo.speed;
  206. float lastTime = time - (Time.deltaTime * speedFactor);
  207. if (isLooping && clip.duration != 0) {
  208. time %= clip.duration;
  209. lastTime %= clip.duration;
  210. }
  211. _OnClipApplied(clip, layerIndex, weight, time, lastTime, speedFactor < 0);
  212. }
  213. public void Apply (Skeleton skeleton) {
  214. if (layerMixModes.Length < animator.layerCount) {
  215. int oldSize = layerMixModes.Length;
  216. System.Array.Resize<MixMode>(ref layerMixModes, animator.layerCount);
  217. for (int layer = oldSize; layer < animator.layerCount; ++layer) {
  218. layerMixModes[layer] = layer == 0 ? MixMode.MixNext : MixMode.AlwaysMix;
  219. }
  220. }
  221. #if UNITY_EDITOR
  222. if (!Application.isPlaying) {
  223. GetLayerBlendModes();
  224. }
  225. #endif
  226. InitClipInfosForLayers();
  227. for (int layer = 0, n = animator.layerCount; layer < n; layer++) {
  228. GetStateUpdatesFromAnimator(layer);
  229. }
  230. //skeleton.Update(Time.deltaTime); // Doesn't actually do anything, currently. (Spine 3.6).
  231. // Clear Previous
  232. if (autoReset) {
  233. var previousAnimations = this.previousAnimations;
  234. for (int i = 0, n = previousAnimations.Count; i < n; i++)
  235. previousAnimations[i].SetKeyedItemsToSetupPose(skeleton);
  236. previousAnimations.Clear();
  237. for (int layer = 0, n = animator.layerCount; layer < n; layer++) {
  238. float layerWeight = (layer == 0) ? 1 : animator.GetLayerWeight(layer); // Animator.GetLayerWeight always returns 0 on the first layer. Should be interpreted as 1.
  239. if (layerWeight <= 0) continue;
  240. AnimatorStateInfo nextStateInfo = animator.GetNextAnimatorStateInfo(layer);
  241. bool hasNext = nextStateInfo.fullPathHash != 0;
  242. int clipInfoCount, nextClipInfoCount, interruptingClipInfoCount;
  243. IList<AnimatorClipInfo> clipInfo, nextClipInfo, interruptingClipInfo;
  244. bool isInterruptionActive, shallInterpolateWeightTo1;
  245. GetAnimatorClipInfos(layer, out isInterruptionActive, out clipInfoCount, out nextClipInfoCount, out interruptingClipInfoCount,
  246. out clipInfo, out nextClipInfo, out interruptingClipInfo, out shallInterpolateWeightTo1);
  247. for (int c = 0; c < clipInfoCount; c++) {
  248. var info = clipInfo[c];
  249. float weight = info.weight * layerWeight; if (weight == 0) continue;
  250. var clip = GetAnimation(info.clip);
  251. if (clip != null)
  252. previousAnimations.Add(clip);
  253. }
  254. if (hasNext) {
  255. for (int c = 0; c < nextClipInfoCount; c++) {
  256. var info = nextClipInfo[c];
  257. float weight = info.weight * layerWeight; if (weight == 0) continue;
  258. var clip = GetAnimation(info.clip);
  259. if (clip != null)
  260. previousAnimations.Add(clip);
  261. }
  262. }
  263. if (isInterruptionActive) {
  264. for (int c = 0; c < interruptingClipInfoCount; c++)
  265. {
  266. var info = interruptingClipInfo[c];
  267. float clipWeight = shallInterpolateWeightTo1 ? (info.weight + 1.0f) * 0.5f : info.weight;
  268. float weight = clipWeight * layerWeight; if (weight == 0) continue;
  269. var clip = GetAnimation(info.clip);
  270. if (clip != null)
  271. previousAnimations.Add(clip);
  272. }
  273. }
  274. }
  275. }
  276. // Apply
  277. for (int layer = 0, n = animator.layerCount; layer < n; layer++) {
  278. float layerWeight = (layer == 0) ? 1 : animator.GetLayerWeight(layer); // Animator.GetLayerWeight always returns 0 on the first layer. Should be interpreted as 1.
  279. bool isInterruptionActive;
  280. AnimatorStateInfo stateInfo;
  281. AnimatorStateInfo nextStateInfo;
  282. AnimatorStateInfo interruptingStateInfo;
  283. float interruptingClipTimeAddition;
  284. GetAnimatorStateInfos(layer, out isInterruptionActive, out stateInfo, out nextStateInfo, out interruptingStateInfo, out interruptingClipTimeAddition);
  285. bool hasNext = nextStateInfo.fullPathHash != 0;
  286. int clipInfoCount, nextClipInfoCount, interruptingClipInfoCount;
  287. IList<AnimatorClipInfo> clipInfo, nextClipInfo, interruptingClipInfo;
  288. bool interpolateWeightTo1;
  289. GetAnimatorClipInfos(layer, out isInterruptionActive, out clipInfoCount, out nextClipInfoCount, out interruptingClipInfoCount,
  290. out clipInfo, out nextClipInfo, out interruptingClipInfo, out interpolateWeightTo1);
  291. MixMode mode = layerMixModes[layer];
  292. MixBlend layerBlendMode = (layer < layerBlendModes.Length) ? layerBlendModes[layer] : MixBlend.Replace;
  293. if (mode == MixMode.AlwaysMix) {
  294. // Always use Mix instead of Applying the first non-zero weighted clip.
  295. for (int c = 0; c < clipInfoCount; c++) {
  296. ApplyAnimation(skeleton, clipInfo[c], stateInfo, layer, layerWeight, layerBlendMode);
  297. }
  298. if (hasNext) {
  299. for (int c = 0; c < nextClipInfoCount; c++) {
  300. ApplyAnimation(skeleton, nextClipInfo[c], nextStateInfo, layer, layerWeight, layerBlendMode);
  301. }
  302. }
  303. if (isInterruptionActive) {
  304. for (int c = 0; c < interruptingClipInfoCount; c++)
  305. {
  306. ApplyInterruptionAnimation(skeleton, interpolateWeightTo1,
  307. interruptingClipInfo[c], interruptingStateInfo,
  308. layer, layerWeight, layerBlendMode, interruptingClipTimeAddition);
  309. }
  310. }
  311. } else { // case MixNext || Hard
  312. // Apply first non-zero weighted clip
  313. int c = 0;
  314. for (; c < clipInfoCount; c++) {
  315. if (!ApplyAnimation(skeleton, clipInfo[c], stateInfo, layer, layerWeight, layerBlendMode, useWeight1:true))
  316. continue;
  317. ++c; break;
  318. }
  319. // Mix the rest
  320. for (; c < clipInfoCount; c++) {
  321. ApplyAnimation(skeleton, clipInfo[c], stateInfo, layer, layerWeight, layerBlendMode);
  322. }
  323. c = 0;
  324. if (hasNext) {
  325. // Apply next clip directly instead of mixing (ie: no crossfade, ignores mecanim transition weights)
  326. if (mode == MixMode.Hard) {
  327. for (; c < nextClipInfoCount; c++) {
  328. if (!ApplyAnimation(skeleton, nextClipInfo[c], nextStateInfo, layer, layerWeight, layerBlendMode, useWeight1:true))
  329. continue;
  330. ++c; break;
  331. }
  332. }
  333. // Mix the rest
  334. for (; c < nextClipInfoCount; c++) {
  335. if (!ApplyAnimation(skeleton, nextClipInfo[c], nextStateInfo, layer, layerWeight, layerBlendMode))
  336. continue;
  337. }
  338. }
  339. c = 0;
  340. if (isInterruptionActive) {
  341. // Apply next clip directly instead of mixing (ie: no crossfade, ignores mecanim transition weights)
  342. if (mode == MixMode.Hard) {
  343. for (; c < interruptingClipInfoCount; c++) {
  344. if (ApplyInterruptionAnimation(skeleton, interpolateWeightTo1,
  345. interruptingClipInfo[c], interruptingStateInfo,
  346. layer, layerWeight, layerBlendMode, interruptingClipTimeAddition, useWeight1:true)) {
  347. ++c; break;
  348. }
  349. }
  350. }
  351. // Mix the rest
  352. for (; c < interruptingClipInfoCount; c++) {
  353. ApplyInterruptionAnimation(skeleton, interpolateWeightTo1,
  354. interruptingClipInfo[c], interruptingStateInfo,
  355. layer, layerWeight, layerBlendMode, interruptingClipTimeAddition);
  356. }
  357. }
  358. }
  359. }
  360. }
  361. static float AnimationTime (float normalizedTime, float clipLength, bool loop, bool reversed) {
  362. float time = AnimationTime(normalizedTime, clipLength, reversed);
  363. if (loop) return time;
  364. const float EndSnapEpsilon = 1f / 30f; // Workaround for end-duration keys not being applied.
  365. return (clipLength - time < EndSnapEpsilon) ? clipLength : time; // return a time snapped to clipLength;
  366. }
  367. static float AnimationTime (float normalizedTime, float clipLength, bool reversed) {
  368. if (reversed)
  369. normalizedTime = (1 - normalizedTime + (int)normalizedTime) + (int)normalizedTime;
  370. return normalizedTime * clipLength;
  371. }
  372. void InitClipInfosForLayers () {
  373. if (layerClipInfos.Length < animator.layerCount) {
  374. System.Array.Resize<ClipInfos>(ref layerClipInfos, animator.layerCount);
  375. for (int layer = 0, n = animator.layerCount; layer < n; ++layer) {
  376. if (layerClipInfos[layer] == null)
  377. layerClipInfos[layer] = new ClipInfos();
  378. }
  379. }
  380. }
  381. void ClearClipInfosForLayers () {
  382. for (int layer = 0, n = layerClipInfos.Length; layer < n; ++layer) {
  383. if (layerClipInfos[layer] == null)
  384. layerClipInfos[layer] = new ClipInfos();
  385. else {
  386. layerClipInfos[layer].isInterruptionActive = false;
  387. layerClipInfos[layer].isLastFrameOfInterruption = false;
  388. layerClipInfos[layer].clipInfos.Clear();
  389. layerClipInfos[layer].nextClipInfos.Clear();
  390. layerClipInfos[layer].interruptingClipInfos.Clear();
  391. }
  392. }
  393. }
  394. #if UNITY_EDITOR
  395. void GetLayerBlendModes() {
  396. if (layerBlendModes.Length < animator.layerCount) {
  397. System.Array.Resize<MixBlend>(ref layerBlendModes, animator.layerCount);
  398. }
  399. for (int layer = 0, n = animator.layerCount; layer < n; ++layer) {
  400. var controller = animator.runtimeAnimatorController as UnityEditor.Animations.AnimatorController;
  401. if (controller != null) {
  402. layerBlendModes[layer] = MixBlend.First;
  403. if (layer > 0) {
  404. layerBlendModes[layer] = controller.layers[layer].blendingMode == UnityEditor.Animations.AnimatorLayerBlendingMode.Additive ?
  405. MixBlend.Add : MixBlend.Replace;
  406. }
  407. }
  408. }
  409. }
  410. #endif
  411. void GetStateUpdatesFromAnimator (int layer) {
  412. var layerInfos = layerClipInfos[layer];
  413. int clipInfoCount = animator.GetCurrentAnimatorClipInfoCount(layer);
  414. int nextClipInfoCount = animator.GetNextAnimatorClipInfoCount(layer);
  415. var clipInfos = layerInfos.clipInfos;
  416. var nextClipInfos = layerInfos.nextClipInfos;
  417. var interruptingClipInfos = layerInfos.interruptingClipInfos;
  418. layerInfos.isInterruptionActive = (clipInfoCount == 0 && clipInfos.Count != 0 &&
  419. nextClipInfoCount == 0 && nextClipInfos.Count != 0);
  420. // Note: during interruption, GetCurrentAnimatorClipInfoCount and GetNextAnimatorClipInfoCount
  421. // are returning 0 in calls above. Therefore we keep previous clipInfos and nextClipInfos
  422. // until the interruption is over.
  423. if (layerInfos.isInterruptionActive) {
  424. // Note: The last frame of a transition interruption
  425. // will have fullPathHash set to 0, therefore we have to use previous
  426. // frame's infos about interruption clips and correct some values
  427. // accordingly (normalizedTime and weight).
  428. var interruptingStateInfo = animator.GetNextAnimatorStateInfo(layer);
  429. layerInfos.isLastFrameOfInterruption = interruptingStateInfo.fullPathHash == 0;
  430. if (!layerInfos.isLastFrameOfInterruption) {
  431. animator.GetNextAnimatorClipInfo(layer, interruptingClipInfos);
  432. layerInfos.interruptingClipInfoCount = interruptingClipInfos.Count;
  433. float oldTime = layerInfos.interruptingStateInfo.normalizedTime;
  434. float newTime = interruptingStateInfo.normalizedTime;
  435. layerInfos.interruptingClipTimeAddition = newTime - oldTime;
  436. layerInfos.interruptingStateInfo = interruptingStateInfo;
  437. }
  438. } else {
  439. layerInfos.clipInfoCount = clipInfoCount;
  440. layerInfos.nextClipInfoCount = nextClipInfoCount;
  441. layerInfos.interruptingClipInfoCount = 0;
  442. layerInfos.isLastFrameOfInterruption = false;
  443. if (clipInfos.Capacity < clipInfoCount) clipInfos.Capacity = clipInfoCount;
  444. if (nextClipInfos.Capacity < nextClipInfoCount) nextClipInfos.Capacity = nextClipInfoCount;
  445. animator.GetCurrentAnimatorClipInfo(layer, clipInfos);
  446. animator.GetNextAnimatorClipInfo(layer, nextClipInfos);
  447. layerInfos.stateInfo = animator.GetCurrentAnimatorStateInfo(layer);
  448. layerInfos.nextStateInfo = animator.GetNextAnimatorStateInfo(layer);
  449. }
  450. }
  451. void GetAnimatorClipInfos (
  452. int layer,
  453. out bool isInterruptionActive,
  454. out int clipInfoCount,
  455. out int nextClipInfoCount,
  456. out int interruptingClipInfoCount,
  457. out IList<AnimatorClipInfo> clipInfo,
  458. out IList<AnimatorClipInfo> nextClipInfo,
  459. out IList<AnimatorClipInfo> interruptingClipInfo,
  460. out bool shallInterpolateWeightTo1) {
  461. var layerInfos = layerClipInfos[layer];
  462. isInterruptionActive = layerInfos.isInterruptionActive;
  463. clipInfoCount = layerInfos.clipInfoCount;
  464. nextClipInfoCount = layerInfos.nextClipInfoCount;
  465. interruptingClipInfoCount = layerInfos.interruptingClipInfoCount;
  466. clipInfo = layerInfos.clipInfos;
  467. nextClipInfo = layerInfos.nextClipInfos;
  468. interruptingClipInfo = isInterruptionActive ? layerInfos.interruptingClipInfos : null;
  469. shallInterpolateWeightTo1 = layerInfos.isLastFrameOfInterruption;
  470. }
  471. void GetAnimatorStateInfos (
  472. int layer,
  473. out bool isInterruptionActive,
  474. out AnimatorStateInfo stateInfo,
  475. out AnimatorStateInfo nextStateInfo,
  476. out AnimatorStateInfo interruptingStateInfo,
  477. out float interruptingClipTimeAddition) {
  478. var layerInfos = layerClipInfos[layer];
  479. isInterruptionActive = layerInfos.isInterruptionActive;
  480. stateInfo = layerInfos.stateInfo;
  481. nextStateInfo = layerInfos.nextStateInfo;
  482. interruptingStateInfo = layerInfos.interruptingStateInfo;
  483. interruptingClipTimeAddition = layerInfos.isLastFrameOfInterruption ? layerInfos.interruptingClipTimeAddition : 0;
  484. }
  485. Spine.Animation GetAnimation (AnimationClip clip) {
  486. int clipNameHashCode;
  487. if (!clipNameHashCodeTable.TryGetValue(clip, out clipNameHashCode)) {
  488. clipNameHashCode = clip.name.GetHashCode();
  489. clipNameHashCodeTable.Add(clip, clipNameHashCode);
  490. }
  491. Spine.Animation animation;
  492. animationTable.TryGetValue(clipNameHashCode, out animation);
  493. return animation;
  494. }
  495. class AnimationClipEqualityComparer : IEqualityComparer<AnimationClip> {
  496. internal static readonly IEqualityComparer<AnimationClip> Instance = new AnimationClipEqualityComparer();
  497. public bool Equals (AnimationClip x, AnimationClip y) { return x.GetInstanceID() == y.GetInstanceID(); }
  498. public int GetHashCode (AnimationClip o) { return o.GetInstanceID(); }
  499. }
  500. class IntEqualityComparer : IEqualityComparer<int> {
  501. internal static readonly IEqualityComparer<int> Instance = new IntEqualityComparer();
  502. public bool Equals (int x, int y) { return x == y; }
  503. public int GetHashCode(int o) { return o; }
  504. }
  505. }
  506. }
  507. }