技能架构设计 - 基于数据驱动(转)

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

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

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


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

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

简单的技能:


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


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


复杂的技能:

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


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


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

  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 + “\n” + e.StackTrace + “\n”;  
  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. }  
        private bool ParseScript(string filename)
        {
            bool ret = false;
            try
            {
                StreamReader sr = FileReaderProxy.ReadFile(filename);
                if (sr != null)
                    ret = LoadScriptFromStream(sr);
            }
            catch (Exception e)
            {
                string err = "Exception:" + e.Message + "\n" + e.StackTrace + "\n";
                LogSystem.ErrorLog(err);
            }

            return ret;
        }

        private bool LoadScriptFromStream(StreamReader sr)
        {
            bool bracket = false;
            SkillInstance skill = null;
            do 
            {
                string line = sr.ReadLine();
                if (line == null)
                    break;

                line = line.Trim();

                if (line.StartsWith("//") || line == "")
                    continue;

                if (line.StartsWith("skill"))
                {
                    int start = line.IndexOf("(");
                    int end = line.IndexOf(")");
                    if (start == -1 || end == -1)
                        LogSystem.ErrorLog("ParseScript Error, start == -1 || end == -1  {0}", line);

                    int length = end - start - 1;
                    if (length <= 0)
                    {
                        LogSystem.ErrorLog("ParseScript Error, length <= 1, {0}", line);
                        return false;
                    }

                    string args = line.Substring(start + 1, length);
                    int skillId = (int)Convert.ChangeType(args, typeof(int));
                    skill = new SkillInstance();
                    AddSkillInstanceToPool(skillId, skill, true);
                }
                else if (line.StartsWith("{"))
                {
                    bracket = true;
                }
                else if (line.StartsWith("}"))
                {
                    bracket = false;

                    // 按时间排序
                    skill.m_SkillTrigers.Sort((left, right) =>
                    {
                        if (left.GetStartTime() > right.GetStartTime())
                        {
                            return -1;
                        }
                        else if (left.GetStartTime() == right.GetStartTime())
                        {
                            return 0;
                        }
                        else
                        {
                            return 1;
                        }
                    });
                }
                else
                {
                    // 解析trigger
                    if (skill != null && bracket == true)
                    {
                        int start = line.IndexOf("(");
                        int end = line.IndexOf(")");
                        if (start == -1 || end == -1)
                            LogSystem.ErrorLog("ParseScript Error, {0}", line);

                        int length = end - start - 1;
                        if (length <= 0)
                        {
                            LogSystem.ErrorLog("ParseScript Error, length <= 1, {0}", line);
                            return false;
                        }

                        string type = line.Substring(0, start);
                        string args = line.Substring(start + 1, length);
                        args = args.Replace(" ", "");
                        ISkillTrigger trigger = SkillTriggerMgr.Instance.CreateTrigger(type, args);
                        if (trigger != null)
                        {
                            skill.m_SkillTrigers.Add(trigger);
                        }
                    }
                }
            } while (true);


            return true;
        }


文件的解析,也很简单


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


1.触发器:

从同一个基类继承,



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

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


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

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



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

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


部分Public 接口代码:



总结一下思路,就是

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

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

SkillTrigger 就执行具体的效果.


展开阅读全文

没有更多推荐了,返回首页