1. 程式人生 > >ACT技能編輯器的製作經驗

ACT技能編輯器的製作經驗

這是侑虎科技第407篇文章,感謝作者Gordon供稿。歡迎轉發分享,未經作者授權請勿轉載。如果您有任何獨到的見解或者發現也歡迎聯絡我們,一起探討。(QQ群465082844)

作者知乎:https://zhuanlan.zhihu.com/p/38001896
作者也是U Sparkle活動參與者,UWA歡迎更多開發朋友加入U Sparkle開發者計劃,這個舞臺有你更精彩!

請輸入圖片描述

技能編輯器功能演示(Unity版本為5.6.4p4)

緣起

我在一個新組建的團隊裡,只有一個策劃兼製作人,加我一個客戶端(公司美術內包)。我們需要在一個月內快速出一個ACT動作的Demo,Demo需要有至少三個角色和若干小兵,並通過評審會正式立項。當時我並沒有做過這類ACT遊戲,對這類遊戲所知甚少,在策劃的講解,以及惡補了一些文章後,我們定下了計劃:

1、在半個月內做一個技能編輯器,要支援編輯動作每一幀的攻擊受擊框,配置按鍵指令的招式轉換,以及各種攻擊資料的配置;

請輸入圖片描述
技能編輯器提高後續策劃工作效率

2、同時制定美術製作動作的規範,以及我們如何切分動作,動作的複用和銜接;

3、第三週做Runtime的戰鬥邏輯,第四周整合資源,編輯器的配置,出Demo包。

計劃

時間緊,任務重。我給自己的任務是不加班完成,並儘可能將編輯器功能提前完成,給後續留出更多的調整和DeBug的時間,因此需要:

1、儘可能和新策劃搞清楚需求,做Prototype的推演,推演Editor下如何配置,Runtime如何執行。有經驗的程式,會花更多的時間在前期的需求整理和邏輯推演上。

2、和美術梳理工作流程,讓美術可以立刻開始做事情,後續能和我們的編輯器很好地對接。所以,在Demo階段,美術只提供模型Fbx和動畫Fbx,剩下由程式整合。

請輸入圖片描述
常用的動畫分割方式

分析和選擇

接下來,有幾個技術點,需要做出選擇:

1、是使用Animator 的狀態機來做邏輯,還是自己做切換邏輯?

請輸入圖片描述

最終選擇了自己做邏輯切換,核心就是工作流可定製。
請輸入圖片描述
Animator狀態機自動生成,最簡化,不包含任何邏輯和資料

2、是使用Unity的GUI來做編輯器,還是UGUI做?

請輸入圖片描述

最終選擇用UGUI,主要是怕用GUI時間來不及。另一個原因是我希望策劃能在一個技能編輯器下做所有的操作,流程一體化。不要那種在A Prefab上掛個指令碼,再在B Prefab上掛個指令碼,最後把A和B拖到C Prefab指令碼的Field上這種編輯方式,這不利於策劃理順思路。(可對比白鷺引擎一堆Editor和Unity All In One)

3、如何做編輯器資料的序列化?

請輸入圖片描述

最終選擇了自己做序列化,最主要的是資料和資源分離,以及熱更新。

4、是使用Unity自帶的Collider(物理系統)來做碰撞,還是自己做碰撞檢測?

請輸入圖片描述

因為我們是橫版遊戲,所以,按傳統的格鬥遊戲做法,只需要做Box,而且是不需要旋轉的Box,自己實現的複雜度大大降低,那肯定自己做更可控。

簡單總結下選擇方案的思路:

  • 沒有方案是對所有專案都合適的,選擇適合自己專案型別以及開發週期,人員配置的方案;
  • 要著重考慮資料和資源的分離,熱更新。考慮好和美術、策劃的銜接工作流程,這些比邏輯本身更重要;
  • 可控性和開源,儘量使用自己熟悉和可控的實現方式,儘量選擇開源的外掛和工具。

關於ACT遊戲,我的理解

我作為非ACT遊戲玩家,我理解的ACT遊戲和MMORPG,ARPG這類遊戲的戰鬥表現上,區別如下:

請輸入圖片描述

所以,ARPG是介於ACT遊戲和MMORPG之間,ARPG和ACT遊戲,有著重大的區別(按策劃的話:市面上太多所謂動作遊戲,只能說是ARPG)。

