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.Map;
using XGame.Framework.Time;
namespace FL.Battle.Components
{
///
/// 特效播放上下文
///
public interface IVfxContext
{
IMapAssetModule Asset { get; }
ITimeModule Time { get; }
///
/// 世界节点,不是则返回空
/// 只有世界节点才能播放VfxFollowType.World的特效
/// 每个地图只能有一个世界节点
///
Transform WorldTr { get; }
Transform CenterTr { get; }
///
/// 施法点,没有则返回空
///
Transform CastTr { get; }
}
///
/// 特效播放组件
/// 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;
}
}
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()
{
Stop();
}
public void Play(VfxArgs args)
{
Assert.IsTrue(args != null, "特效播放参数不能为空.");
Assert.IsFalse(string.IsNullOrEmpty(args.vfxName), "特效名字不能为空.");
Assert.IsTrue(args.duration > 0, $"特效播放时长必须大于零. Name:{args.vfxName}");
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.Result;
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();
}
}
public void Stop()
{
foreach (var item in _playingVfxMap)
{
RecycleDto(item.Key, false);
RecycleArgs(item.Value);
}
_playingVfxMap.Clear();
}
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
}
}