using Spine.Unity; using System; using UnityEngine; using XGame.Database; using XGame.Framework.Components; using XGame.Framework.Interfaces; using XGame.Framework.Time; namespace FL.Battle.Components { public interface ISpineContext { IEntity Entity { get; } SkeletonAnimation Skeleton { get; } ITimeModule Time { get; } } public class SpineComponent : Component, IAnimator, IReset { private ITimer _actionTimer; /// /// 触发器的定时器 /// private ITimer _triggerTimer; protected override void OnDisable() { ClearTimers(); } protected override void OnDispose() { ClearTimers(); } private void ClearTimers() { _actionTimer?.Cancel(); _actionTimer = null; _triggerTimer?.Cancel(); _triggerTimer = null; } public bool IsLoop(EAnimationName actionName) { return actionName switch { EAnimationName.idle or EAnimationName.idle2 or EAnimationName.dizzy or EAnimationName.freeze or EAnimationName.palsy => true, _ => false, }; } public void Play(EAnimationName aniName) { Play(new AnimationPlayArgs { aniName = aniName }); } public void Play(AnimationPlayArgs args) { var skeleton = Context.Skeleton; if (skeleton == null) return; //if (_actionTimer != null) //{ // Log.Debug($"Spine Name:{skeleton.name} Now:{skeleton.AnimationName} Next:{args.aniName}"); //} var isLoop = IsLoop(args.aniName); skeleton.loop = isLoop; skeleton.AnimationName = args.aniName.ToString(); //var state = skeleton.AnimationState; //state.Tracks.Items[0].Animation.Duration ClearTimers(); var duration = skeleton.GetDurationMS(); if (args.onTrigger != null && args.triggerFrame > 0 && args.triggerFrame < duration) { _triggerTimer = Context.Time.AddDelayTimer(args.triggerFrame, () => { _triggerTimer = null; args.onTrigger.SafeInvoke(); //try //{ // args.onTrigger?.Invoke(); //} //catch (Exception ex) //{ // XGame.Log.Error($"SpineComponent onTrigger Exception Entity:{Context.Entity.EntityId} Name:{Context.Entity.Attr.Name}"); // XGame.Log.Exception(ex); //} }); } if (!isLoop) { // 非循环动作才有回调 SetSkeletonFill(Color.yellow, 0.7f); //var startTime = Time.realtimeSinceStartup; _actionTimer = Context.Time.AddDelayTimer(duration, () => { //Log.Debug($"SpineComponent Completed. Entity:{Context.Entity.EntityId} Name:{Context.Entity.Attr.Name} Frame:{Time.frameCount} duration:{duration} start:{startTime} used:{Time.realtimeSinceStartup - startTime}"); SetSkeletonFill(Color.white, 0); _actionTimer = null; skeleton.loop = IsLoop(args.fallback); skeleton.AnimationName = args.fallback.ToString(); args.onCompleted.SafeInvoke(); //try //{ // args.onCompleted?.Invoke(); //} //catch (Exception ex) //{ // XGame.Log.Error($"SpineComponent onCompleted Exception Entity:{Context.Entity.EntityId} Name:{Context.Entity.Attr.Name}"); // XGame.Log.Exception(ex); //} }); } //state.Complete += (_) => //{ // callback?.Invoke(); //}; } private MaterialPropertyBlock _mpb = new MaterialPropertyBlock(); /// /// 设置spine的自定义颜色 /// http://zh.esotericsoftware.com/spine-unity /// /// /// private void SetSkeletonFill(Color color, float phase) { var renderer = Context.Skeleton.GetComponent(); if (renderer.sharedMaterial.shader.name != "Spine/Skeleton Fill") { // 该shader可自定义颜色叠加(color overlay)的Unlit 透明着色器. 不写 z-buffer. FillColor 决定叠加颜色, FillPhase 决定叠加强度. return; } //Log.Debug($"SetSkeletonFill color:{color} phase:{phase} Frame:{Time.frameCount}"); _mpb.SetColor("_FillColor", color); // "_FillColor" is a named property on the used shader. _mpb.SetFloat("_FillPhase", phase); // "_FillPhase" is another named property on the used shader. renderer.SetPropertyBlock(_mpb); } void IReset.Reset() { ClearTimers(); } //private void OnCompleted(TrackEntry trackEntry) //{ //} } }