SkeletonMecanim.cs 28 KB

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