感悟(裝逼)語(yǔ)##
工程化的藝術(shù)是最美的藝術(shù)
前言##
大家好,我是漢家松鼠工作室創(chuàng)始人cg,今天我們來(lái)借我們開(kāi)發(fā)中的游戲《江湖X》,來(lái)工程化的談一下配置表以及它引申出來(lái)的各種概念。(我們本文只討論游戲開(kāi)發(fā)技術(shù)、管理,不討論具體的游戲內(nèi)容設(shè)計(jì)。)
江湖X雖然是一款獨(dú)立游戲,但我們?cè)谙到y(tǒng)框架級(jí)別,希望它是一個(gè)能夠容納項(xiàng)目團(tuán)隊(duì)擴(kuò)展乃至未來(lái)作為我們所有游戲引擎框架的一個(gè)模板。簡(jiǎn)而言之,我們希望實(shí)現(xiàn)一個(gè)完整的配置表打包體系,并且融入到我們整個(gè)項(xiàng)目,貫穿了開(kāi)發(fā)、測(cè)試、發(fā)布、運(yùn)維等各個(gè)流程。目前來(lái)看,我們確實(shí)在技術(shù)層面做到了(雖然還有許多地方需要優(yōu)化,但總體達(dá)標(biāo))
在此,我詳細(xì)的介紹一下我們?cè)谠O(shè)計(jì)之初的思考點(diǎn)和遇到的坑。
我們定位《江湖X》是一個(gè)需要長(zhǎng)線運(yùn)營(yíng),需要能夠高效、快速的填充游戲內(nèi)容,而又需要策劃易于上手,容易測(cè)試。所以我們?cè)谙到y(tǒng)架構(gòu)上需要保證和思考幾點(diǎn):
- 如何寫(xiě)出一個(gè)強(qiáng)大、高效的配置表程序?讓后期盡量我們不需要去維護(hù)打包程序,而具備我們想要的復(fù)雜數(shù)據(jù)結(jié)構(gòu)打包功能?
- 如何保障策劃和程序員各司其職,能夠有序的在項(xiàng)目中協(xié)作?
- 如何保障許多名策劃協(xié)作工作,而互相影響降到最小?如何保障策劃們獨(dú)立分工,如何管理一個(gè)完整的RPG項(xiàng)目中所有的資源?
- 如何在此機(jī)制下實(shí)現(xiàn)熱更新,如何來(lái)管理我們的版本?
先說(shuō)一下我們目前的使用效果:
- 4人(其中3人寫(xiě)代碼+策劃,1人美術(shù))開(kāi)發(fā)團(tuán)隊(duì)把游戲框架做出來(lái);
- 目前在完全沒(méi)有動(dòng)框架的情況下,擴(kuò)展到 4程序(全部兼策劃)+3策劃+1美術(shù) 較高效協(xié)作開(kāi)發(fā);
- 在沒(méi)動(dòng)框架情況下,運(yùn)營(yíng)游戲4個(gè)月,沒(méi)有出過(guò)大的技術(shù)問(wèn)題和瓶頸;
- 我們有信心在不大動(dòng)框架的情況下,擴(kuò)展到20+人團(tuán)隊(duì)高效協(xié)作開(kāi)發(fā);
不能說(shuō)有多出色,但基本實(shí)現(xiàn)了我們最初的構(gòu)想和規(guī)劃。接下來(lái),我們開(kāi)始談?wù)劯鱾€(gè)模塊我的設(shè)計(jì)思考?xì)v程
配置表
先解釋一下什么叫配置表
在游戲設(shè)計(jì)中,有大量的內(nèi)容填充的東東(比如有多少個(gè)角色;多少個(gè)技能,每個(gè)技能的攻擊模式是什么、攻擊力多少、成長(zhǎng)多少;多少種物品等等) 這些東西如果寫(xiě)死在程序中,則會(huì)造成
1、程序難以維護(hù)和擴(kuò)展(hardcode..有多么恐怖,程序員都知道)
2、難以集中管理所有的資源
3、策劃難于上手(需要了解程序的語(yǔ)法)
所以一般很多游戲開(kāi)發(fā)團(tuán)隊(duì)的做法是,用excel排表、填數(shù)據(jù),再用程序去解析。我們稱這些excel叫配置表。 那么在游戲開(kāi)發(fā)中,程序和策劃就可以用這個(gè)表切分,程序員來(lái)實(shí)現(xiàn)邏輯解析這些表,并且在游戲內(nèi)用邏輯關(guān)聯(lián)起來(lái)。游戲策劃專注于表的設(shè)計(jì)、內(nèi)容填充。
在頂級(jí)的公司或團(tuán)隊(duì),大多是開(kāi)發(fā)一個(gè)專門“地圖編輯器”來(lái)實(shí)現(xiàn)此類功能(比如startcraft、warcraft的編輯器)。當(dāng)然,這個(gè)的開(kāi)發(fā)代價(jià)和成本不是簡(jiǎn)單的配表程序可比的……
我的理解,其實(shí)配置表就是一個(gè)游戲的靜態(tài)數(shù)據(jù)庫(kù)。它保存了游戲的基礎(chǔ)內(nèi)容,并且各個(gè)表之間實(shí)現(xiàn)了大量的邏輯關(guān)聯(lián)。使用excel的原因是:
1、直觀化編輯,提高策劃開(kāi)發(fā)效率;
2、能夠方便的做數(shù)據(jù)分析(excel的各種公式、報(bào)表等)
3、入門門檻低
4、SVN/github等管理起來(lái)方便,和代碼一起關(guān)聯(lián)存
網(wǎng)絡(luò)上確實(shí)有許多開(kāi)源的配置表打包程序,而我們工作室根據(jù)自己的需求,獨(dú)立實(shí)現(xiàn)了一套我們自己的配置表框架。有如下特性:
- 有一套獨(dú)立的XML語(yǔ)法定義excel的映射關(guān)系
- 支持excel綁定到XML
- 支持綁定到C#的Pojo,支持復(fù)雜數(shù)據(jù)結(jié)構(gòu)定義(實(shí)現(xiàn)了ORM功能)
- 支持到XML的增量打包
- 支持拆分excel表
- 支持打包protobuf(性能最高的序列化/反序列化庫(kù))
- 開(kāi)發(fā)環(huán)境支持跨平臺(tái)(mac/windows)
- 客戶端運(yùn)行平臺(tái)支持跨平臺(tái)(ios/android/pc...)
我們計(jì)劃實(shí)現(xiàn)但目前尚未實(shí)現(xiàn)的特性:
- 開(kāi)發(fā)環(huán)境熱配置(不需要重新打包,即可在開(kāi)發(fā)環(huán)境中讀取修改,方便策劃人員編寫(xiě)和調(diào)試)
而我們確實(shí)使用此套程序在打包客戶端、服務(wù)器所有相關(guān)配置表。
我們考慮在未來(lái)開(kāi)源此套程序
我們最終的一鍵打包/程序讀取后臺(tái)流程如下:
excel -> xml -> [Deserialize]C# Pojo -> protobuf -> data文件 -> [Runtime]C# Pojo
下面給出一個(gè)我們的綁定語(yǔ)法例子大家可以看一下
<?xml version="1.0" encoding="utf-8"?>
<bean name="explore_event" from="T探索地圖事件.xlsx" to="explore_events.xml" >
<variable name="tag" fromCol="TAG" type="string" />
<variable name="map" fromCol="所屬地圖" type="string" />
<variable name="type" fromCol="類型" type="int"/>
<variable name="story" fromCol="劇情" type="string" />
<variable type="collection" fromCol="觸發(fā)條件" split="\n">
<bean name="condition" split=":">
<variable name="type" notNull="true" fromIndex="0" type="string" />
<variable name="value" fromIndex="1" type="string" />
</bean>
</variable>
<variable name="number" fromCol="刷出次數(shù)" type="int" defaultValue="1"/>
<variable name="locations" fromCol="刷出地點(diǎn)" type="string"/>
<variable name="property" fromCol="概率" type="int" defaultValue="100"/>
<variable name="image" fromCol="縮略圖" type="string"/>
<variable name="active_on_start" fromCol="開(kāi)場(chǎng)刷出" type="int" defaultValue="0"/>
</bean>
這里個(gè)描述文件,描述了我們的excel映射關(guān)系。T探索地圖事件.xlsx被打包成explore_events.xml形成名為explore_event的pojo。
里頭關(guān)于各項(xiàng)屬性,見(jiàn)具體描述,fromCol是來(lái)自具體excel的哪一列,可以定義類型(type)、默認(rèn)值(defaultValue)等。
此外我們還可以定義collection類型,其也可以是一個(gè)自定義的pojo,具體語(yǔ)法細(xì)節(jié)此處不再贅述。
讓程序和策劃有序
或許一些獨(dú)立開(kāi)發(fā)者不會(huì)注重分工,因?yàn)閳F(tuán)隊(duì)只有一兩個(gè)人,無(wú)所謂分工和工作切面。但我的建議是,工程化的管理是必要的,哪怕只有自己一個(gè)人,也應(yīng)該嚴(yán)格區(qū)分幾個(gè)工作模塊。(用方法把自己的策劃類、程序類、美術(shù)類等工作區(qū)分開(kāi)來(lái))
這是為了項(xiàng)目未來(lái)擴(kuò)展和增長(zhǎng),也是用切面強(qiáng)迫自己遵循開(kāi)發(fā)原則。
如何讓程序和策劃們有序的工作起來(lái),這里其實(shí)已經(jīng)到了一個(gè)我們的開(kāi)發(fā)工藝流程的問(wèn)題。這里仁者見(jiàn)仁,我僅談?wù)勎覀兊姆椒ā?/p>
我的核心思想還是:
程序來(lái)實(shí)現(xiàn)編輯器,策劃是編輯器的使用者。也就是說(shuō),策劃是程序員的用戶。
具體開(kāi)發(fā)流程:
1、大家一起開(kāi)腦洞,討論游戲功能
2、偏策劃的模塊,由策劃寫(xiě)需求文檔,設(shè)計(jì)配置表結(jié)構(gòu)(比如XXXX系統(tǒng)玩法)
3、偏程序的模塊,由程序?qū)懶枨笪臋n,設(shè)計(jì)配置表結(jié)構(gòu)(比如XXXX打擊感增強(qiáng)模塊)
4、策劃填表、程序開(kāi)發(fā)邏輯
5、程序編寫(xiě)邏輯代碼和開(kāi)發(fā)策劃所需的測(cè)試工具(我們使用統(tǒng)一的控制臺(tái)指令)
6、代碼提交SVN
7、策劃取代碼,測(cè)試配置表,提交
8、打版工程師從SVN上取代碼和配置表,打包發(fā)布
我們?cè)趗nity中實(shí)現(xiàn)了一個(gè)小的控制臺(tái)面板,用于測(cè)試開(kāi)發(fā)和我們自己調(diào)試。由程序提供各項(xiàng)的測(cè)試控制臺(tái)指令
如上圖,下半部分放的是我們一些常用的指令集,每個(gè)人都可以根據(jù)自己的需要靈活配置。
讓策劃之間有序
使用excel在SVN上管理有一個(gè)比較麻煩的地方,使用csv模式的話不夠強(qiáng)大(如不能使用公式、圖標(biāo)、表格格式等功能),但是可以做代碼的merge。使用xls默認(rèn)的格式的話,我們目前沒(méi)有找到好的merge方法。
再說(shuō)即使能夠merge,我們也不希望。因?yàn)椴邉澋墓ぷ魇呛塥?dú)立,需要獨(dú)立測(cè)試和考核的。大家如果經(jīng)常混編一塊資源,很容易造成極大的項(xiàng)目管理混亂。
所以我們的做法是:
將每個(gè)策劃獨(dú)立拆分一組excel。舉個(gè)例子,每個(gè)我們把物品表,根據(jù)不同的策劃人員,拆成了多個(gè)文件,最后打包的時(shí)候統(tǒng)一。這樣每個(gè)人可以編輯自己的部分,而不影響其他人。不同的策劃人員,也可以根據(jù)分工不同,給不同的權(quán)限。
對(duì)于一些必須公用的或者重要的excel,我們?cè)O(shè)置必須svn lock才可編輯。
或許會(huì)有人問(wèn),為什么要搞得這么麻煩?不會(huì)損失工作效率么?——沒(méi)有辦法,我們認(rèn)為這個(gè)地方笨的辦法就是最好的辦法,千萬(wàn)不能取巧。策劃的配置表是個(gè)極其嚴(yán)謹(jǐn)?shù)氖虑椋仨氊?zé)任到人。特別是游戲進(jìn)入到運(yùn)營(yíng)狀態(tài)后,嚴(yán)格的管理能夠省去無(wú)窮多的后患。經(jīng)驗(yàn)之談!
關(guān)于版本運(yùn)維
游戲發(fā)布了以后,需要更新版本。我們分“大更新”和“熱更新”。
大更新(版本更新)也就是整個(gè)游戲包的更新,對(duì)應(yīng)蘋果的是ipa、安卓的是apk。
熱更新(小版本更新)也就是游戲內(nèi)更新,玩家可以進(jìn)入游戲后下載新包。
大更新不用提了,直接使用unity打包測(cè)試提交。
我們這里主要討論小版本的更新及版本管理方式,在設(shè)計(jì)的時(shí)候,有幾個(gè)問(wèn)題需要思考:
- 哪些內(nèi)容需要熱更新?怎么做到?
- 需要了解平臺(tái)的約束,哪些東西是不能熱更新的?
- 小版本號(hào)之前是增量更新還是全量?
- 大版本和小版本之間的差異如何來(lái)管理?
幾個(gè)前提需要知道:
- unity的assetbundle所有含有C#代碼的部分,在ios平臺(tái)上均不能加載。(ios平臺(tái)的保護(hù)機(jī)制)
- 目前的國(guó)內(nèi)的CDN流量費(fèi)用一般是0.3元/GB
那么我們的做法是:
1、增量圖片資源部分:每次大版本既有資源(包括原來(lái)所有的熱更獨(dú)立資源)打圖集,隨包一起發(fā)布。熱更部分,如果有新增的話,打assetbundle獨(dú)立發(fā)布。
舉個(gè)例子,我們所有的物品圖標(biāo),為了降低drawcall,統(tǒng)一打包成圖集,隨包體發(fā)布。但如果我們需要臨時(shí)增加一個(gè)物品A,我們則把它單獨(dú)打在名為item的assetbundle包里熱更發(fā)布。我們的熱更新框架,將保證在程序?qū)用嫫浜驮械膱D集被一致的讀取,對(duì)于策劃人員來(lái)說(shuō),此項(xiàng)透明。
2、增量音樂(lè)/音效部分:直接使用assetbundle打包下發(fā)
3、配置表/LUA等:每次完整打包、全量下發(fā)
這樣做的目的是,我們需要在某個(gè)大版本下打包熱更新,則只需要把配置表和相關(guān)的lua代碼到當(dāng)時(shí)的打版代碼下,生成即可。主要原因有以下幾點(diǎn):
1、protobuf必須保證原生數(shù)據(jù)結(jié)構(gòu)不發(fā)生改變
2、全量在客戶端讀取方更好管理,而且我們算下來(lái)也只有壓縮后不到2MB的更新數(shù)據(jù),CDN費(fèi)用可以承受
3、降低我們的運(yùn)維復(fù)雜度,運(yùn)維人員永遠(yuǎn)只需要維護(hù)一份最新的更新包,而不用管歷史的
4、副本/地圖配置等:每次完整打包、全量下發(fā)
同上,占用數(shù)據(jù)不大,為了方便運(yùn)維管理,都是全量。
對(duì)于本塊熱更新如果有人感興趣的話,我將回頭另外寫(xiě)一篇筆談,專門討論熱更新框架的代碼層面,如何比較優(yōu)雅的實(shí)現(xiàn)對(duì)客戶端程序員和策劃的透明化。
結(jié)語(yǔ)
誠(chéng)然我們的這些東東都很稚嫩,但確實(shí)是在開(kāi)發(fā)和運(yùn)營(yíng)過(guò)程中摸索出來(lái)的。我們也會(huì)一直不斷的去完善它,作為一個(gè)框架性的生產(chǎn)力工具,將會(huì)貫穿我們未來(lái)的每一款制作游戲中。
當(dāng)然,工程化和創(chuàng)意其實(shí)也是有隱性對(duì)立關(guān)系的,如何保證優(yōu)秀的創(chuàng)意能夠工程化的方法實(shí)現(xiàn)出來(lái),也是一種藝術(shù),不是么?
最后,歡迎大家與我交流!