ACT遊戲需要具備:

  • 快速的操作反饋,需要玩家銜接好操作,才能輸出最大化;
  • 招式銜接的豐富,不同招式下,不同時刻,不同按鍵,要切換到不同的招式;
  • 打擊反饋的豐富,被擊需要有動作反饋,輕中重,浮空等各種擊中方式,被擊者需要不同的動作反饋。
  • 以上是一個非ACT玩家,做為程式開發者的理解,方便我區分當年做MMO的經驗。

開動:以資料為基礎來搭建框架

編輯器的目的是為了資料,所以,對於技能(這裡只包含按鍵觸發的主動技能)的資料組織如下:

請輸入圖片描述
角色資料圖

ActorCfg:角色資料的根,包含所有角色相關資料;
ActorAttr:基礎資料,包含資源Prefab ID,移動速度,重力,指令起始招式ID等;

請輸入圖片描述
指令

ActInfo:一個動作的資訊,對應美術製作的一個動畫(Animation);

請輸入圖片描述
動作列表

FrameInfo:每一幀的資訊,一般包含攻擊,被擊框的資訊(Position,Scale),以及一些複雜邏輯使用的標記;

請輸入圖片描述

動作遊戲,需要給攻擊和被擊都打上框,沒有被擊框的幀,就不會被擊中。

SkillInfo:招式資訊。每個招式可以由一個或多個動作(ActInfo)組成,並可以選擇動作的幀範圍,這樣最大程度地複用美術動作,並可以由策劃自由發揮,組合出新的動畫;

請輸入圖片描述
不同的ActInfo(Animation)組合出不同的招式

BoxInfo:不同型別的Box,不同資訊。比如攻擊,被擊,霸體等不同Box的資訊不同;

請輸入圖片描述
攻擊類Box資料

HitInfo:攻擊類Box,擊中以後的資料配置;

請輸入圖片描述
當擊中後,需要的資料,根據策劃的需求來

ChangeCtrl:切換招式的資料。比如在幀範圍(0~10內)觸發了指令(Up),切換到招式xx;

請輸入圖片描述
Change To 招式 ID

SkillCtrl:各種技能處理,播放音效,特效,設定速度等等;

請輸入圖片描述
各種控制

Trigger:各種判斷條件,條件達成,才會執行ChangeCtrl或SkillCtrl

請輸入圖片描述
各種Trigger

以上是主要的資料模組,ActInfo主要儲存每一幀框的位置縮放資訊,SkillInfo儲存各種ChangeCtrl和SkillCtrl,並用Trigger來做為生效條件。後續按策劃需求對技能編輯器的擴充套件,更多是SkillCtrl的新增和Trigger的新增。
通過這一套技能編輯處理,策劃可以配置出豐富的表現效果。只要資料組織好了,同一資料,有不同的表現形式,這也是基礎的MVC的適用。

請輸入圖片描述
對於習慣使用類似TimeLine編輯方式的,可以用Frameline方式(Gif壓縮有點糊了)

序列化資料

編輯器,最重要的是資料的讀寫,也就是序列化和反序列化。

之前已經說明,我不使用Scriptobject和Prefab上掛MonoBehaviour來序列化資料的原因,不再贅述。現在放在手邊的選擇有兩個,一種是序列化為文字(XML,JSON),一種是序列化為Bytes。

我最先是序列化為XML,因為我認為XML比JSON可讀性強一些,剛開始我可以先手動寫一個XML資料,Deserialize為對應的Class,等編輯器基本成型以後,再補上Serialize即可。

這樣,我只需要設計一個根據Tag反射來自動化處理的方式即可(當時沒仔細找,應該此方式有現成方案)。

請輸入圖片描述
通過Tag,可以寫一套通用的序列化反序列化處理

XML序列化在前期的好處是,編輯器不穩定,資料經常錯誤報錯,可以手動修改XML的資料,方便測試編輯器Bug,以及除錯功能。XML還有很好的可讀性,以及版本管理方便Merge。
但是當我們編輯器穩定後,XML的劣勢就顯現了:

1、序列化反序列化效能差;
2、這裡需要給一些不支援String化的資料做支援(Color,Curve等);
3、序列化依賴變數Name,如果我們重構改了變數名(重構名字是常事),資料就丟失了。

