SkeletonMecanim.cs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661
  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. [HelpURL("http://esotericsoftware.com/spine-unity#SkeletonMecanim-Component")]
  34. public class SkeletonMecanim : SkeletonRenderer, ISkeletonAnimation {
  35. [SerializeField] protected MecanimTranslator translator;
  36. public MecanimTranslator Translator { get { return translator; } }
  37. private bool wasUpdatedAfterInit = true;
  38. #region Bone Callbacks (ISkeletonAnimation)
  39. protected event UpdateBonesDelegate _BeforeApply;
  40. protected event UpdateBonesDelegate _UpdateLocal;
  41. protected event UpdateBonesDelegate _UpdateWorld;
  42. protected event UpdateBonesDelegate _UpdateComplete;
  43. /// <summary>
  44. /// Occurs before the animations are applied.
  45. /// Use this callback when you want to change the skeleton state before animations are applied on top.
  46. /// </summary>
  47. public event UpdateBonesDelegate BeforeApply { add { _BeforeApply += value; } remove { _BeforeApply -= value; } }
  48. /// <summary>
  49. /// Occurs after the animations are applied and before world space values are resolved.
  50. /// Use this callback when you want to set bone local values.</summary>
  51. public event UpdateBonesDelegate UpdateLocal { add { _UpdateLocal += value; } remove { _UpdateLocal -= value; } }
  52. /// <summary>
  53. /// Occurs after the Skeleton's bone world space values are resolved (including all constraints).
  54. /// Using this callback will cause the world space values to be solved an extra time.
  55. /// Use this callback if want to use bone world space values, and also set bone local values.</summary>
  56. public event UpdateBonesDelegate UpdateWorld { add { _UpdateWorld += value; } remove { _UpdateWorld -= value; } }
  57. /// <summary>
  58. /// Occurs after the Skeleton's bone world space values are resolved (including all constraints).
  59. /// Use this callback if you want to use bone world space values, but don't intend to modify bone local values.
  60. /// This callback can also be used when setting world position and the bone matrix.</summary>
  61. public event UpdateBonesDelegate UpdateComplete { add { _UpdateComplete += value; } remove { _UpdateComplete -= value; } }
  62. #endregion
  63. public override void Initialize (bool overwrite, bool quiet = false) {
  64. if (valid && !overwrite)
  65. return;
  66. base.Initialize(overwrite, quiet);
  67. if (!valid)
  68. return;
  69. if (translator == null) translator = new MecanimTranslator();
  70. translator.Initialize(GetComponent<Animator>(), this.skeletonDataAsset);
  71. wasUpdatedAfterInit = false;
  72. }
  73. public void Update () {
  74. if (!valid) return;
  75. wasUpdatedAfterInit = true;
  76. // animation status is kept by Mecanim Animator component
  77. if (updateMode <= UpdateMode.OnlyAnimationStatus)
  78. return;
  79. ApplyAnimation();
  80. }
  81. protected void ApplyAnimation () {
  82. if (_BeforeApply != null)
  83. _BeforeApply(this);
  84. #if UNITY_EDITOR
  85. var translatorAnimator = translator.Animator;
  86. if (translatorAnimator != null && !translatorAnimator.isInitialized)
  87. translatorAnimator.Rebind();
  88. if (Application.isPlaying) {
  89. translator.Apply(skeleton);
  90. }
  91. else {
  92. if (translatorAnimator != null && translatorAnimator.isInitialized &&
  93. translatorAnimator.isActiveAndEnabled && translatorAnimator.runtimeAnimatorController != null) {
  94. // Note: Rebind is required to prevent warning "Animator is not playing an AnimatorController" with prefabs
  95. translatorAnimator.Rebind();
  96. translator.Apply(skeleton);
  97. }
  98. }
  99. #else
  100. translator.Apply(skeleton);
  101. #endif
  102. // UpdateWorldTransform and Bone Callbacks
  103. {
  104. if (_UpdateLocal != null)
  105. _UpdateLocal(this);
  106. skeleton.UpdateWorldTransform();
  107. if (_UpdateWorld != null) {
  108. _UpdateWorld(this);
  109. skeleton.UpdateWorldTransform();
  110. }
  111. if (_UpdateComplete != null)
  112. _UpdateComplete(this);
  113. }
  114. }
  115. public override void LateUpdate () {
  116. // instantiation can happen from Update() after this component, leading to a missing Update() call.
  117. if (!wasUpdatedAfterInit) Update();
  118. base.LateUpdate();
  119. }
  120. [System.Serializable]
  121. public class MecanimTranslator {
  122. const float WeightEpsilon = 0.0001f;
  123. #region Inspector
  124. public bool autoReset = true;
  125. public bool useCustomMixMode = true;
  126. public MixMode[] layerMixModes = new MixMode[0];
  127. public MixBlend[] layerBlendModes = new MixBlend[0];
  128. #endregion
  129. public delegate void OnClipAppliedDelegate (Spine.Animation clip, int layerIndex, float weight,
  130. float time, float lastTime, bool playsBackward);
  131. protected event OnClipAppliedDelegate _OnClipApplied;
  132. public event OnClipAppliedDelegate OnClipApplied { add { _OnClipApplied += value; } remove { _OnClipApplied -= value; } }
  133. public enum MixMode { AlwaysMix, MixNext, Hard }
  134. readonly Dictionary<int, Spine.Animation> animationTable = new Dictionary<int, Spine.Animation>(IntEqualityComparer.Instance);
  135. readonly Dictionary<AnimationClip, int> clipNameHashCodeTable = new Dictionary<AnimationClip, int>(AnimationClipEqualityComparer.Instance);
  136. readonly List<Animation> previousAnimations = new List<Animation>();
  137. protected class ClipInfos {
  138. public bool isInterruptionActive = false;
  139. public bool isLastFrameOfInterruption = false;
  140. public int clipInfoCount = 0;
  141. public int nextClipInfoCount = 0;
  142. public int interruptingClipInfoCount = 0;
  143. public readonly List<AnimatorClipInfo> clipInfos = new List<AnimatorClipInfo>();
  144. public readonly List<AnimatorClipInfo> nextClipInfos = new List<AnimatorClipInfo>();
  145. public readonly List<AnimatorClipInfo> interruptingClipInfos = new List<AnimatorClipInfo>();
  146. public AnimatorStateInfo stateInfo;
  147. public AnimatorStateInfo nextStateInfo;
  148. public AnimatorStateInfo interruptingStateInfo;
  149. public float interruptingClipTimeAddition = 0;
  150. }
  151. protected ClipInfos[] layerClipInfos = new ClipInfos[0];
  152. Animator animator;
  153. public Animator Animator { get { return this.animator; } }
  154. public int MecanimLayerCount {
  155. get {
  156. if (!animator)
  157. return 0;
  158. return animator.layerCount;
  159. }
  160. }
  161. public string[] MecanimLayerNames {
  162. get {
  163. if (!animator)
  164. return new string[0];
  165. string[] layerNames = new string[animator.layerCount];
  166. for (int i = 0; i < animator.layerCount; ++i) {
  167. layerNames[i] = animator.GetLayerName(i);
  168. }
  169. return layerNames;
  170. }
  171. }
  172. public void Initialize(Animator animator, SkeletonDataAsset skeletonDataAsset) {
  173. this.animator = animator;
  174. previousAnimations.Clear();
  175. animationTable.Clear();
  176. var data = skeletonDataAsset.GetSkeletonData(true);
  177. foreach (var a in data.Animations)
  178. animationTable.Add(a.Name.GetHashCode(), a);
  179. clipNameHashCodeTable.Clear();
  180. ClearClipInfosForLayers();
  181. }
  182. private bool ApplyAnimation (Skeleton skeleton, AnimatorClipInfo info, AnimatorStateInfo stateInfo,
  183. int layerIndex, float layerWeight, MixBlend layerBlendMode, bool useClipWeight1 = false) {
  184. float weight = info.weight * layerWeight;
  185. if (weight < WeightEpsilon)
  186. return false;
  187. var clip = GetAnimation(info.clip);
  188. if (clip == null)
  189. return false;
  190. var time = AnimationTime(stateInfo.normalizedTime, info.clip.length,
  191. info.clip.isLooping, stateInfo.speed < 0);
  192. weight = useClipWeight1 ? layerWeight : weight;
  193. clip.Apply(skeleton, 0, time, info.clip.isLooping, null,
  194. weight, layerBlendMode, MixDirection.In);
  195. if (_OnClipApplied != null)
  196. OnClipAppliedCallback(clip, stateInfo, layerIndex, time, info.clip.isLooping, weight);
  197. return true;
  198. }
  199. private bool ApplyInterruptionAnimation (Skeleton skeleton,
  200. bool interpolateWeightTo1, AnimatorClipInfo info, AnimatorStateInfo stateInfo,
  201. int layerIndex, float layerWeight, MixBlend layerBlendMode, float interruptingClipTimeAddition,
  202. bool useClipWeight1 = false) {
  203. float clipWeight = interpolateWeightTo1 ? (info.weight + 1.0f) * 0.5f : info.weight;
  204. float weight = clipWeight * layerWeight;
  205. if (weight < WeightEpsilon)
  206. return false;
  207. var clip = GetAnimation(info.clip);
  208. if (clip == null)
  209. return false;
  210. var time = AnimationTime(stateInfo.normalizedTime + interruptingClipTimeAddition,
  211. info.clip.length, stateInfo.speed < 0);
  212. weight = useClipWeight1 ? layerWeight : weight;
  213. clip.Apply(skeleton, 0, time, info.clip.isLooping, null,
  214. weight, layerBlendMode, MixDirection.In);
  215. if (_OnClipApplied != null) {
  216. OnClipAppliedCallback(clip, stateInfo, layerIndex, time, info.clip.isLooping, weight);
  217. }
  218. return true;
  219. }
  220. private void OnClipAppliedCallback (Spine.Animation clip, AnimatorStateInfo stateInfo,
  221. int layerIndex, float time, bool isLooping, float weight) {
  222. float speedFactor = stateInfo.speedMultiplier * stateInfo.speed;
  223. float lastTime = time - (Time.deltaTime * speedFactor);
  224. if (isLooping && clip.duration != 0) {
  225. time %= clip.duration;
  226. lastTime %= clip.duration;
  227. }
  228. _OnClipApplied(clip, layerIndex, weight, time, lastTime, speedFactor < 0);
  229. }
  230. public void Apply (Skeleton skeleton) {
  231. #if UNITY_EDITOR
  232. if (!Application.isPlaying) {
  233. GetLayerBlendModes();
  234. }
  235. #endif
  236. if (layerMixModes.Length < animator.layerCount) {
  237. int oldSize = layerMixModes.Length;
  238. System.Array.Resize<MixMode>(ref layerMixModes, animator.layerCount);
  239. for (int layer = oldSize; layer < animator.layerCount; ++layer) {
  240. bool isAdditiveLayer = false;
  241. if (layer < layerBlendModes.Length)
  242. isAdditiveLayer = layerBlendModes[layer] == MixBlend.Add;
  243. layerMixModes[layer] = isAdditiveLayer ? MixMode.AlwaysMix : MixMode.MixNext;
  244. }
  245. }
  246. InitClipInfosForLayers();
  247. for (int layer = 0, n = animator.layerCount; layer < n; layer++) {
  248. GetStateUpdatesFromAnimator(layer);
  249. }
  250. // Clear Previous
  251. if (autoReset) {
  252. var previousAnimations = this.previousAnimations;
  253. for (int i = 0, n = previousAnimations.Count; i < n; i++)
  254. previousAnimations[i].SetKeyedItemsToSetupPose(skeleton);
  255. previousAnimations.Clear();
  256. for (int layer = 0, n = animator.layerCount; layer < n; layer++) {
  257. float layerWeight = (layer == 0) ? 1 : animator.GetLayerWeight(layer); // Animator.GetLayerWeight always returns 0 on the first layer. Should be interpreted as 1.
  258. if (layerWeight <= 0) continue;
  259. AnimatorStateInfo nextStateInfo = animator.GetNextAnimatorStateInfo(layer);
  260. bool hasNext = nextStateInfo.fullPathHash != 0;
  261. int clipInfoCount, nextClipInfoCount, interruptingClipInfoCount;
  262. IList<AnimatorClipInfo> clipInfo, nextClipInfo, interruptingClipInfo;
  263. bool isInterruptionActive, shallInterpolateWeightTo1;
  264. GetAnimatorClipInfos(layer, out isInterruptionActive, out clipInfoCount, out nextClipInfoCount, out interruptingClipInfoCount,
  265. out clipInfo, out nextClipInfo, out interruptingClipInfo, out shallInterpolateWeightTo1);
  266. for (int c = 0; c < clipInfoCount; c++) {
  267. var info = clipInfo[c];
  268. float weight = info.weight * layerWeight; if (weight < WeightEpsilon) continue;
  269. var clip = GetAnimation(info.clip);
  270. if (clip != null)
  271. previousAnimations.Add(clip);
  272. }
  273. if (hasNext) {
  274. for (int c = 0; c < nextClipInfoCount; c++) {
  275. var info = nextClipInfo[c];
  276. float weight = info.weight * layerWeight; if (weight < WeightEpsilon) continue;
  277. var clip = GetAnimation(info.clip);
  278. if (clip != null)
  279. previousAnimations.Add(clip);
  280. }
  281. }
  282. if (isInterruptionActive) {
  283. for (int c = 0; c < interruptingClipInfoCount; c++)
  284. {
  285. var info = interruptingClipInfo[c];
  286. float clipWeight = shallInterpolateWeightTo1 ? (info.weight + 1.0f) * 0.5f : info.weight;
  287. float weight = clipWeight * layerWeight; if (weight < WeightEpsilon) continue;
  288. var clip = GetAnimation(info.clip);
  289. if (clip != null)
  290. previousAnimations.Add(clip);
  291. }
  292. }
  293. }
  294. }
  295. // Apply
  296. for (int layer = 0, n = animator.layerCount; layer < n; layer++) {
  297. float layerWeight = (layer == 0) ? 1 : animator.GetLayerWeight(layer); // Animator.GetLayerWeight always returns 0 on the first layer. Should be interpreted as 1.
  298. bool isInterruptionActive;
  299. AnimatorStateInfo stateInfo;
  300. AnimatorStateInfo nextStateInfo;
  301. AnimatorStateInfo interruptingStateInfo;
  302. float interruptingClipTimeAddition;
  303. GetAnimatorStateInfos(layer, out isInterruptionActive, out stateInfo, out nextStateInfo, out interruptingStateInfo, out interruptingClipTimeAddition);
  304. bool hasNext = nextStateInfo.fullPathHash != 0;
  305. int clipInfoCount, nextClipInfoCount, interruptingClipInfoCount;
  306. IList<AnimatorClipInfo> clipInfo, nextClipInfo, interruptingClipInfo;
  307. bool interpolateWeightTo1;
  308. GetAnimatorClipInfos(layer, out isInterruptionActive, out clipInfoCount, out nextClipInfoCount, out interruptingClipInfoCount,
  309. out clipInfo, out nextClipInfo, out interruptingClipInfo, out interpolateWeightTo1);
  310. MixBlend layerBlendMode = (layer < layerBlendModes.Length) ? layerBlendModes[layer] : MixBlend.Replace;
  311. MixMode mode = GetMixMode(layer, layerBlendMode);
  312. if (mode == MixMode.AlwaysMix) {
  313. // Always use Mix instead of Applying the first non-zero weighted clip.
  314. for (int c = 0; c < clipInfoCount; c++) {
  315. ApplyAnimation(skeleton, clipInfo[c], stateInfo, layer, layerWeight, layerBlendMode);
  316. }
  317. if (hasNext) {
  318. for (int c = 0; c < nextClipInfoCount; c++) {
  319. ApplyAnimation(skeleton, nextClipInfo[c], nextStateInfo, layer, layerWeight, layerBlendMode);
  320. }
  321. }
  322. if (isInterruptionActive) {
  323. for (int c = 0; c < interruptingClipInfoCount; c++)
  324. {
  325. ApplyInterruptionAnimation(skeleton, interpolateWeightTo1,
  326. interruptingClipInfo[c], interruptingStateInfo,
  327. layer, layerWeight, layerBlendMode, interruptingClipTimeAddition);
  328. }
  329. }
  330. } else { // case MixNext || Hard
  331. // Apply first non-zero weighted clip
  332. int c = 0;
  333. for (; c < clipInfoCount; c++) {
  334. if (!ApplyAnimation(skeleton, clipInfo[c], stateInfo, layer, layerWeight, layerBlendMode, useClipWeight1:true))
  335. continue;
  336. ++c; break;
  337. }
  338. // Mix the rest
  339. for (; c < clipInfoCount; c++) {
  340. ApplyAnimation(skeleton, clipInfo[c], stateInfo, layer, layerWeight, layerBlendMode);
  341. }
  342. c = 0;
  343. if (hasNext) {
  344. // Apply next clip directly instead of mixing (ie: no crossfade, ignores mecanim transition weights)
  345. if (mode == MixMode.Hard) {
  346. for (; c < nextClipInfoCount; c++) {
  347. if (!ApplyAnimation(skeleton, nextClipInfo[c], nextStateInfo, layer, layerWeight, layerBlendMode, useClipWeight1:true))
  348. continue;
  349. ++c; break;
  350. }
  351. }
  352. // Mix the rest
  353. for (; c < nextClipInfoCount; c++) {
  354. if (!ApplyAnimation(skeleton, nextClipInfo[c], nextStateInfo, layer, layerWeight, layerBlendMode))
  355. continue;
  356. }
  357. }
  358. c = 0;
  359. if (isInterruptionActive) {
  360. // Apply next clip directly instead of mixing (ie: no crossfade, ignores mecanim transition weights)
  361. if (mode == MixMode.Hard) {
  362. for (; c < interruptingClipInfoCount; c++) {
  363. if (ApplyInterruptionAnimation(skeleton, interpolateWeightTo1,
  364. interruptingClipInfo[c], interruptingStateInfo,
  365. layer, layerWeight, layerBlendMode, interruptingClipTimeAddition, useClipWeight1:true)) {
  366. ++c; break;
  367. }
  368. }
  369. }
  370. // Mix the rest
  371. for (; c < interruptingClipInfoCount; c++) {
  372. ApplyInterruptionAnimation(skeleton, interpolateWeightTo1,
  373. interruptingClipInfo[c], interruptingStateInfo,
  374. layer, layerWeight, layerBlendMode, interruptingClipTimeAddition);
  375. }
  376. }
  377. }
  378. }
  379. }
  380. public KeyValuePair<Spine.Animation, float> GetActiveAnimationAndTime (int layer) {
  381. if (layer >= layerClipInfos.Length)
  382. return new KeyValuePair<Spine.Animation, float>(null, 0);
  383. var layerInfos = layerClipInfos[layer];
  384. bool isInterruptionActive = layerInfos.isInterruptionActive;
  385. AnimationClip clip = null;
  386. Spine.Animation animation = null;
  387. AnimatorStateInfo stateInfo;
  388. if (isInterruptionActive && layerInfos.interruptingClipInfoCount > 0) {
  389. clip = layerInfos.interruptingClipInfos[0].clip;
  390. stateInfo = layerInfos.interruptingStateInfo;
  391. }
  392. else {
  393. clip = layerInfos.clipInfos[0].clip;
  394. stateInfo = layerInfos.stateInfo;
  395. }
  396. animation = GetAnimation(clip);
  397. float time = AnimationTime(stateInfo.normalizedTime, clip.length,
  398. clip.isLooping, stateInfo.speed < 0);
  399. return new KeyValuePair<Animation, float>(animation, time);
  400. }
  401. static float AnimationTime (float normalizedTime, float clipLength, bool loop, bool reversed) {
  402. float time = AnimationTime(normalizedTime, clipLength, reversed);
  403. if (loop) return time;
  404. const float EndSnapEpsilon = 1f / 30f; // Workaround for end-duration keys not being applied.
  405. return (clipLength - time < EndSnapEpsilon) ? clipLength : time; // return a time snapped to clipLength;
  406. }
  407. static float AnimationTime (float normalizedTime, float clipLength, bool reversed) {
  408. if (reversed)
  409. normalizedTime = (1 - normalizedTime);
  410. if (normalizedTime < 0.0f)
  411. normalizedTime = (normalizedTime % 1.0f) + 1.0f;
  412. return normalizedTime * clipLength;
  413. }
  414. void InitClipInfosForLayers () {
  415. if (layerClipInfos.Length < animator.layerCount) {
  416. System.Array.Resize<ClipInfos>(ref layerClipInfos, animator.layerCount);
  417. for (int layer = 0, n = animator.layerCount; layer < n; ++layer) {
  418. if (layerClipInfos[layer] == null)
  419. layerClipInfos[layer] = new ClipInfos();
  420. }
  421. }
  422. }
  423. void ClearClipInfosForLayers () {
  424. for (int layer = 0, n = layerClipInfos.Length; layer < n; ++layer) {
  425. if (layerClipInfos[layer] == null)
  426. layerClipInfos[layer] = new ClipInfos();
  427. else {
  428. layerClipInfos[layer].isInterruptionActive = false;
  429. layerClipInfos[layer].isLastFrameOfInterruption = false;
  430. layerClipInfos[layer].clipInfos.Clear();
  431. layerClipInfos[layer].nextClipInfos.Clear();
  432. layerClipInfos[layer].interruptingClipInfos.Clear();
  433. }
  434. }
  435. }
  436. private MixMode GetMixMode (int layer, MixBlend layerBlendMode) {
  437. if (useCustomMixMode) {
  438. MixMode mode = layerMixModes[layer];
  439. // Note: at additive blending it makes no sense to use constant weight 1 at a fadeout anim add1 as
  440. // with override layers, so we use AlwaysMix instead to use the proper weights.
  441. // AlwaysMix leads to the expected result = lower_layer + lerp(add1, add2, transition_weight).
  442. if (layerBlendMode == MixBlend.Add && mode == MixMode.MixNext) {
  443. mode = MixMode.AlwaysMix;
  444. layerMixModes[layer] = mode;
  445. }
  446. return mode;
  447. }
  448. else {
  449. return layerBlendMode == MixBlend.Add ? MixMode.AlwaysMix : MixMode.MixNext;
  450. }
  451. }
  452. #if UNITY_EDITOR
  453. void GetLayerBlendModes() {
  454. if (layerBlendModes.Length < animator.layerCount) {
  455. System.Array.Resize<MixBlend>(ref layerBlendModes, animator.layerCount);
  456. }
  457. for (int layer = 0, n = animator.layerCount; layer < n; ++layer) {
  458. var controller = animator.runtimeAnimatorController as UnityEditor.Animations.AnimatorController;
  459. if (controller != null) {
  460. layerBlendModes[layer] = MixBlend.First;
  461. if (layer > 0) {
  462. layerBlendModes[layer] = controller.layers[layer].blendingMode == UnityEditor.Animations.AnimatorLayerBlendingMode.Additive ?
  463. MixBlend.Add : MixBlend.Replace;
  464. }
  465. }
  466. }
  467. }
  468. #endif
  469. void GetStateUpdatesFromAnimator (int layer) {
  470. var layerInfos = layerClipInfos[layer];
  471. int clipInfoCount = animator.GetCurrentAnimatorClipInfoCount(layer);
  472. int nextClipInfoCount = animator.GetNextAnimatorClipInfoCount(layer);
  473. var clipInfos = layerInfos.clipInfos;
  474. var nextClipInfos = layerInfos.nextClipInfos;
  475. var interruptingClipInfos = layerInfos.interruptingClipInfos;
  476. layerInfos.isInterruptionActive = (clipInfoCount == 0 && clipInfos.Count != 0 &&
  477. nextClipInfoCount == 0 && nextClipInfos.Count != 0);
  478. // Note: during interruption, GetCurrentAnimatorClipInfoCount and GetNextAnimatorClipInfoCount
  479. // are returning 0 in calls above. Therefore we keep previous clipInfos and nextClipInfos
  480. // until the interruption is over.
  481. if (layerInfos.isInterruptionActive) {
  482. // Note: The last frame of a transition interruption
  483. // will have fullPathHash set to 0, therefore we have to use previous
  484. // frame's infos about interruption clips and correct some values
  485. // accordingly (normalizedTime and weight).
  486. var interruptingStateInfo = animator.GetNextAnimatorStateInfo(layer);
  487. layerInfos.isLastFrameOfInterruption = interruptingStateInfo.fullPathHash == 0;
  488. if (!layerInfos.isLastFrameOfInterruption) {
  489. animator.GetNextAnimatorClipInfo(layer, interruptingClipInfos);
  490. layerInfos.interruptingClipInfoCount = interruptingClipInfos.Count;
  491. float oldTime = layerInfos.interruptingStateInfo.normalizedTime;
  492. float newTime = interruptingStateInfo.normalizedTime;
  493. layerInfos.interruptingClipTimeAddition = newTime - oldTime;
  494. layerInfos.interruptingStateInfo = interruptingStateInfo;
  495. }
  496. } else {
  497. layerInfos.clipInfoCount = clipInfoCount;
  498. layerInfos.nextClipInfoCount = nextClipInfoCount;
  499. layerInfos.interruptingClipInfoCount = 0;
  500. layerInfos.isLastFrameOfInterruption = false;
  501. if (clipInfos.Capacity < clipInfoCount) clipInfos.Capacity = clipInfoCount;
  502. if (nextClipInfos.Capacity < nextClipInfoCount) nextClipInfos.Capacity = nextClipInfoCount;
  503. animator.GetCurrentAnimatorClipInfo(layer, clipInfos);
  504. animator.GetNextAnimatorClipInfo(layer, nextClipInfos);
  505. layerInfos.stateInfo = animator.GetCurrentAnimatorStateInfo(layer);
  506. layerInfos.nextStateInfo = animator.GetNextAnimatorStateInfo(layer);
  507. }
  508. }
  509. void GetAnimatorClipInfos (
  510. int layer,
  511. out bool isInterruptionActive,
  512. out int clipInfoCount,
  513. out int nextClipInfoCount,
  514. out int interruptingClipInfoCount,
  515. out IList<AnimatorClipInfo> clipInfo,
  516. out IList<AnimatorClipInfo> nextClipInfo,
  517. out IList<AnimatorClipInfo> interruptingClipInfo,
  518. out bool shallInterpolateWeightTo1) {
  519. var layerInfos = layerClipInfos[layer];
  520. isInterruptionActive = layerInfos.isInterruptionActive;
  521. clipInfoCount = layerInfos.clipInfoCount;
  522. nextClipInfoCount = layerInfos.nextClipInfoCount;
  523. interruptingClipInfoCount = layerInfos.interruptingClipInfoCount;
  524. clipInfo = layerInfos.clipInfos;
  525. nextClipInfo = layerInfos.nextClipInfos;
  526. interruptingClipInfo = isInterruptionActive ? layerInfos.interruptingClipInfos : null;
  527. shallInterpolateWeightTo1 = layerInfos.isLastFrameOfInterruption;
  528. }
  529. void GetAnimatorStateInfos (
  530. int layer,
  531. out bool isInterruptionActive,
  532. out AnimatorStateInfo stateInfo,
  533. out AnimatorStateInfo nextStateInfo,
  534. out AnimatorStateInfo interruptingStateInfo,
  535. out float interruptingClipTimeAddition) {
  536. var layerInfos = layerClipInfos[layer];
  537. isInterruptionActive = layerInfos.isInterruptionActive;
  538. stateInfo = layerInfos.stateInfo;
  539. nextStateInfo = layerInfos.nextStateInfo;
  540. interruptingStateInfo = layerInfos.interruptingStateInfo;
  541. interruptingClipTimeAddition = layerInfos.isLastFrameOfInterruption ? layerInfos.interruptingClipTimeAddition : 0;
  542. }
  543. Spine.Animation GetAnimation (AnimationClip clip) {
  544. int clipNameHashCode;
  545. if (!clipNameHashCodeTable.TryGetValue(clip, out clipNameHashCode)) {
  546. clipNameHashCode = clip.name.GetHashCode();
  547. clipNameHashCodeTable.Add(clip, clipNameHashCode);
  548. }
  549. Spine.Animation animation;
  550. animationTable.TryGetValue(clipNameHashCode, out animation);
  551. return animation;
  552. }
  553. class AnimationClipEqualityComparer : IEqualityComparer<AnimationClip> {
  554. internal static readonly IEqualityComparer<AnimationClip> Instance = new AnimationClipEqualityComparer();
  555. public bool Equals (AnimationClip x, AnimationClip y) { return x.GetInstanceID() == y.GetInstanceID(); }
  556. public int GetHashCode (AnimationClip o) { return o.GetInstanceID(); }
  557. }
  558. class IntEqualityComparer : IEqualityComparer<int> {
  559. internal static readonly IEqualityComparer<int> Instance = new IntEqualityComparer();
  560. public bool Equals (int x, int y) { return x == y; }
  561. public int GetHashCode(int o) { return o; }
  562. }
  563. }
  564. }
  565. }