Unity3D如何有效地組織代碼?

Unity3D可以說是高度的Component-Based Architecture,

同時它的庫提供了大量的全局變數。

這些都和我曾接觸到的cocos2d-x,和非遊戲框架有很大出入,

請問各位前輩有沒有什麼好的方法、模式、框架來組織代碼呢?

謝謝!


準確地說,代碼作為Unity項目里的一種資源,此問題應該擴展到如何組織Unity資源。簡單說說我們的經驗:

- Unity有一些自身的約定,譬如項目里的Editor,Plugins等目錄作為編輯器,插件目錄等等。知名的插件會自己存放一個目錄,譬如NGUI等。

所以我們自己的代碼,一般目錄名會以下劃線開頭,譬如 "_Scripts", "_Prefabs"等。

對於場景,文檔等目錄,用兩條下劃線,以便他們能排在最頂部。

- 代碼用C#,別用JS。必要的話用namespace將自己的代碼括起來。我們是用namespace把自己積攢的公用庫包住。

- C#的注釋要認真寫,打///就能幫你補全了,沒理由偷懶。

- 每個程序文件開頭要用一段注釋寫修改Log,誰改過什麼簡單留一條說明。就算用了Unity的版本管理或者Git,那些log終究會丟失,只有認真把log寫在代碼里,才會有意識去認真優化它。

- Unity的腳本邏輯,就功能而言大體分為兩種,一種是比較獨立的,譬如爆炸之後1秒鐘消失,這種單獨寫個腳本綁定到目標上即可。

更多的是腳本里與其它的腳本進行交互。Unity里提供了一種萬金油的方法是SendMessage, 這種方法性能略差,如果你調用的頻率不高,隨便用也無妨。另一種方法是直接通過對象的實例去調用。

我們的做法是寫幾個公用的控制器,讓它們各司其職,負責各自的事情:

- 寫一個一個GlobalManager.cs來控制遊戲的全局變數及全局方法。靜態類模式。譬如當前玩到第幾大關第幾小關,玩家的金幣數量等。

- 寫一個GameController.cs來控制當前關的遊戲進程。單實例模式。遊戲的主循環也是用它控制。初始化,勝利、失敗判定等等。

- 寫一個InputController.cs來控制所有的用戶輸入。單實例模式。滑鼠、鍵盤、觸摸屏,我們做遊戲是保證同時支持這三種輸入的,因為大部分時間是在PC上測試。

關於GameController與InputController的聯繫,有點讓人糾結。一般來講是在InputContoller里調用GameController.Instance.Foo()執行方法。或者直接對Input寫成Listener的模式,讓GameController去監聽。

- 其它的類似菜單控制器,聲音控制器,成就控制器,IAP虛擬道具控制器等等,也是採用類似的方法管理。

- 關於PlayerPref的操作,統一寫成靜態類的get/set模式,程序中哪裡要用則直接讀寫。

- 如果你的項目里場景的數量少(&<5),那麼拖入場景的資源可以很隨意。如果場景數量很多(幾十個,有的解謎遊戲每個關卡就是一個場景),那麼拖入場景的prefab數量一定要少。

- 設計你的prefab資源里,你要想像當其他人拿到這些資源,是否直接拖入一個空場景里就能run,頂多再簡單設置幾下。如果你設計的資源不能做到這些,那麼得好好重新想想。

寫了這些,感覺寫不下去了。

想吃透Unity,起碼得真做出幾款產品放上線才行。真正做產品的過程中會碰到各種各樣意想不到的問題,代碼不斷地被重構和妥協,不存在什麼最佳的方案。

暫時就寫這些吧,希望能拋磚引玉。


Unity 遊戲框架搭建 (一) 概述

Unity 遊戲框架搭建 (二) 單例的模板

Unity 遊戲框架搭建 (三) MonoBehaviour單例的模板

Unity 遊戲框架搭建 (四) 簡易有限狀態機

Unity 遊戲框架搭建 (五) 簡易消息機制

Unity 遊戲框架搭建 (六) 關於框架的一些好文和一些思考

Unity 遊戲框架搭建 (七) 減少加班利器-QApp類

Unity 遊戲框架搭建 (八) 減少加班利器-QLog

Unity 遊戲框架搭建 (九) 減少加班利器-QConsole

Unity 遊戲框架搭建 (十) QFramework v0.0.2小結

Unity 遊戲框架搭建 (十一) 簡易AssetBundle打包工具(一)

Unity 遊戲框架搭建 (十二) 簡易AssetBundle打包工具(二)

Unity 遊戲框架搭建 (十三) 無需繼承的單例的模板

Unity 遊戲框架搭建 (十四) 優雅的QSignleton(零) QuickStart

