我想把技能做的比較牛逼,所以項目一開始我就在思考,是否需要一個靈活自由的技能系統架構設計,傳統的技能設計,做法都是填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 就執行具體的效果.
代碼封裝上,可以把核心代碼做成庫,只開放觸發器的擴展接口,項目已經在使用,很不錯.
