• Unity3D手游开发日记(2)


    我想把技能做的比较牛逼,所以项目一开始我就在思考,是否需要一个灵活自由的技能系统架构设计,传统的技能设计,做法都是填excel表,技能需要什么,都填表里,很死板,比如有的技能只需要1个特效,有的要10个,那么表格也得预留10个特效的字段.在代码里面也是写死一些东西,要增加和修改,就得改核心代码,如果我要把核心部分做成库封装起来,就很麻烦了.

    能不能做成数据驱动的方式呢?

    改技能文件就行了,即使要增加功能,也只需要扩展外部代码,而不用改核心代码,

    我是这么来抽象一个技能的,技能由一堆触发器组成,比如特效触发器,动作触发器,声音触发器,摄像机震动触发器等等,这些触发器到了某个条件就执行触发,触发条件一般是时间,如果有比较复杂的浮空技能,可以增加落地触发等.

    自定义一个技能文件,代替excel表格,看起来是这样:

    简单的技能:

    每一行都是一个触发器,这些触发器,到了某个条件会自动触发.

    上面的意思就是,第0秒开始面向目标,第0秒开始播放动作1000

    复杂的技能:

    这个技能能将目标打到空中,并完成3连击,然后从空中砸向地面,

    CurveMove(0, 0.413, 104, 0, 0, 0, 0);的意思就是,第0.413秒,开始做曲线运动,让角色飞到空中,曲线运动的ID是104,

    用这样的文件来配置一个技能,很灵活,也很快,

    [csharp] view plain copy
     
    1. private bool ParseScript(string filename)  
    2. {  
    3.     bool ret = false;  
    4.     try  
    5.     {  
    6.         StreamReader sr = FileReaderProxy.ReadFile(filename);  
    7.         if (sr != null)  
    8.             ret = LoadScriptFromStream(sr);  
    9.     }  
    10.     catch (Exception e)  
    11.     {  
    12.         string err = "Exception:" + e.Message + " " + e.StackTrace + " ";  
    13.         LogSystem.ErrorLog(err);  
    14.     }  
    15.   
    16.     return ret;  
    17. }  
    18.   
    19. private bool LoadScriptFromStream(StreamReader sr)  
    20. {  
    21.     bool bracket = false;  
    22.     SkillInstance skill = null;  
    23.     do   
    24.     {  
    25.         string line = sr.ReadLine();  
    26.         if (line == null)  
    27.             break;  
    28.   
    29.         line = line.Trim();  
    30.   
    31.         if (line.StartsWith("//") || line == "")  
    32.             continue;  
    33.   
    34.         if (line.StartsWith("skill"))  
    35.         {  
    36.             int start = line.IndexOf("(");  
    37.             int end = line.IndexOf(")");  
    38.             if (start == -1 || end == -1)  
    39.                 LogSystem.ErrorLog("ParseScript Error, start == -1 || end == -1  {0}", line);  
    40.   
    41.             int length = end - start - 1;  
    42.             if (length <= 0)  
    43.             {  
    44.                 LogSystem.ErrorLog("ParseScript Error, length <= 1, {0}", line);  
    45.                 return false;  
    46.             }  
    47.   
    48.             string args = line.Substring(start + 1, length);  
    49.             int skillId = (int)Convert.ChangeType(args, typeof(int));  
    50.             skill = new SkillInstance();  
    51.             AddSkillInstanceToPool(skillId, skill, true);  
    52.         }  
    53.         else if (line.StartsWith("{"))  
    54.         {  
    55.             bracket = true;  
    56.         }  
    57.         else if (line.StartsWith("}"))  
    58.         {  
    59.             bracket = false;  
    60.   
    61.             // 按时间排序  
    62.             skill.m_SkillTrigers.Sort((left, right) =>  
    63.             {  
    64.                 if (left.GetStartTime() > right.GetStartTime())  
    65.                 {  
    66.                     return -1;  
    67.                 }  
    68.                 else if (left.GetStartTime() == right.GetStartTime())  
    69.                 {  
    70.                     return 0;  
    71.                 }  
    72.                 else  
    73.                 {  
    74.                     return 1;  
    75.                 }  
    76.             });  
    77.         }  
    78.         else  
    79.         {  
    80.             // 解析trigger  
    81.             if (skill != null && bracket == true)  
    82.             {  
    83.                 int start = line.IndexOf("(");  
    84.                 int end = line.IndexOf(")");  
    85.                 if (start == -1 || end == -1)  
    86.                     LogSystem.ErrorLog("ParseScript Error, {0}", line);  
    87.   
    88.                 int length = end - start - 1;  
    89.                 if (length <= 0)  
    90.                 {  
    91.                     LogSystem.ErrorLog("ParseScript Error, length <= 1, {0}", line);  
    92.                     return false;  
    93.                 }  
    94.   
    95.                 string type = line.Substring(0, start);  
    96.                 string args = line.Substring(start + 1, length);  
    97.                 args = args.Replace(" ", "");  
    98.                 ISkillTrigger trigger = SkillTriggerMgr.Instance.CreateTrigger(type, args);  
    99.                 if (trigger != null)  
    100.                 {  
    101.                     skill.m_SkillTrigers.Add(trigger);  
    102.                 }  
    103.             }  
    104.         }  
    105.     } while (true);  
    106.   
    107.   
    108.     return true;  
    109. }  



    文件的解析,也很简单

    那么从代码上怎么实现呢?

    1.触发器:

    从同一个基类继承,

    2.工厂模式来创建注册触发器,

    在外部注册触发器的代码:

    3.技能实例来管理触发器,

    执行触发其实也可以写这里.

    4.技能系统来管理所有技能

    技能是可以复用的,技能系统就是一个技能池子,不停地new技能实例和回收技能实例

    部分Public 接口代码:

    总结一下思路,就是

    SkillSystem 管理SkillInstance,创建和回收所有技能 

    SkillInstance 管理 SkillTrigger,负责触发器的触发.

    SkillTrigger 就执行具体的效果.

    代码封装上,可以把核心代码做成库,只开放触发器的扩展接口,项目已经在使用,很不错.

  • 相关阅读:
    jQuery同步Ajax带来的UI线程阻塞问题及解决办法
    jQuery的deferred对象详解
    原生js,jquery ajax请求以及jsonp的调用
    vue10行代码实现上拉翻页加载更多数据,纯手写js实现下拉刷新上拉翻页不引用任何第三方插件
    js判断手机或Pc端登陆.并跳转到相应的页面
    移动端touch事件封装
    坦然面对:应对前端疲劳
    webpack2 项目
    PPK提供的浏览器类型及版本检测方法
    2013年五大主流浏览器 HTML5 和 CSS3 兼容性大比拼
  • 原文地址:https://www.cnblogs.com/czaoth/p/5785628.html
Copyright © 2020-2023  润新知