從多角度看易擴展的游戲技能系統設計

>>>  技術話題—商業文明的嶄新時代  >>> 簡體     傳統


  GameRes游資網授權發布 文 / 梧桐& 猴與花果山&扼殺黑暗


  一個好擴展的技能系統能讓技能、BUFF、道具之間的關系能夠很好的表示和調整,對游戲開發而言無疑是減少了不少的加班時間。那么要怎么設計一個易擴展的游戲技能系統呢?游資網從游戲策劃、程序和玩家這三個角度進行整理:


  策劃角度(猴與花果山):

  感覺這個問題很久不提了但是還是值得拿出來探討下。我花了至少5年在設計和實踐(我自己經歷了大小7個項目,雖然大多最后因為其他原因都沒上)這樣一套機制并且可以說基本解決了這個問題!我并不想藏著他,我也和很多朋友分享了,同時也感謝他們提出了很多意見,包括細節和優化等方面,正因為互相之間的借鑒和交流,我到今天已經把這套機制歸納的非常好了,還是想把它分享給更多想做好游戲的人,希望大家能進一步交流,把它更完善化,以形成一種規范,這套機制適用于任何類型的游戲開發,因為他是一個很棒的思路。

  首先分析一下可擴展性:我從一個設計師的角度來看,所謂的可擴展性是——你并不知道策劃下一個設計的是什么,但是你需要在盡可能不改動核心代碼的基礎上去把它實現了,并且在調試的時候(甚至是上線之后要做調整的時候),你可以并不傷筋動骨的去修改它。這里除了你要有好的代碼規范外,還需要抽象一套機制來實現它。

  實際上,好的策劃的腦洞是非常大的,你真不知道他會設計出什么樣的技能,但你并不能說一些設計因為無法實現就理所應當被埋沒了,(我對策劃設計Dota類游戲的思路要求是開放的,發揮想象力的,

  因此我想了這樣一個機制:[設計思想] 游戲系統設計思路的牢籠 一味追求實用性

  他們的核心在于:

  1、明確區別了AOE\Buff和技能3塊,策劃應該從這個角度出發思考問題。

  2、這既然是一套機制,你可以把它用在任何游戲的框架當中。比如我要做一個Dota傳奇的卡牌游戲,一樣可以用這套機制,但是核心在于——你的策劃要有能力歸納出游戲中的回調點。

  當然我們在使用這套機制的時候,邏輯上實現并沒有任何問題,但是我們一樣會遇到一些從邏輯變成動畫的困難,尤其是當我們的戰斗在服務器上一瞬間完成了,但是要把結果告訴客戶端嗎,并且有客戶端重新驗算一遍的時候,因此我在之后有總結了一套作法,來完成這個事情:手游回合制游戲戰斗機制歸納式設計

  這個解決的是,當你有各種有趣的buff,但是又想不大改客戶端的時候,你應該這樣去建立這個框架。你可以想象如果你做一個MT類型的游戲,戰斗是服務器一瞬間的,但你又要客戶端重演,我們舉個例子:

  比如我門設計了在MT類型游戲中加入地形因素:可以有火海,火海每回合開始的時候對所有場上敵我英雄造成火焰傷害。

  然后有個英雄是一只鳳凰,鳳凰有2個被動效果:

  1、受到火焰傷害的時候變成治療自己相當于傷害值的血量。

  2、戰斗中第一次死亡可以復活,回復最大生命值50%,如果在火海中則回復100%。

  這樣一個英雄和地形,我們如何實現呢?如果你看了我上面的幾套機制,并且理解了,那基本沒有難度,你根本不需要硬編碼。但這里有個問題,我如何讓客戶端重現?這就是上面這篇說的關鍵了。

  程序角度(扼殺黑暗):

  技能沒什么框架,只是有很多字段罷了。

  比如 cd 施法距離、釋放動畫、飛行動畫等等。。。

  其實游戲技能不是一直不是什么難點,畢竟根據每個屬性實現邏輯就好了。

  技能真正麻煩一點是其實是 所謂的“效果”。

  因為從很久以前,游戲設計的時候就把效果這個概念添加進來了。

  對于 游戲戰斗對象主體,我們暫時叫做BattleAgent簡稱BA。

  影響BA的數據有很多,比如移動速度 攻擊力 基礎屬性 等等,影響的入口也有很多:

  技能

  buff/被動技能

  裝備

  強化

  寶石

  

  等等,而這些實際上從影響結果沒什么區別。

  首先我們先談區別,對于這些數值影響,其實區別只有入口或者說是作用的方式,

  技能是BA(castor)對BA(target)釋放造成的瞬間數值影響。

  buff是castor對BA(target)安裝后造成的持續數值影響,分為按時觸發瞬發和持續修改數值。

  裝備是特定容器對BA持續修改數值。

  所以這里游戲開發者們抽象出了 效果這個概念。

  對與效果而言,只存在2個行為:

  1 對BA產生數值影響

  2 對BA撤銷數值影響

  所以效果最終定義為:

  1. interface Effect {

  2. void cast(BattleAgent target);

  3. default void reverse(){

  4. }

  5. }


  而對于其他功能實體來說,就可以簡化為效果的容器:

  1. interface EffectContainer extends Effect{

  2. List<Effect> getEffects();

  3. }


  這樣我們就只要定義不同效果容器就可以了,

  比如技能:

  1. class abstract  Skill implements EffectContainer{

  2. public void spellTo(BattleAgent target){

  3. foreach(Effect effect in getEffects()){

  4. effect.cast(target);

  5. }    

  6. }

  7. }


  對于buff:

  1. class abstract Buff implements EffectContainer{

  2. public void update(){

  3. foreach(Effect effect in getEffects()){

  4. effect.cast(target);

  5. }    

  6. }

  7. }


  對于被動技能(其實也是buff):

  1. class abstract  BuffSkill extends Buff {

  2. public void install(){

  3. foreach(Effect effect in getEffects()){

  4. effect.cast(target);

  5. }    

  6. }


  7. public void unstall(){

  8. foreach(Effect effect in getEffects()){

  9. effect.reverse(target);

  10. }    

  11. }

  12. }


  裝備同理被動技能,

  是不是很清晰?

  而對于復雜的技能效果,因為我們已經抽象出了Effect。

  所以怎么實現也就很容易了!

  1. class DamageEffect implements Effect{

  2. private int damage = 100;

  3. public void cast(BattleAgent target){

  4. target.hp -= damage;

  5. }

  6. }


  看起來是不是很簡單,我們來寫個變羊。

  這個技能包括2個效果外形修改和屬性。

  1、外形變羊

  1. class ChangSheepEffect implements Effect{


  2. public void cast(BattleAgent target){

  3. target.gameObject = GameManager.getAnimeObject("sheep");

  4. }

  5. }


  2、攻擊力和防御力變0 速度變慢

  1. class PropChangeEffect implements Effect{


  2. public void cast(BattleAgent target){

  3. target.atk = 0;

  4. target.def = 0;

  5. target.speed = 50;

  6. }

  7. }


  就是這么簡單,同學你明白了嗎?

  如果要深入一點的話,就是變羊是持續型的,到了時間會變回來。

  所以我們要一個可以觸發buff的效果:

  1. class TriggerBuffEffect implements Effect{

  2. BuffSkill buff = new BuffSkill (){

  3. public List<>getEffects(){

  4. return new List().add(new ChangSheepEffect()).add(new PropChangeEffect());

  5. }

  6. }

  7. public void cast(BattleAgent target){

  8. int time = 3000;//3秒

  9. target.addBuff(buff,time);

  10. }

  11. }


  然后把這個TriggerBuffEffect加到技能能上就ok了,就完成了一個可以變羊3秒的技能。

  玩家角度(梧桐):

  這篇文章想表達的是我作為一個玩家,不是作為商業公司的員工,所謂的程序、美術或策劃,對感興趣的一些游戲技能體系的一些分析。

  這個要看追求了,沒追求一張 excel 表配置上幾百個參數,碰到一個需求加一個總能寫出來;有追求甚至能和 war3 一樣十幾年前的游戲就可以在沒有源碼情況下做出 dota(然而總有嘴強王者認為這沒有什么大不了);又或者像我一樣幻想有沒有可能設計出一套集成 GTA(模擬現實)+神之浩劫(moba)+虐殺原型(超現實動作)又可供玩家擴展的技能系統?

  技能是如此有意思的事情,精巧的、可擴展的技能框架簡直可以說是游戲最大的樂趣,遠不是一些泡菜網游里面一句一切皆 buff 就能把我打發了的,所以很久以來我對探索技能在各種游戲里面的實現樂此不疲,分享一點研究的線索。

  比較少談具體的編碼細節,因為就像我本篇最后說的那樣,研究多了甚至開始反思技能這樣一個概念到底存不存在,或者說不同游戲類型討論的技能真的是同一種東西嗎?如果游戲真的是現實生活的映射,那我們人類作為角色擁有技能嗎?

  最簡單的就是虛幻競技場那種 fps,感覺這種沒什么好說的,雖然虛幻競技場的改裝槍是一個玩點,但是代碼層面并不復雜。

  復雜點的泡菜網游 rpg 或者 moba 可以參考星際爭霸的銀河編輯器,風暴英雄就是用這個做出來的。

  熟悉 war3 不熟悉 sc 就下載 ga 的 war3 mod(魔獸爭霸III 2015),這是論壇頭目在 sc 體系下完整的復刻了 war3,可以觀察 war3 里面的技能一個一個是怎么實現的,常見的比如暴風雪、空中鎖鏈、變羊、風暴之錘等等,其實簡單的的網游技能無非就是這些技能的變種。

  里面大體來說把技能相關的東西解耦成了如下模塊,unit、action、order、ability、behavior、effect、actor,然后通過反射和連線配置來擴展游戲。

  簡單描述這個體系,很復雜,也不求沒研究過的人能看懂:ability 就是通俗意義上的技能,包裝成 order 被 unit 調用,ability 本身不負責技能邏輯,只有使用 ability 消耗的資源(比如消耗多少魔法)前搖后搖之類的處理,他指向一個 effect,在 ability 調用時候轉發到 effect 身上;effect 晚點說,先說 behavior,常見的 behavior 類型就是添加一個 buff,也就是對一個屬性的階段性修改,behavior 是有狀態的;回頭說 effect,effect 最終組成了一顆 effect 樹,effect 常見的有發射投射物、應用 behavior、周期性的調用下一個 effect、對區域進行搜索然后對搜索到的每個對象進行調用 effect、造成傷害、創建單位等,除了周期性效果之外的 effect 都是無狀態的,只是簡單的對邏輯進行一次函數調用。以上所有的一切描述都是純邏輯層面的,而 actor 是表現體,所有的特效、動畫都是通過 actor 監聽以上所有對象的信號 來做出相應的反應,而不是常見的動畫驅動邏輯,換句話說,如果 actor 不監聽邏輯層的信號,表現會有問題,但是游戲的核心邏輯還是照樣跑,包括 unit 類其實都不包括任何的表現信息,只是在 unit 類創建時候通知 actor 創建并做表現 。actor 很類似一個完全被動驅使的客戶端,其他模塊是服務器。最后,在整個一套異步流程中,需要一些全局的類似 ai 中的黑板數據,記錄比如施法者、施法位置、目標攻擊位置、目標攻擊對象等,由于 effect behavior 的樹狀向下擴散,所以這個黑板數據其實也要不停地更換上下文,這個就有點復雜了。

  另外,表現層的 actor 的技能動作處理,也比較有意思,采用動作名稱模糊匹配的思路:大概就是這樣的:對角色當前各種狀態進行采樣(比如心情、天氣、角色上一個狀態名稱等),根據狀態類型優先級自動選擇最合適的且存在的動作,比如如果資源同時存在Attack 00 Attack 01動作,那就隨機播放一個 Attack,再比如風暴英雄里面普通死亡、爆炸死亡、天空擊飛死亡播放不同動作也是在這個層次處理的。當然,動作模塊的設計是個大問題,各大引擎中間件也經常是一個版本一個方案的大改,他這套方案也有局限性。好處就是用起來方便,ruby 曰:約定大于配置。想象一下,當玩家新做一個路人角色,這個角色在看到美女時候就自動做特定的好玩動作,而這一切需要做的也許僅僅是,配置一個 npc,給他一個特定名稱的動作,比如 love stand。星際目前有296個關鍵字可以相互組合。

  再談一個小細節,一般自研引擎都解決的比較好,但是也可能還有人還沒注意這件事:星際美術的動作資產中,除了骨骼動畫,聲音、特效等實時演算的功能也集成在里面,而且編輯器可以做到直接預覽,上層技能框架里面把很多單位的表現狀態(比如替換模型)都抽象成“動作”(而不是單純的骨骼動畫),這種直觀的設計很舒服。反觀一些方案(比如 unity 很常見的做法)直接把 fbx 作為美術流水線的終點,局限性很大,加個簡單特效都要放到上層技能框架里,還不能預覽,最好在裸骨骼動畫和“技能”之間加個支持編輯器預覽的、引擎實時演算功能(主要是特效、聲音)相關的中間層,這個中間層的結果作為“動作”最終暴露給業務層,這方面 unreal 做的就比較好,編輯器對 fbx 的二次加工支持預覽,用起來方便多了。

  研究完了會發現順便把插件接口、游戲錄像(甚至可以做到從錄像中某個點加入扮演某個陣營、網絡同步、表現信息自動適配機器配置等內容還一塊解決了。只不過我個人山寨的時候最后還是把這套方案的一些繁瑣細節給折中了,最后的結果很類似 dota2 的技能系統,這就不多說了。

  一個玩家都能隨意擴展的 moba 技能體系,難道專業策劃還做不好?這恐怕是做游戲思路的差異吧。再補充個好玩的,之前 war3 平臺的地圖《軍團 td》作者做了個網游夢塔防,在測試中。

  具體到編碼細節我從 war3 時代就開始研究,也山寨過,非三言兩語可以說清楚,但核心思路其實也就上面說的那些。只要把常見技能都親手制作一遍,心中自有定論。雖然說起來簡單,但是如果非要把自己定位到程序、美術或策劃的立場上,而不是玩家或者 mod 作者,這玩意可能都太復雜了。

  有人說看起來和某些 ai 的設計有點像?那是當然的,無論是 ai 還是所謂的“技能”,從基礎的 if else 到上層做了一層又一層的抽象,說到底都是無非就是為了控制異步邏輯的復雜度,手段都差不多。至于游戲 ai 里面的亂七八糟理論,和本主題無關,就忍住不吐槽了,反正也是一個亂象叢生的領域。

  實在覺得搞不懂也懶得折騰這么復雜的東西,就去研究 war3 的 we 編輯器吧。

  大體思路就是把持續性效果、瞬間傷害、生成單位等一些基礎設施準備好,再設計出常見技能的技能模板,最后在合理的時間點發出信號,配合觸發器或腳本來擴展技能內容。

  技能模板如果懶得設計,甚至初始技能每個技能一個狀態機硬寫好像也沒什么不可以,反正魔獸爭霸那么復雜的游戲其實基礎技能也沒多少。

  未來擴展出來的技能都是這些技能模板的實例,區別是發出的信號或觸發器在腳本層處理不一樣。

  魔獸那么多自定義地圖的技能都是這么出來的,復制一個已有的技能的配置,然后稍作修改,雖然說起來好像有點傻。

  還記得當年第一次見 dota 屠夫的鉤子,驚為天人。

  要研究 we,ga 也是大本營,只不過現在討論的少了,畢竟老了,都轉到 sc 上面去了。

  比 moba 的技能還要復雜?mugen 之類的格斗夠不夠?

  b 站的 mugen 專區,大家玩的不亦樂乎,unity 上讀過一些逆向的格斗游戲,做的不錯的比如韓國的網游靈魂之心,用的是狀態機。國內的比如王者之劍、影之刃啥的,其實都很好逆,unity 寫的。unity 平臺還有一個格斗插件寫的也不錯,讀的是早期版本,沒有用狀態機有點難讀,但很多格斗業務邏輯相關的細節處理的蠻好,比如技能取消、幀優勢(Frame Advantage這么翻譯沒錯吧,街霸的概念)、物理,現在的新版本應該更優秀吧。

  格斗相關的代碼雖然讀了很多,但是沒有實際做過格斗游戲,不好給更多的參考意見了。

  個人的一些反思,可能有點口齒不清亂七八糟 ,對真正做游戲未必有用,放在這里暫記下吧:再復雜的游戲,所謂的技能,本質上是在一段時間線上有撤銷需求、需要強擴展設計的、分支極為復雜的、支持配置的異步邏輯,這些異步邏輯之間還可能會有并行交互。更遠一點來看,游戲單位在游戲世界中的存在本身就是狀態(比如云風的 skynet 游戲服務器中對每個單位生成一個 luastate 的 agent,配合 coroutine 來實現異步拉成同步不阻塞),很多技能就是單位這個狀態體生成的子狀態,這樣理解單位的行為(比如 walk、idle,而不單單是通俗意義上的技能)會變得更有意思,仿佛和人類社會一樣,一切都是并行順著時間向前走的,沒有回調沒有中斷,一切邏輯的上下文變得自然而然(異步的復雜性很多時候是來源于上下文的丟失)。

  如果這套設想是成立的,簡化異步編程,各行各業有很多探索,游戲行業未必就是做得最好的,比如我曾業余實踐過基于 actor 模型(coroutine模擬的) 的技能框架,雖然很多細節至今沒能解決,但確實是很有意思的探索。



GameRes游資網 2015-08-23 08:57:21

[新一篇] 移動應用新趨勢:App需要ASO,正如Web需要SEO

[舊一篇] 關于概率要素和統計學要素在游戲設計中的運用(上)
回頂部
寫評論


評論集


暫無評論。

稱謂:

内容:

驗證:


返回列表