經過調研,我們選擇了用Protobuf來做序列化,直接解決了1和3的問題。需要了解Protobuf詳情的請自行Google。

請輸入圖片描述
Protobuf來序列化,Protobuf的Tag

對於2裡面的問題,需要做一些處理,將其Protobuf化

請輸入圖片描述
將Color Protobuf化

請輸入圖片描述
PBColor的使用

再結合AdvancedInspector外掛(後面介紹)來使inspector下支援和原來一樣的顯示。

請輸入圖片描述

至此,基本上解決了序列化和反序列化的問題,最後我們將每一個角色資料,序列化,存為一個.Bytes檔案。(帶來的副作用,即不方便Merge,這裡只有策劃通過規範提交修改的流程,減少衝突,目前我們遇到很少)

請輸入圖片描述
一個角色對應一個檔案,由編輯器讀寫

配合AdvancedInspector提供的UDictionary,我們可以方便地做Dictionary的序列化。

請輸入圖片描述
UDictionary

請輸入圖片描述
UDictionary的使用

具體使用我就不說了,大家可以去看他的Sample,對於在Inspector上做東西,方便很多很多。

SkillCtrl 和 Trigger的設計

在總體設計好之後,新加的技能方面的功能,主要集中在SkillCtrl 和 Trigger的新增。
這裡在語義上是:當XXX的時候,執行YYY。 XXX就是Trigger,YYY就是SkillCtrl 。比如策劃可以配置:

  • 當0~10幀時,播放技能特效;
  • 當10~20幀時,並且是擊中狀態,播放技能特效;
  • 當20~30幀時,震動螢幕;
  • 當20~30幀時,角色在浮空狀態,新增人物殘影;

在設計上,SkillCtrl和Trigger,都是這樣:

1、定義一個TriggerType

請輸入圖片描述
Trigger Enum

2、定義Trigger的基類

請輸入圖片描述
Trigger Base

3、之後擴充套件,就是繼承基類,加上配置資料,Override isTrigger方法,處理自己的條件邏輯。

請輸入圖片描述
Trigger Implement

SkillCtrl,主要包含一列Trigger,所有的Trigger都為True,就返回True。這裡沒有設計or的邏輯方式,Trigger之間,都是And的方式。

請輸入圖片描述

對應Runtime邏輯,一個SkillCtrl cfg,對應一個ctrler

請輸入圖片描述
Runtime Ctrler Base

擴充套件的時候,實現對應的Ctrler即可:

請輸入圖片描述
對應的播放聲音 Ctrler

大部分遊戲,需要設定邏輯主迴圈,在每一個Logic Tick,去檢查Trigger,Trigger都為True,執行對應的Ctrler,即可。這裡的Logic Tick,即對應編輯器裡面的一個幀,同一概念。

接下來是一些小的點:
1、這裡有一個小Trick,就是類命名用"_"分隔,前半部分一樣,後半部分割槽分具體實現,可以通過反射來獲得Instance類,以免做工廠,或者Switch case。
ActorSkillCtrlerCfg_Base cfg = U3DUtil.CreateClassByBaseType<ActorSkillCtrlerCfg_Base>(ctrlType);

請輸入圖片描述

2、在這樣設計的過程中,遇到了一個問題,就是Unity的Inspector。宣告是Base Class,但是賦值了Child Class,Unity的Inspector還是顯示Base Class 的資料。
例如我希望在編輯器裡點任何一個Ctrler,都顯示對應Ctrler型別的資料。

請輸入圖片描述
資源的Ctler

請輸入圖片描述
對應的程式碼

Unity預設的Inspector,只會顯示申明的型別ActorSkillCtrlerCfg_Base,而不會顯示對應的特效資料。

請輸入圖片描述

這裡解決方案很簡單,引入Advanced Inspector即可,外掛能自動顯示真實Type的資料,這是Advanced Inspector幫助解決的最大的問題。

思考:每一個功能模組,我們要處理好完全不設計和過度設計的平衡,特別是一開始,不要過度設計。花時間理順需求,儘量找準擴充套件點,把擴充套件點處理好,後續就是往上搭積木了。這個過程中,必然經歷幾次重構和優化,沒有一次就做好的設計,重構是程式的核心技能。