優雅的QSignleton (一) Singleton單例實現

優雅的QSignleton (二) MonoSingleton單例實現

優雅的QSignleton (三) 通過屬性器實現Singleton

優雅的QSignleton (四) 通過屬性器實現MonoSingleton

優雅的QSignleton (五) 優雅地進行GameObject命名

Unity 遊戲框架搭建 (十五) 優雅的QChain (零)

Unity 遊戲框架搭建 (十六) v0.0.1 架構調整

Unity 遊戲框架搭建 (十七) 靜態擴展GameObject實現鏈式編程

Unity 遊戲框架搭建 (十八) 靜態擴展 + 泛型實現transform的鏈式編程

Unity 遊戲框架搭建 (十九) 簡易對象池

Unity 遊戲框架搭建 (二十) 更安全的對象池


http://mp.weixin.qq.com/s?__biz=MjM5NjE1MTkwMg==mid=401571840idx=1sn=64128b22a3fd092ed2a3d5f5be25b094scene=0#wechat_redirect

今天看到unity官微發送2015技術講座,第一個就是代碼架構,看完之後很有啟發。


我的代碼大概遵循這麼幾條原則吧(想到哪兒寫哪兒不分先後):

1.邏輯腳本基於場景劃分

2.抽離靜態配置數據、全局管理數據以及對局臨時數據的管理

3.使用單例模式創建不依賴於場景的遊戲對象及其上的全局管理器

4.避免使用GameObject.Find以及SendMessage,聲明對象引用以顯示標明腳本之間的依賴性,活用delegate解耦合

5.多用組合少用繼承(Component的架構真的是太棒了)

6.數據行為與邏輯表現分離,即V與MC的分離,換句話說多寫class少寫MonoBehavior。(通常初期在快速開發原型時會把一個功能全部實現寫在一個繼承於MonoBehavior的腳本中,儘早進行重構,抽離出負責數據管理與控制的類,這對於後期功能的增加與修改時很有必要的)

7.善用Coroutine(Coroutine真是太方便了)

8.盡量能夠使用自定義的配置文件輔助Prefab上腳本參數的配置。

總的來說記得知乎上看到誰說過cocos2d是程序員友好的,而Unity3D是設計師友好的,寫了這麼多年Unity3D代碼我真是覺得我的思考方式越來越像策劃而不是程序員了,使用Unity3D開發,寫代碼應該只佔了大概50%的工作,另外50%都在編輯器上,如果你用過相信你懂得。


Unity除了掛在Game Object上的代碼比較難找,這一點跟其他工具做項目不同外,其他就是每個公司自己的管理風格了。在實際操作中,無非就是文件夾/文件夾/.../文件,名字要起的好。這樣就在源代碼級別管理好了自己的代碼。

至於樓主所提的Component-based architecture,個人在管理過程中會習慣去適應unity的腳本概念,雖然與C++等代碼做的項目一樣,一樣有源代碼級的很多manager,但是這些manager的啟動不再需要自己做一個類似main一樣的入口手動啟動,只要掛在某個Game Object上,在Start()中啟動就可以。配合Unity的script execution order,慢慢腳本的概念就會扎入你的心裡。用Unity,因為逃不開在Game Object上掛腳本,所以,個人覺得還是習慣這種方式,的確在概念上也很簡單。唯一要注意的是,一定要對Game Object的名字有一定的約束,養成很好的命名習慣。然後自己再寫一些查找工具用來查找用到某個名字的腳本的Game Object,配合起來就會理解Unity這樣工具的設計理念:基於腳本的對象化。

舉個例子,單機版的一些存儲,如果配合Unity的playerprefs和Unity的回調機制(OnApplicationPause(),OnApplicationQuit()),你會發現你不用為save/load去設計存儲格式,去設計一個基於虛函數的OnSave()、OnLoad(),更不用需要去考慮各個模塊的存儲先後問題。Unity都給你做好了。

我不知道有哪些模式,只知道也曾經用C++等熟悉的語言來組織自己的邏輯(因為當初剛開始untiy),但是當我做項目多了,發現其實unity的方式挺簡單,也很好用。如果所謂的腳本能做好一件事情,你會發現小即是美。


我的建議是:不要在開頭去想如何組織代碼,而是先讓代碼跑起來。

不過既然lz問了,還是大概說下吧。。。

與unity相比,cocos2d頂多只能算是一個跨平台圖形庫,他基本沒有遊戲引擎相關的代碼,而unity里component-based design就是為了能更靈活地去處理遊戲內的邏輯。梁偉國 說的controller,inputManager的概念(其實這些在遊戲編程里很普遍)都狠詳細,但這些東西在你沒有實際詳盡的認識之前,其實並不好理解。我建議lz去大致系統的看一下unity方面的書籍,然後實際照著去寫,把設計先放在後頭,等做出形了再重構。

