using System;
|
using System.Collections;
|
using System.Collections.Generic;
|
using Spine.Unity;
|
using UnityEngine;
|
using Spine;
|
using UnityEngine.UI;
|
|
// 特效播放器,对象池管理:unity特效和spine特效都是做好的预制体
|
// unity特效预制体是特效师在制作的时候生成的,unity特效必须挂载设置播放时长脚本 EffectTime
|
// spine特效是特效师制作的动画文件可直接加载用,制作成预制体可增加BoneFollower之类的进行逻辑处理
|
// 非UI特效使用,UI特效UIEffectPlayer继承EffectPlayer,后续如果逻辑冲突大则不用继承
|
public class EffectPlayer : MonoBehaviour
|
{
|
[SerializeField]
|
private int m_EffectID;
|
public int effectId
|
{
|
get
|
{
|
return m_EffectID;
|
}
|
set
|
{
|
if (value != m_EffectID)
|
{
|
isInit = false;
|
m_EffectID = value;
|
}
|
}
|
}
|
|
|
|
public EffectConfig effectConfig;
|
|
public float speedRate = 1f;
|
|
|
|
|
[Header("播放完毕立即回收")]
|
public bool isReleaseImmediately = true; //界面特效一般不需要自我销毁,跟随界面或者父对象销毁就行
|
|
public Action<EffectPlayer> onDestroy;
|
|
[HideInInspector] public Canvas canvas = null;
|
|
[HideInInspector] public GameObject effectTarget = null;
|
|
protected EffectPenetrationBlocker blocker = null;
|
|
protected bool isInit = false;
|
|
protected List<ParticleSystem> particleList = new List<ParticleSystem>();
|
|
protected List<Animator> animatorList = new List<Animator>();
|
|
protected List<Renderer> rendererList = new List<Renderer>();
|
|
protected SkeletonGraphic spineComp;
|
protected Spine.AnimationState spineAnimationState;
|
|
public GameObjectPoolManager.GameObjectPool pool;
|
|
public Action onComplete;
|
|
protected virtual void OnEnable()
|
{
|
if (spineComp != null)
|
{
|
//隐藏,会有静态显示问题
|
spineComp.enabled = false;
|
}
|
}
|
|
protected void InitComponent(bool showLog = true)
|
{
|
if (effectId <= 0)
|
{
|
effectConfig = null;
|
#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)
|
{
|
#if UNITY_EDITOR
|
Debug.LogError("could not find effect config, effect id is " + effectId);
|
UnityEditor.Selection.activeGameObject = gameObject;
|
UnityEditor.EditorGUIUtility.PingObject(gameObject);
|
#endif
|
return;
|
}
|
|
|
Clear();
|
return;
|
}
|
|
protected virtual void Clear()
|
{
|
particleList.Clear();
|
animatorList.Clear();
|
rendererList.Clear();
|
spineComp = null;
|
pool = null;
|
}
|
|
public virtual void Stop()
|
{
|
if (null != effectTarget)
|
{
|
// 如果使用了特效预制体池,则归还到池中
|
if (pool != null)
|
{
|
pool.Release(effectTarget);
|
}
|
effectTarget = null;
|
}
|
|
isInit = false;
|
|
Clear();
|
onComplete?.Invoke();
|
}
|
|
public virtual void Play(bool showLog = true)
|
{
|
if (!isInit)
|
{
|
InitComponent(showLog);
|
isInit = true;
|
}
|
else
|
{
|
//避免重复创建
|
if (!this.gameObject.activeSelf)
|
{
|
this.gameObject.SetActive(true);
|
}
|
return;
|
}
|
|
if (EffectMgr.IsNotShowBySetting(effectId))
|
{
|
return;
|
}
|
|
if (null != effectTarget)
|
{
|
if (pool != null)
|
pool.Release(effectTarget);
|
effectTarget = null;
|
}
|
|
if (!this.gameObject.activeSelf)
|
{
|
this.gameObject.SetActive(true);
|
}
|
|
PlayerEffect(true);
|
SoundPlayer.Instance.PlayUIAudio(effectConfig.audio);
|
|
}
|
|
|
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 virtual void PlayerEffect(bool playSpine)
|
{
|
var effectPrefab = ResManager.Instance.LoadAsset<GameObject>("UIEffect/" + effectConfig.packageName, effectConfig.fxName);
|
if (effectPrefab == null)
|
{
|
Debug.LogError($"加载UI特效失败: {effectConfig.packageName}");
|
return;
|
}
|
|
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}";
|
|
if (effectConfig.isSpine != 0 && playSpine)
|
{
|
//预制体
|
PlaySpineEffect();
|
}
|
else
|
{
|
//挂载组件后 开始收集
|
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>();
|
|
// 如果没有canvas的话 正常是因为不在BattleWin下面的节点 意思就是当前没有显示 等到切回战斗的时候再通过BattleField.UpdateCanvas来更新
|
if (canvas != null)
|
{
|
blocker.SetParentCanvas(canvas);
|
}
|
}
|
|
|
protected void OnDestroy()
|
{
|
if (onDestroy != null)
|
{
|
onDestroy.Invoke(this);
|
onDestroy = null;
|
}
|
// 停止特效并归还到池中
|
Stop();
|
|
if (spineAnimationState != null)
|
{
|
spineAnimationState.Complete -= OnSpineAnimationComplete;
|
}
|
|
|
}
|
|
//单次播放完毕就会触发,即使是循环
|
protected virtual void OnSpineAnimationComplete(Spine.TrackEntry trackEntry)
|
{
|
if (isReleaseImmediately)
|
{
|
spineComp.enabled = false;
|
Stop();
|
}
|
}
|
|
|
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)
|
Stop();
|
}
|
|
|
// 创建后的特效会自动隐藏 需要手动调用Play才能播放
|
public static EffectPlayer Create(int effectId, Transform parent, bool createNewChild = false)
|
{
|
// 直接创建特效播放器,不使用对象池
|
EffectPlayer effectPlayer = null;
|
|
if (createNewChild)
|
{
|
GameObject newGo = new GameObject("EffectPlayer_" + effectId);
|
newGo.transform.SetParent(parent, false);
|
effectPlayer = newGo.AddComponent<EffectPlayer>();
|
}
|
else
|
{
|
effectPlayer = parent.AddMissingComponent<EffectPlayer>();
|
}
|
|
effectPlayer.effectId = effectId;
|
effectPlayer.SetActive(true);
|
return effectPlayer;
|
}
|
|
/// <summary>
|
/// 设置游戏对象激活状态
|
// /// </summary>
|
// public void SetActive(bool active)
|
// {
|
// if (gameObject != null)
|
// {
|
// gameObject.SetActive(active);
|
// }
|
// }
|
|
public void Pause()
|
{
|
if (effectTarget == null) return;
|
|
// Spine动画
|
// var spineGraphics = effectTarget.GetComponentsInChildren<SkeletonGraphic>(true);
|
// foreach (var sg in spineGraphics)
|
if (spineComp != null)
|
{
|
spineComp.timeScale = 0f;
|
}
|
|
// Animator动画
|
foreach (var animator in animatorList)
|
{
|
animator.speed = 0f;
|
}
|
|
// 粒子特效
|
foreach (var ps in particleList)
|
{
|
ps.Pause();
|
}
|
}
|
|
public void Resume()
|
{
|
if (effectTarget == null) return;
|
|
if (spineComp != null)
|
{
|
spineComp.timeScale = speedRate;
|
}
|
|
// Animator动画
|
foreach (var animator in animatorList)
|
{
|
animator.speed = speedRate;
|
}
|
|
// 粒子特效
|
foreach (var ps in particleList)
|
{
|
ps.Play();
|
}
|
}
|
|
public bool IsFinish()
|
{
|
if (effectTarget == null) return true;
|
|
// Spine动画
|
if (!spineComp.AnimationState.GetCurrent(0).IsComplete)
|
{
|
return false;
|
}
|
|
// Animator动画
|
foreach (var animator in animatorList)
|
{
|
AnimatorStateInfo stateInfo = animator.GetCurrentAnimatorStateInfo(0);
|
|
// 循环动画不考虑结束的问题
|
if (!stateInfo.loop && stateInfo.normalizedTime < 1f)
|
{
|
return false;
|
}
|
}
|
|
// 粒子特效
|
foreach (var ps in particleList)
|
{
|
if (ps.IsAlive())
|
{
|
return false;
|
}
|
}
|
|
return true;
|
}
|
|
/// <summary>
|
/// 设置遮罩(支持RectMask2D、Mask、SmoothMask等)
|
/// </summary>
|
public void SetMask(RectTransform maskArea = null)
|
{
|
if (effectTarget == null || blocker == null)
|
return;
|
|
// 优先使用传入的maskArea
|
if (maskArea != null)
|
{
|
blocker.PerformMask(maskArea);
|
return;
|
}
|
}
|
|
|
}
|