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 + "\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. }  



 

文件的解析,也很簡單

 

那么從代碼上怎么實現呢?

 

1.觸發器:

從同一個基類繼承,

2.工廠模式來創建注冊觸發器,

在外部注冊觸發器的代碼:

3.技能實例來管理觸發器,

執行觸發其實也可以寫這里.

 

4.技能系統來管理所有技能

技能是可以復用的,技能系統就是一個技能池子,不停地new技能實例和回收技能實例

 

部分Public 接口代碼:

 

總結一下思路,就是

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

SkillInstance 管理 SkillTrigger,負責觸發器的觸發.

SkillTrigger 就執行具體的效果.

 

代碼封裝上,可以把核心代碼做成庫,只開放觸發器的擴展接口,項目已經在使用,很不錯.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM