SpineComponent.cs 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. using Spine.Unity;
  2. using System;
  3. using UnityEngine;
  4. using XGame.Database;
  5. using XGame.Framework.Components;
  6. using XGame.Framework.Interfaces;
  7. using XGame.Framework.Time;
  8. namespace FL.Battle.Components
  9. {
  10. public interface ISpineContext
  11. {
  12. IEntity Entity { get; }
  13. SkeletonAnimation Skeleton { get; }
  14. ITimeModule Time { get; }
  15. }
  16. public class SpineComponent : Component<ISpineContext>, IAnimator, IReset
  17. {
  18. private ITimer _actionTimer;
  19. /// <summary>
  20. /// 触发器的定时器
  21. /// </summary>
  22. private ITimer _triggerTimer;
  23. protected override void OnDisable()
  24. {
  25. ClearTimers();
  26. }
  27. protected override void OnDispose()
  28. {
  29. ClearTimers();
  30. }
  31. private void ClearTimers()
  32. {
  33. _actionTimer?.Cancel();
  34. _actionTimer = null;
  35. _triggerTimer?.Cancel();
  36. _triggerTimer = null;
  37. }
  38. public bool IsLoop(EAnimationName actionName)
  39. {
  40. return actionName switch
  41. {
  42. EAnimationName.idle or EAnimationName.idle2 or EAnimationName.dizzy or EAnimationName.freeze or EAnimationName.palsy => true,
  43. _ => false,
  44. };
  45. }
  46. public void Play(EAnimationName aniName)
  47. {
  48. Play(new AnimationPlayArgs { aniName = aniName });
  49. }
  50. public void Play(AnimationPlayArgs args)
  51. {
  52. var skeleton = Context.Skeleton;
  53. if (skeleton == null)
  54. return;
  55. //if (_actionTimer != null)
  56. //{
  57. // Log.Debug($"Spine Name:{skeleton.name} Now:{skeleton.AnimationName} Next:{args.aniName}");
  58. //}
  59. var isLoop = IsLoop(args.aniName);
  60. skeleton.loop = isLoop;
  61. skeleton.AnimationName = args.aniName.ToString();
  62. //var state = skeleton.AnimationState;
  63. //state.Tracks.Items[0].Animation.Duration
  64. ClearTimers();
  65. var duration = skeleton.GetDurationMS();
  66. if (args.onTrigger != null && args.triggerFrame > 0 && args.triggerFrame < duration)
  67. {
  68. _triggerTimer = Context.Time.AddDelayTimer(args.triggerFrame, () =>
  69. {
  70. _triggerTimer = null;
  71. args.onTrigger.SafeInvoke();
  72. //try
  73. //{
  74. // args.onTrigger?.Invoke();
  75. //}
  76. //catch (Exception ex)
  77. //{
  78. // XGame.Log.Error($"SpineComponent onTrigger Exception Entity:{Context.Entity.EntityId} Name:{Context.Entity.Attr.Name}");
  79. // XGame.Log.Exception(ex);
  80. //}
  81. });
  82. }
  83. if (!isLoop)
  84. { // 非循环动作才有回调
  85. SetSkeletonFill(Color.yellow, 0.7f);
  86. //var startTime = Time.realtimeSinceStartup;
  87. _actionTimer = Context.Time.AddDelayTimer(duration, () =>
  88. {
  89. //Log.Debug($"SpineComponent Completed. Entity:{Context.Entity.EntityId} Name:{Context.Entity.Attr.Name} Frame:{Time.frameCount} duration:{duration} start:{startTime} used:{Time.realtimeSinceStartup - startTime}");
  90. SetSkeletonFill(Color.white, 0);
  91. _actionTimer = null;
  92. skeleton.loop = IsLoop(args.fallback);
  93. skeleton.AnimationName = args.fallback.ToString();
  94. args.onCompleted.SafeInvoke();
  95. //try
  96. //{
  97. // args.onCompleted?.Invoke();
  98. //}
  99. //catch (Exception ex)
  100. //{
  101. // XGame.Log.Error($"SpineComponent onCompleted Exception Entity:{Context.Entity.EntityId} Name:{Context.Entity.Attr.Name}");
  102. // XGame.Log.Exception(ex);
  103. //}
  104. });
  105. }
  106. //state.Complete += (_) =>
  107. //{
  108. // callback?.Invoke();
  109. //};
  110. }
  111. private MaterialPropertyBlock _mpb = new MaterialPropertyBlock();
  112. /// <summary>
  113. /// 设置spine的自定义颜色
  114. /// http://zh.esotericsoftware.com/spine-unity
  115. /// </summary>
  116. /// <param name="color"></param>
  117. /// <param name="phase"></param>
  118. private void SetSkeletonFill(Color color, float phase)
  119. {
  120. var renderer = Context.Skeleton.GetComponent<MeshRenderer>();
  121. if (renderer.sharedMaterial.shader.name != "Spine/Skeleton Fill")
  122. { // 该shader可自定义颜色叠加(color overlay)的Unlit 透明着色器. 不写 z-buffer. FillColor 决定叠加颜色, FillPhase 决定叠加强度.
  123. return;
  124. }
  125. //Log.Debug($"SetSkeletonFill color:{color} phase:{phase} Frame:{Time.frameCount}");
  126. _mpb.SetColor("_FillColor", color); // "_FillColor" is a named property on the used shader.
  127. _mpb.SetFloat("_FillPhase", phase); // "_FillPhase" is another named property on the used shader.
  128. renderer.SetPropertyBlock(_mpb);
  129. }
  130. void IReset.Reset()
  131. {
  132. ClearTimers();
  133. }
  134. //private void OnCompleted(TrackEntry trackEntry)
  135. //{
  136. //}
  137. }
  138. }