特效會有個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);}}}