編輯器和Excel表資料的配合

當我們開發Demo的時候,儘可能求快。角色的資料,我們就直接在編輯器裡配了,比如攻防血,技能每一個Hit的傷害,招式的CD,消耗的SP等,也並未考慮招式升級了,基礎傷害提升等,這些也不是編輯器應該關心的邏輯。

當專案正式化之後,我們需要解決的,就是資料,策劃是配置在Excel裡的,我們需要整合Excel資料和編輯器資料。

對我們來說,比較麻煩的是,我們結算傷害,並不是以一個技能為一個單位,而是以每一個Hit,一個技能,策劃可能配置多個AtkBox,一個AtkBox,可能產生多次Hit,但是,我們不可能給每個Hit配置一個傷害,數值策劃配置到這麼細,他們會崩潰。所以,策劃的Excel表,是以技能(招式)為單位,配置一個傷害。

請輸入圖片描述
數值配一個角色,某一個招式動作的總傷害數值

而技能編輯策劃,在技能編輯器裡,配置每一個Hit,對應這個傷害的百分比:

請輸入圖片描述

比如這個招式,策劃配整體傷害1000,某個Hit這裡配置0.4,最終結果就是1000*0.4 = 400。通過這種方式,將數值策劃從編輯器中解脫出來,他們無需關心有幾個Hit,只需要處理整體效果,技能編輯策劃去將所有Hit的係數分割。當然,我們數值計算很複雜,這只是初步基礎數值。

思考:這裡其實是想說,將邏輯和數值分開來,各自關心各自的東西。這樣,我們將Prefab(View),邏輯(技能邏輯),數值(Excel配表)都區分開來,按照一定的規則,在Runtime時候結合。

經驗教訓:
1、沒有做整體的Undo和Redo功能,因為做Demo的時候,整體只有兩週,實際大概一週半做完。沒有時間設計整體的Undo,Redo。後續根據策劃需求,解決了部分Copy,Paste,以及依靠Advanced Inspector的功能,做到了Inspector部分的Copy,Paste,也算基本不影響策劃的使用。但仍然算是一個不足。

2、戰鬥整體預覽,做得不夠完整。目前在播放器裡,可以在Play 招式的時候,對應播放特效,聲音。但是不能所有SkillCtrl整體模擬。 之前想過在Editor裡面跑一個簡單的Runtime Battle,只包含當前編輯的角色和一個NPC,讓編輯和預覽能夠無縫銜接。但是處理起來,有點麻煩。 現在的做法是在Runtime 的GM選單中給一個介面,當開啟技能編輯器編輯儲存以後,將Runtime Cache的配置清空並重新開啟戰鬥,選定的角色就會重新Load 新儲存的配置。雖然這個方法不夠完美,但也極大提高了策劃的速度(以前編輯好一個,需要重新Run遊戲,現在可以在遊戲中實時更改,再進入戰鬥就更新),幫助策劃提高工作效率,是程式責無旁貸的責任。

  • 有些部分設計得不夠友好,策劃配起來,有點麻煩。例如配置子彈的邏輯,目前做的不夠好。
  • 我不想做一個大而全的,能通用的編輯器,我只想理順思路,當我們下次做專案,或者類似東西的時候,能夠在現有的基礎上,通過裁剪和重構,快速得到一個符合專案需求的編輯器。(所以我的編輯器,不做任何Unity版本相容)

現有編輯器,可以支援格鬥,ACT,ARPG類遊戲,如果做MMO和RPG,那就要裁剪到過於複雜的部分,簡化即可。如果做點跑酷呢?那要這編輯器就沒意義了,一切都要根據自己的專案來,不是越強大越好。

最後總結:

  • 當我們需要快速出Demo的時候,更應該理順思路再動手,切忌上來就編碼。把可能的難點先測試搞定,之後就順手了;
  • 重構,不要怕重構,在合適的時間,做合適的重構,是我們程式的核心技能;
  • 組織好資料,讓資料和美術資源分離,不要讓美術資源的變化,干擾你處理資料的工作流;
  • 幫助策劃提升效率,是程式能力的體現;
  • 做編輯器,要站在策劃的角度,他們是使用者,自己要用,才會發現問題,Eating our own dog food.