lcy
8 天以前 485db99b5c51a971c173664cf6e808dc8424b0a0
Main/Component/UI/Effect/EffectPlayer.cs
@@ -5,25 +5,54 @@
using UnityEngine;
using Spine;
using UnityEngine.UI;
using Cysharp.Threading.Tasks;
// 特效播放器,预制体对象池管理,unity的预制体池使用有问题(1个特效1个池但复用又低)后续修改
// unity特效《预制体》是特效师在制作的时候生成的不同预制体,unity特效必须挂载设置播放时长脚本 EffectTime
// spine特效是特效师制作的动画文件,可共同复用一个《预制体》
public class EffectPlayer : MonoBehaviour
{
    public int effectId;
    [SerializeField]
    private int m_EffectID;
    public int effectId
    {
        get
        {
            return m_EffectID;
        }
        set
        {
            if (value != m_EffectID)
            {
                Stop();
                isInit = false;
                m_EffectID = value;
            }
        }
    }
    public EffectConfig effectConfig;
    public Action<EffectPlayer> onDestroy;
    public Action onComplete;
    public float speedRate = 1f;
    [Header("是否循环播放spine特效")]
    public bool isPlaySpineLoop = false;
    [Header("是否在显示时播放")]
    public bool isPlayOnEnable = false;
    [Header("是否立即播放spine特效")]
    public bool isPlaySpineImmediately = false;
    [Header("是否循环播放spine特效")]
    public bool isPlaySpineLoop = false;
    [Header("延迟播放(毫秒)")]
    public int playDelayTime = 0;
    public int playSpineAnimIndex = -1; //播放spine特效动画索引,代码控制
    [Header("播放完毕立即回收")]
    public bool isReleaseImmediately = false;  //界面特效一般不需要自我销毁,跟随界面或者父对象销毁就行
    public Action<EffectPlayer> onDestroy;
    [HideInInspector] public Canvas canvas = null;
@@ -32,6 +61,11 @@
    protected EffectPenetrationBlocker blocker = null;
    protected bool isInit = false;
    public bool isPlaying
    {
        get;
        protected set;
    }
    protected List<ParticleSystem> particleList = new List<ParticleSystem>();
@@ -42,150 +76,258 @@
    protected SkeletonGraphic spineComp;
    protected Spine.AnimationState spineAnimationState;
    protected void OnEnable()
    public GameObjectPoolManager.GameObjectPool pool;
    public Action onComplete;
    protected virtual void OnEnable()
    {
        if (isPlayOnEnable)
        {
            Play();
            Play(false);
        }
        else if (spineComp != null)
        {
            if (!isPlaying)
            {
                //隐藏,会有静态显示问题
                spineComp.enabled = false;
            }
        }
    }
    protected bool InitCompnent()
    protected void InitComponent(bool showLog = true)
    {
        if (effectId <= 0)
        {
            effectConfig = null;
            // 根据逻辑需求,动态设置特效的情况
            // Debug.LogError("EffectPlayer effectId is not set");
            // #if UNITY_EDITOR
            //             UnityEditor.Selection.activeGameObject = gameObject;
            //             UnityEditor.EditorGUIUtility.PingObject(gameObject);
            // #endif
            return false;
#if UNITY_EDITOR
            if (showLog)
            {
                Debug.LogError("EffectPlayer effectId is not set");
                UnityEditor.Selection.activeGameObject = gameObject;
                UnityEditor.EditorGUIUtility.PingObject(gameObject);
            }
#endif
            return;
        }
        effectConfig = EffectConfig.Get(effectId);
        if (null == effectConfig)
        {
            Debug.LogError("could not find effect config, effect id is " + effectId);
#if UNITY_EDITOR
            Debug.LogError("could not find effect config, effect id is " + effectId);
            UnityEditor.Selection.activeGameObject = gameObject;
            UnityEditor.EditorGUIUtility.PingObject(gameObject);
#endif
            return false;
        }
        particleList.Clear();
        animatorList.Clear();
        rendererList.Clear();
        spineComp = null;
        //spine是加载文件,需要提前挂载脚本,unity特效是已经做好的预制体待加载后收集组件
        if (effectConfig.isSpine != 0)
        {
            spineComp = gameObject.AddMissingComponent<SkeletonGraphic>();
        }
        // else
        // {
        //     //  收集组件
        //     particleList.AddRange(gameObject.GetComponentsInChildren<ParticleSystem>(true));
        //     animatorList.AddRange(gameObject.GetComponentsInChildren<Animator>(true));
        // }
        // rendererList.AddRange(gameObject.GetComponentsInChildren<Renderer>(true));
        return true;
    }
    public void Stop()
    {
        if (null != effectTarget)
        {
            DestroyImmediate(effectTarget);
            effectTarget = null;
        }
        isInit = false;
        particleList.Clear();
        animatorList.Clear();
        rendererList.Clear();
        spineComp = null;
    }
    public void Play()
    {
        ReStart();
    }
    protected void ReStart()
    {
        if (!isInit)
        {
            if (InitCompnent())
            {
                isInit = true;
            }
            else
            {
                //根据逻辑需求,动态设置特效的情况
                return;
            }
        }
        if (EffectMgr.Instance.IsNotShowBySetting(effectId))
        {
            return;
        }
        Clear();
        return;
    }
    protected virtual void Clear()
    {
        isInit = false;
        particleList.Clear();
        animatorList.Clear();
        rendererList.Clear();
        if (spineComp != null)
        {
            spineComp.skeletonDataAsset = null;
            // spineComp.Initialize(false);
            spineComp.Clear();
        }
        spineComp = null;
    }
    public virtual void Stop()
    {
        if (null != effectTarget)
        {
            DestroyImmediate(effectTarget);
            // 如果使用了特效预制体池,则归还到池中
            if (pool != null)
            {
                pool.Release(effectTarget);
            }
            effectTarget = null;
        }
        if (spineComp != null)
        {
            spineComp.enabled = false;
        }
        isPlaying = false;
        playSpineAnimIndex = -1;
    }
    protected void Release()
    {
        Stop();
        Clear();
    }
    public virtual void Play(bool showLog = true, bool closePMA = false)
    {
        isPlaying = true;
        if (!isInit)
        {
            InitComponent(showLog);
            //effeid 为0也初始化成功,避免重复处理,在变更effectid时会重新初始化
            isInit = true;
        }
        else
        {
            //避免重复创建
            if (!this.gameObject.activeSelf)
            {
                this.gameObject.SetActive(true);
            }
            //防范effeid 为0
            if (effectConfig != null && effectConfig.isSpine != 0)
            {
                PlaySpineEffect(closePMA);
            }
            return;
        }
        if (effectConfig == null)
            return;
        if (EffectMgr.IsNotShowBySetting(effectId))
            {
                return;
            }
        if (null != effectTarget)
        {
            if (pool != null)
                pool.Release(effectTarget);
            effectTarget = null;
        }
        //   YYL TODO
        //   在这里考虑用池的话可能走配置好一点 原本的是无论如何都走池 但是实际上有些特效并不需要
        if (!this.gameObject.activeSelf)
        {
            this.gameObject.SetActive(true);
        }
        // 加载spine特效资源
        if (effectConfig.isSpine != 0)
        {
            spineComp.skeletonDataAsset = ResManager.Instance.LoadAsset<SkeletonDataAsset>("UIEffect/" + effectConfig.packageName, effectConfig.fxName + "_SkeletonData");
            spineComp.raycastTarget = false;
            spineComp.Initialize(true);
            spineAnimationState = spineComp.AnimationState;
            spineAnimationState.Complete -= OnSpineAnimationComplete;
            spineAnimationState.Complete += OnSpineAnimationComplete;
            if (isPlaySpineImmediately)
            {
                //UI特效常用方式
                spineComp.enabled = true;
                // 播放第一个动画(作为默认动画)
                var skeletonData = spineComp.Skeleton.Data;
                if (skeletonData.Animations.Count > 0)
                {
                    string defaultAnimationName = skeletonData.Animations.Items[0].Name;
                    spineComp.AnimationState.SetAnimation(0, defaultAnimationName, isPlaySpineLoop);
                }
                else
                {
                    Debug.LogError("Spine 数据中没有找到任何动画!" + effectConfig.id);
                }
            }
            else
            {
                spineComp.enabled = false;
            }
            PlaySpineEffect(closePMA);
        }
        else
        {
            PlayerEffect();
        }
        SoundPlayer.Instance.PlayUIAudio(effectConfig.audio);
            return;
    }
    // protected virtual void PlaySpineEffect()
    // {
    //     spineComp = gameObject.GetComponentInChildren<SkeletonGraphic>(true);
    //     spineComp.raycastTarget = false;
    //     spineComp.Initialize(true);
    //     spineAnimationState = spineComp.AnimationState;
    //     spineAnimationState.Complete -= OnSpineAnimationComplete;
    //     spineAnimationState.Complete += OnSpineAnimationComplete;
    //     // 外层控制具体播放哪个动画
    //     spineComp.enabled = true;
    //     return;
    // }
    protected void PlaySpineEffect(bool closePMA = false)
    {
        // 从特效预制体池获取特效
        if (spineComp == null)
        {
            spineComp = gameObject.AddMissingComponent<SkeletonGraphic>();
        }
        //加载unity特效
        if (spineComp.skeletonDataAsset == null || spineAnimationState == null)
        {
            //LoadAsset 已经有缓存SkeletonDataAsset
            spineComp.skeletonDataAsset = ResManager.Instance.LoadAsset<SkeletonDataAsset>("UIEffect/" + effectConfig.packageName, effectConfig.fxName);
            //为true时会有部分特效不显示 如主界面装备特效;改成伽马后不会出现BUG故注释代码
            // spineComp.MeshGenerator.settings.pmaVertexColors = !closePMA;
            spineComp.raycastTarget = false;
            spineComp.Initialize(true);
            spineComp.timeScale = speedRate;
            // 检查动画是否有相加模式
            // bool hasAdditiveBlend = CheckForAdditiveBlend(spineComp.Skeleton);
            // if (hasAdditiveBlend)
            // {
            //    spineComp.material = ResManager.Instance.LoadAsset<Material>("UIEffect/" + effectConfig.packageName, effectConfig.fxName.Split('_')[0] + "_Material-Additive");
            // }
            // else
            // {
            //     spineComp.material = null;
            // }
            spineComp.material = ResManager.Instance.LoadAsset<Material>("Materials", "SkeletonGraphicDefault-Straight");
            spineAnimationState = spineComp.AnimationState;
            spineAnimationState.Data.DefaultMix = 0f;
            spineAnimationState.Complete -= OnSpineAnimationComplete;
            spineAnimationState.Complete += OnSpineAnimationComplete;
        }
        spineComp.enabled = true;
        PlayerTheSpineAnim();
    }
        // 播放指定动画
    void PlayerTheSpineAnim()
    {
        spineComp.enabled = true;
        var skeletonData = spineComp.Skeleton.Data;
        if (skeletonData.Animations.Count > 0)
        {
            //按配置或者默认第一个
            int defaultAnimIndex = Math.Max(0, effectConfig.animIndex.Length == 0 ? 0 : effectConfig.animIndex[0]);
            if (playSpineAnimIndex >= skeletonData.Animations.Count)
            {
                playSpineAnimIndex = -1;
                Debug.LogError("特效:" + effectConfig.id + " 索引超出播放默认动画。 error:" + playSpineAnimIndex);
            }
            string defaultAnimationName = skeletonData.Animations.Items[playSpineAnimIndex == -1 ? defaultAnimIndex : playSpineAnimIndex].Name;
            spineAnimationState.SetAnimation(0, defaultAnimationName, isPlaySpineLoop);
        }
        else
        {
            Debug.LogError("Spine 数据中没有找到任何动画!" + effectConfig.id);
        }
    }
    private bool CheckForAdditiveBlend(Spine.Skeleton skeleton)
    {
        // 遍历所有插槽,检查是否有相加模式
        foreach (var slot in skeleton.Slots)
        {
            if (slot.Data.BlendMode == Spine.BlendMode.Additive)
            {
                return true;
            }
        }
        return false;
    }
    //unity特效
    protected virtual void PlayerEffect()
    {
        var effectPrefab = ResManager.Instance.LoadAsset<GameObject>("UIEffect/" + effectConfig.packageName, effectConfig.fxName);
        if (effectPrefab == null)
        {
@@ -193,20 +335,37 @@
            return;
        }
        // 实例化特效
        effectTarget = Instantiate(effectPrefab, transform);
        if (effectConfig.isSpine == 0)
        {
            //unity特效 判断预制体是否挂载EffectTime
            var timeObj = effectPrefab.GetComponent<EffectTime>();
            if (null == timeObj)
            {
                Debug.LogError($"{effectPrefab.name}预制体没有挂载EffectTime");
                return;
            }
        }
        // 从特效预制体池获取特效
        pool = GameObjectPoolManager.Instance.RequestPool(effectPrefab);
        effectTarget = pool.Request();
        // 设置父节点和位置
        effectTarget.transform.SetParent(transform);
        effectTarget.transform.localPosition = Vector3.zero;
        effectTarget.transform.localScale = Vector3.one;
        effectTarget.transform.localRotation = Quaternion.identity;
        effectTarget.name = $"Effect_{effectConfig.fxName}";
        //挂载组件后 开始收集
        particleList.AddRange(gameObject.GetComponentsInChildren<ParticleSystem>(true));
        animatorList.AddRange(gameObject.GetComponentsInChildren<Animator>(true));
        rendererList.AddRange(gameObject.GetComponentsInChildren<Renderer>(true));
        OnUnityAnimationComplete();
        //  思考一下在没有挂在节点的时候
        if (null == canvas)
            canvas = GetComponentInParent<Canvas>();
        // 添加特效穿透阻挡器
        blocker = effectTarget.AddMissingComponent<EffectPenetrationBlocker>();
@@ -216,14 +375,20 @@
        {
            blocker.SetParentCanvas(canvas);
        }
        // 自动销毁
        if (effectConfig.autoDestroy != 0)
        {
            Destroy(effectTarget, effectConfig.destroyDelay);
        }
    }
    public async UniTask PlayAsync(bool showLog = true, bool closePMA = false)
    {
        await UniTask.Delay(playDelayTime);
        try
        {
            Play(showLog, closePMA);
        }
        catch (Exception e)
        {
            Debug.LogError(e);
        }
    }
    protected void OnDestroy()
    {
@@ -232,33 +397,58 @@
            onDestroy.Invoke(this);
            onDestroy = null;
        }
        // 停止特效并归还到池中
        Release();
        if (spineAnimationState != null)
        {
            spineAnimationState.Complete -= OnSpineAnimationComplete;
        }
    }
    //单次播放完毕就会触发,即使是循环
    protected void OnSpineAnimationComplete(Spine.TrackEntry trackEntry)
    protected virtual void OnSpineAnimationComplete(Spine.TrackEntry trackEntry)
    {
        if (!isPlaySpineLoop)
        {
            if (onComplete == null)
            {
                if (effectConfig.isSpine != 0)
                {
                    spineComp.enabled = false;
                }
            }
            spineComp.enabled = false;
            isPlaying = false;
            onComplete?.Invoke();
            // TODO 会报错暂时注释
            // if (isReleaseImmediately)
            // {
            //     Release();
            // }
        }
    }
    private void OnUnityAnimationComplete()
    {
        var timeObj = effectTarget.GetComponent<EffectTime>();
        if (!timeObj.isLoop)
        {
            this.SetWait(timeObj.playTime);
            this.DoWaitRestart();
            this.OnWaitCompelete(OnEffectComplete);
        }
    }
    private void OnEffectComplete(Component comp)
    {
        this.DoWaitStop();
        if (isReleaseImmediately)
            Release();
    }
    //  创建后的特效会自动隐藏 需要手动调用Play才能播放
    public static EffectPlayer Create(int effectId, Transform parent, bool createNewChild = false)
    {
        // 直接创建特效播放器,不使用对象池
        EffectPlayer effectPlayer = null;
        if (createNewChild)
@@ -270,11 +460,23 @@
        else
        {
            effectPlayer = parent.AddMissingComponent<EffectPlayer>();
            effectPlayer.effectId = effectId;
        }
        effectPlayer.effectId = effectId;
        effectPlayer.SetActive(true);
        return effectPlayer;
    }
    /// <summary>
    /// 设置游戏对象激活状态
    // /// </summary>
    // public void SetActive(bool active)
    // {
    //     if (gameObject != null)
    //     {
    //         gameObject.SetActive(active);
    //     }
    // }
    public void Pause()
    {
@@ -372,4 +574,6 @@
            return;
        }
    }
}