無止境的重構是做不出東西的。


並沒有啥本質區別,以前寫cocos的時候也可以寫成component based。所以主要的矛盾並不是unity vs cocos,而是component vs inheritance。非常推薦game programming patterns書中的component章節(Component · Decoupling Patterns · Game Programming Patterns)

unity自己有一些蹩腳的地方比如全局的dt,沒有全局統一入口,update順序不確定等等,都還是比較容易克服的。


轉答案:那些事情是用Unity開發項目應該一開始規劃好的?如何避免後期釀成巨坑? - fonzieyang 的回答


1.代碼目錄分分好, 保持良好的層次關係

2.盡量不要使用全局變數


有句話,我總會仔細的跟很多朋友講到

做遊戲,牛逼的不是實現某一個功能,而是把成百上千的功能放到一起,卻不亂。

而我認為,這條路,必須自己不斷總結反思。別人給的經驗,往往並不一定合適具體的項目。

不管別的,有幾個標準可以檢驗你的代碼是否合理

1:美術資源 提交後,策劃配表提交後,不需要程序變換,即可立馬看到結果。 比如,一個圖標變了,美術可以打包圖集,提交。 策劃變了一個配表,他可以直接提交配表。 一個新的怪物出現了,讓美術自己提交怪物資源..... 最後他們自己跑遊戲里一看,發現東西是OK 的。

不管你程序如何設計,這事是最重要的一條。 簡單來說就是 讓美術和策劃能夠提交後直接看到結果。

2:遊戲暫停時,能否直接通過 視圖窗口,看到盡量多的遊戲當前狀態。

以我做的ARPG為例,

再某一下,我一刀揮出去,打到一個怪物了。 再怪物受擊的那一瞬間,我點擊暫停。

這個時候我點擊怪物這個GameObject,可以看到怪物當前正在播放那個動畫,重力是多少,重力加速度是多少,受擊的效果有哪幾個,數值分別是多少... 可以通過視圖窗口看到當前怪物移動組件是否使用中,AI 組件是不是被停滯掉了

.........

這個點是存在爭議的,諸多從C++過來的朋友,cocos寫得多的朋友。並不習慣組件化這樣的東西

3:是否一個類只干一件事

這個同樣很重要

甚至可以說是需要被確保的。組件化或許這點有些爽。就是基本確保了你寫的某個組件只干一件事。

4:,,,,,


我以前寫c++的

談談我自己的架構,個人認為用的比較舒心

我的思路是數據文檔分離(可能是以前mfc寫多了)

數據通通用單件模式儲存,比如說一個三國遊戲,所有武將的數據寫一個單間類herolistglobal,所有城市數據寫一個類citylistglobal

unity裡面gameobj只負責數據的表現

一個武將在哪裡,現在什麼狀態,都在需要表現的時候去global類裡面找,然後顯示


使用行為樹就可以了。不要相信有萬能的框架,唯有把代碼細分到足夠小粒度的Action,從宏觀層去架構遊戲。固定的框架只能使開發遊戲前期投入大量的學習成本,後期的話,遷移也不容易... 只能說多人開發方便解耦。現在我用行為樹寫了很多Action,基本上很少寫重複的代碼了... 大部分時間看到我都是在拼圖... 不知道的人以為我是策劃呢,各種連連看


unity是基於組件開發 一上來確實不太適應,也跟同事討論了好久,至今沒什麼太好的結論。說下我們的方案,

一.啟動場景一個 object上掛腳本主入口,他負責驅動遊戲內所有manager的創建,更新及銷毀。

二.人物,AI等需要繼承的通過持有gameObject來是實現。而不是掛載到gameObject上。

三.基礎功能通過組件實現。掛載到gameObject上。

總體上就是最基礎層面 負責具體功能的用組件來降低耦合度,這樣也清晰。而一些需要始終存在的或者需要繼承則通過原來的方式,創建腳本對象,然後持有Object,這是這個object不在負責具體邏輯,只是各個基礎功能的總和。


首先是幾點要在項目初期考慮清楚的東西,會影響到代碼組織:

  • 是否要做熱更新(代碼,資源),怎麼做?

  • 項目是什麼類型,有些什麼特點,不同類型組織方式不同

  • 是否要套用框架(個人不贊成)

