using System; using System.Collections.Generic; using UnityEngine; using XGame.Framework; using XGame.Framework.Asyncs; using XGame.Framework.Components; using XGame.Framework.Interfaces; using XGame.Framework.Time; namespace FL.Battle.Vfxs { /// /// 特效播放组件 /// TODO 世界跟随类型需要将特效转到地图播放,以防止角色死亡时特效播放被打断 /// public class VfxComponent : Component { class VfxPlayingDto : IReference { public IAsync loadAsync; public ITimer timer; public GameObject vfxGo; void IReference.Clear() { loadAsync = null; timer = null; vfxGo = null; } } public override int Order => 0; private Dictionary _playingVfxMap = new Dictionary(); protected override void OnEnable(object intent) { if (Context.WorldTr != null) { EventSingle.Instance.AddListener(EventDefine.VfxComponentWorldVfx, OnWorldVfx); } } protected override void OnDisable() { if (Context.WorldTr != null) { EventSingle.Instance.RemoveListener(EventDefine.VfxComponentWorldVfx, OnWorldVfx); } } protected override void OnDispose() { foreach (var item in _playingVfxMap) { RecycleDto(item.Key, false); RecycleArgs(item.Value); } _playingVfxMap.Clear(); } public void Play(VfxArgs args) { Assert.IsTrue(args != null, "特效播放参数不能为空."); Assert.IsFalse(string.IsNullOrEmpty(args.vfxName), "特效名字不能为空."); Assert.IsTrue(args.duration > 0, "特效播放时长必须大于零."); if (args.followType == EVfxFollowType.World && Context.WorldTr == null) { EventSingle.Instance.Notify(EventDefine.VfxComponentWorldVfx, args); return; } var dto = AcquireDto(args); void DelayPlay() { var loadAsync = Context.Asset.LoadAsync(args.vfxName); dto.loadAsync = loadAsync; loadAsync.On(_ => { dto.loadAsync = null; var vfxGo = loadAsync.GetResult(); if (vfxGo == null) { RecycleDto(dto); args.onFinish.SafeInvoke(); RecycleArgs(args); return; } dto.vfxGo = vfxGo; SetParent(vfxGo.transform, args.followType); vfxGo.SetActive(true); args.action?.Start(vfxGo.transform); dto.timer = Context.Time.AddDelayTimer(args.duration, () => { dto.timer = null; dto.vfxGo = null; //回收GameObject Context.Asset.Recycle(vfxGo); //回收dto RecycleDto(dto); args.onFinish.SafeInvoke(); var next = args.next; if (next != null) { args.next = null; //播放下一个 Play(next); } RecycleArgs(args); }); }); } if (args.delay > 0) { dto.timer = Context.Time.AddDelayTimer(args.delay, () => { dto.timer = null; DelayPlay(); }); } else { DelayPlay(); } } private void SetParent(Transform vfxTr, EVfxFollowType followType) { Transform parent; switch (followType) { case EVfxFollowType.World: parent = Context.WorldTr; break; case EVfxFollowType.CastPoint: parent = Context.CastTr != null ? Context.CastTr : Context.CenterTr; break; default: parent = Context.CenterTr; break; } vfxTr.SetParent(parent, false); if (followType != EVfxFollowType.World) { vfxTr.localPosition = Vector3.zero; } //TODO 技能特效需要缩小尺寸 vfxTr.localScale = Vector3.one * 0.7f; } //public VfxArgs AcquireArgs() //{ // return ObjectPool.Acquire(); //} public void RecycleArgs(VfxArgs args) { if (args == null) return; if (args.next != null) { RecycleArgs(args.next); } if (args.action != null) { ObjectPool.Recycle(args.action); args.action = null; } ObjectPool.Recycle(args); } private VfxPlayingDto AcquireDto(VfxArgs args) { var dto = ObjectPool.Acquire(); _playingVfxMap.Add(dto, args); return dto; } private void RecycleDto(VfxPlayingDto dto, bool removeMap = true) { dto.loadAsync?.RemoveAll(); dto.timer?.Cancel(); if (dto.vfxGo != null) { Context.Asset.Recycle(dto.vfxGo); } if (removeMap) { _playingVfxMap.Remove(dto); } ObjectPool.Recycle(dto); } #region 事件监听 private void OnWorldVfx(int eventId, object args) { var vfxArgs = args as VfxArgs; Play(vfxArgs); } #endregion } }