特效会有个EffectManager用于统一管理所有特效,技能特效只是各类特效中的一种
EffectManager需要提供特效的创建,返回被封装为EffectHandle
每类特效都有各种不同的配置参数,这些配置参数会传递给EffectManager用于生成EffectHandler
为支持实现更复杂的特效表现,EffectManager应当提供接口支持根据需要动态修改配置参数
技能的特效分为三类:随身特效、表现特效、场景特效
随身特效是指跟随角色的特效,例如角色的拖尾、飘带,这些特效通常位于角色身上的某个挂载点
表现特效,指为丰富画面或技能表现而出现的特效,例如技能释放瞬间的闪光特效
场景特效,指创建出来较大的特效,创建完成后会固定在场景内基本不动
注意,这里的特效不动只是相对于挂载目标而言的,其自身可以有不同的运动形式,当挂载点运动,其也会跟着运动
【特效参数】
基础参数
- 特效名字
- 特效ID
- 特效Prefab
- 特效路径
- 特效类型
位置参数
- 挂载目标
- 随身特效一般为角色自身,通常特效一直跟随角色,会在角色身上的某个点做挂载
- 表现特效一般为角色周围的某个空间的位置
- 场景特效一般为角色周围的场景中的某个位置,需要通过技能指向动态计算得到
- 挂载点
- 初始位置、偏移位置、初始朝向、初始偏移(在工程实践中,位置通常都不是单个的精确值,通常都是基础+偏移)
存活参数
- 存活方式
- 随身特效通常跟随技能,尤其是状态或者限时类技能,也即通常该类特效跟随技能生命周期,即进入技能时,该特效就出现,退出技能时就消失,但不是完全绝对的,可能消失有个FadeOut
- 表现特效通常是固定时间,时间通常不长
- 场景特效可以是跟随技能的、固定时间的,还有可能是固定范围的,即距离角色一定范围内有效,超出范围即失效
- 渐出时间
- 有效距离
其他各类特效相关参数
【代码实现】
public class EffectConfigData : ScriptableObject, IData{[FoldoutGroup("基础参数")][LabelText("特效名字")]public string effectName;[FoldoutGroup("基础参数")][LabelText("特效Id"), ReadOnly]public int effectId;//todo:根据名字生成[FoldoutGroup("基础参数")][LabelText("特效Prefab")]public GameObject effectGo;[FoldoutGroup("基础参数")][LabelText("特效路径"), ReadOnly]public string effectPath;[FoldoutGroup("基础参数")][LabelText("特效类型")]public SkillEffectType type;[FoldoutGroup("初始位置参数")][LabelText("挂载目标")]public AttachTarget target;[FoldoutGroup("初始位置参数")][LabelText("挂载点")]public int slotId;[FoldoutGroup("初始位置参数")][LabelText("位置偏移")]public Vector3 offsetPos;[FoldoutGroup("初始位置参数")][LabelText("初始朝向")]public Vector3 foward;[FoldoutGroup("初始位置参数")][LabelText("朝向偏移")]public Vector3 offsetRot;[FoldoutGroup("存活参数")][LabelText("朝向偏移")]public LifeTimeType lifeTimeType;[FoldoutGroup("存活参数")][LabelText("存活时间")]public float liftTime;[FoldoutGroup("存活参数")][LabelText("渐出时间")]public float fadeOutTime;[FoldoutGroup("存活参数")][LabelText("存活距离")]public float liftDistance;public void Dispose(){}//其他特效相关参数}public enum AttachTarget{Self,Round,Scence,}public enum SkillEffectType{Role,Attack,Scene,}public enum LifeTimeType{FollowSkill = 1 << 0,FixedTime = 1 << 1,FixedDistance = 1 << 2,}public class EffectComponent:SkillLogicComponent{private GameObject prefab;private bool hasTick = false;private float curTime;protected override void OnInit(){base.OnInit();configData = EffectConfigDataSystem.Instance.GetOrCreateData(dataId);}protected override void OnStart(){base.OnStart();//根据路径在这里加载Prefab,或者传入路径,交给特效系统做加载调用//(configData as EffectConfigData).effectPath;}protected override void OnExcute(){base.OnExcute();if (prefab != null){prefab.SetActive(true);//调用prefab上的其他脚本var data = configData as EffectConfigData;if ( (data.lifeTimeType & LifeTimeType.FixedTime) != 0){GetOrReleaseTick(true);curTime = 0;}else if((data.lifeTimeType & LifeTimeType.FixedDistance) != 0 ){GetOrReleaseTick(true);}}}protected override void OnEnd(){base.OnEnd();if( ((configData as EffectConfigData).lifeTimeType & LifeTimeType.FollowSkill) != 0){DeActiceEffect();}}public override void OnDestroy() { base.OnDestroy();}private void DeActiceEffect(){GetOrReleaseTick(false);if (prefab != null){prefab.SetActive(false);//调用prefab上的其他脚本//也可以销毁}}private void GetOrReleaseTick(bool register){if(register){if(!hasTick){ComponentTick.RegisterComTick(instanceId, this, OnTick);hasTick = true;}}else{if(hasTick){ComponentTick.UnRegisterComTick(instanceId);}}}private void OnTick(float deltaTime){var data = configData as EffectConfigData;if ((data.lifeTimeType & LifeTimeType.FixedTime) != 0){curTime += deltaTime;if(curTime >= data.liftTime){DeActiceEffect();}}else if ((data.lifeTimeType & LifeTimeType.FixedDistance) != 0){//获取特效和角色的距离//var dis = (prefab.transform.position - playerPos).magnitude;var dis = 10;if(dis > data.liftDistance){DeActiceEffect();}}}}public class EffectConfigDataSystem:DataSystem<EffectConfigDataSystem, EffectConfigData>{public override string dataPath => "Assets/ConfigData/SkillEffectConfigData.asset";}public class ComponentTick//不通过层层调用做Tick,将需要Tick的Component做统一管理,可以扩展为通过优先级做先后Tick的调度{public static Dictionary<long, TickInfo> Id2Com = new Dictionary<long, TickInfo>();public struct TickInfo{public long Id;public Component com;public Action<float> cb;}public static void Tick(float deltaTime){foreach (var item in Id2Com.Values){if(item.com.Enable){item.cb?.Invoke(deltaTime);}}}public static void RegisterComTick(long instanceId,Component com,Action<float> cb){if(!Id2Com.TryGetValue(instanceId, out var info)){info = new TickInfo(){Id = instanceId,com = com,cb = cb,};Id2Com.Add(instanceId, info);}else{Debug.Log($"重复注册:{instanceId}");}}public static void UnRegisterComTick(long instanceId){if(Id2Com.TryGetValue(instanceId,out var info)){Id2Com.Remove(instanceId);}}}