当前位置: 首页 > news >正文

技能系统详解(2)——特效表现

特效会有个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);}}}

http://www.lqws.cn/news/498043.html

相关文章:

  • nnv开源神经网络验证软件工具
  • 【第二章:机器学习与神经网络概述】03.类算法理论与实践-(1)逻辑回归(Logistic Regression)
  • 华大北斗TAU951M-P200单频定位模块 多系统冗余保障永不掉线 物流/车载导航首选
  • 历史项目依赖库Bugfix技巧-类覆盖
  • LED-Merging: 无需训练的模型合并框架,兼顾LLM安全和性能!!
  • Spring Boot:运用Redis统计用户在线数量
  • Flask学习笔记
  • 1.2、CAN总线帧格式
  • DeepSeek今天喝什么随机奶茶推荐器
  • Redis简介
  • 通过使用gitee发布项目到Maven中央仓库最新教程
  • 前端高频面试题汇总
  • 【AI 测试】测试用例设计:人工智能语言大模型性能测试用例设计
  • Qt源码分析: QChildEvent
  • Robyn高性能Web框架系列04:事件、中间件与错误处理
  • 异步IO框架io_uring实现TCP服务器
  • Postgresql中不同数据类型的长度限制
  • 用Python“看见未来”:自动驾驶感知系统实战全解析
  • 矩阵阶数(线性代数) vs. 张量维度(深度学习):线性代数与深度学习的基石辨析,再也不会被矩阵阶数给混淆了
  • react快速开始项目模板
  • 越南数学家吴宝珠恶搞式证明朗兰兹纲领
  • HTML基础结构
  • 速通KVM(云计算学习指南)
  • /var/lib/docker/overlay2目录过大怎么办
  • 深入浅出Node.js中间件机制
  • Android开发 原生设置-apps-里面隐藏应用信息
  • OpenSSL 混合加密
  • 企业级AI平台的能力架构与模块化规划
  • 大数据时代UI前端的变革:从静态展示到动态交互
  • TypeScript移动端导航工具实现