SkeletonMecanim.cs 27 KB

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