Unity3D手游开发日记(2) - 技能系统架构设计

来源:互联网 发布:电机选型计算软件 编辑:程序博客网 时间:2024/04/29 17:08

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

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

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


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

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

简单的技能:


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


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


复杂的技能:

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


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


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

        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 就执行具体的效果.


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

5 0
原创粉丝点击