這幾點會影響:

  • 如果你要熱更新,就不能在prefab上直接掛腳本(prefab上的腳本不能熱更新),無論是dll方式,還是lua。

  • 如果是lua熱更新,要考慮清楚哪些部分用lua,哪些部分用c#,中間如何銜接,c#部分是否通過dll熱更?

  • 不同的項目,特點很不一樣,除掉一些框架性的部分(網路,資源管理等),遊戲的框架要因項目而異。大而全的框架,維護和調優都是困難的,且會增大持續開發難度,畢竟手游是個快速產品

  • 是否要用框架?(一些mvc框架),個人不贊成遊戲套用MVC,MVC更適合軟體開發模式,不適合遊戲,如果使用,反而會大大臃腫遊戲框架。定一個針對項目類型的清晰直接的更好,手游項目,更看重快速迭代,重構,而不是寫好一個,通用之。

參考文章

Why are MVC TDD not employed more in game architecture?

Unity: Now You"re Thinking With Components

以上....一點點小總結


開發了3個Unity項目, 想到幾個問題需要注意:

1. 避免monobehavior腳本濫用, 作為一種需要掛到gameobject的資源腳本, 與很多prefab產生了大量相關性, 造成維護困難, 除非一些不經常修改的, 如ngui的各種資源型腳本, 避免遊戲邏輯撲到該腳本上. 寫一個健壯的MVC架構, 頂住各種"便利"的實現方式, 是項目成功的必備基礎

2. 資源目錄劃分, 涉及到如何打包資源, 如何發布版本, 如何控制資源大小, 做一些你認為可行的劃分方式, 如按照圖片, 動畫, 模型等, 或按照角色, UI, 特效等, 寫一些必要的效率插件, 寫一個穩定的資源管理框架

3. 程序腳本, 美術資源, 策劃配置, 常常會因為項目的倉促進行, 導致最終發版本一團亂, 同上述第2點, 前期一定要規劃好版本控制方式, 是否採用git, svn, asset server等等, 是否需要開發專用工具導入導出, 版本發布是否需要考慮各種本地化, 渠道分支怎麼管理, 甚至開發人員職責如何劃分(邏輯程序, 插件程序, 引擎程序等)


我來說一句,少用掛腳本!少用掛腳本!少用掛腳本!遊戲邏輯只用一個Monobehaviour驅動,這樣的項目再亂也是有下限的,不知道有多少公司多少項目被濫用掛腳本坑了。


1. 目錄結構。按公用性,模塊性,為美術資源和代碼劃分清晰。注意Unity3d內建即定的目錄,避免衝突。最近我看到了這篇關於Unity3d開發實踐的文章,談及的很廣泛,也很有參考慮價值,裡面有目錄結構劃分的建議。Unity3D手游開發實踐《騰訊撞球》客戶端開發經驗總結

2. 遊戲框架。沒有最好的遊戲框架。只要適用項目、足夠的靈活性、能夠約束好各個程序同事的代碼風格。我覺得就可以了。我了解到的Unity3d框架有strangeioc、uiframe


能談到代碼布局可能就不是初級水平了,可以去看看一些熱門的框架 比如MVCVM 有點學習成本 但很靈活 框架沒有萬能的 每個遊戲結構可能不一樣 一般都是根據項目自行設計 以易用易維護為準


1、unity組件式開發的思想(有點像View + ViewModel)

2、數據驅動,事件驅動

3、使用MVC等結構,View和邏輯分離(這個太重要了)

4、用狀態機管理複雜邏輯,當普通狀態機不夠用時,可以考慮更複雜的多層次狀態機或行為樹。

5、不要掛太多腳本,腳本之間的生命周期回調先後很難控制,所以個人覺得還是掛一個腳本驅動遊戲(可能是以前寫遊戲的時候習慣了),當然unity的組件式開發思想也是很好的,每個組件做單一的事情,只是負責驅動整個遊戲的不要掛太多的腳本。

6、遊戲中的各個配置表要程序來設計和建立,參考資料庫設計(如果策劃設計數據表真的後期沒法活了……)

7、消息機制

9、命名規範和資源文件管理

8、還有就是編程上要注意的問題了……


Unity3d 最不缺的就是框架了吧。。

Ulua Xlua JSBING, CSTOLUA,SLUA 還有其他等等等等


少掛腳本,多寫工具,要有框架思想


推薦閱讀:

程序員平常加班嚴重,是怎樣兼顧自己個人能力提升的?
有哪些迅速提升編程技術的方法或途徑?
印度的軟體產業為什麼發達?
遊戲公司的程序員會不會在做項目的時候預留後門,然後自己編寫外掛之類的惡意程序賺錢?
為什麼會有編程行業的轉行做IT培訓?

TAG:軟體開發 | 遊戲開發 | 編程 | Unity遊戲引擎 |