遊戲專案的目錄結構規劃

不知道各位如何規劃遊戲專案的目錄結構?各位所使用的是專案管理體系的正規方法,或者只是隨意地將檔案與資料夾散佈在電腦中的各處?在一個遊戲專案的開發過程裡,重要的資產除了程式碼檔案之外,還包括了設計文件、美術素材、音效檔案,以及最終的執行檔等等。該如何妥善管理這些不同類別的項目,以達到良好的成效?在此將以我個人目前使用的方法,提出一套目錄結構的規劃架構,與各位一同進行交流討論。

有些人喜歡將許多各式各樣不同用途的資料夾,直接放置在電腦的「桌面」上,最後經常會使得整個桌布,幾乎被各種不同圖示與資料夾所淹沒。這樣的作法,在一時的使用上或許非常方便,但是如果沒有善加整理歸檔,未來很容易會找不到想尋找的目標文件或檔案。不論是公司的正式專案,或是自己在工作之餘開發的個人專案,都能夠從良好的目錄結構規劃中,得到易於管理與變動的益處。

首先,我習慣在電腦硬碟的作業系統槽(通常是 C 槽)之外,選擇一個空間足夠的磁碟槽,在根目錄下開啟幾個資料夾進行專案的管理:

  • Codespace:專案區,只存放由 CVS 或 SVN 等版本控制系統取出的各項專案。
  • Workspace:工作區,存放各項測試中的程式、待完成的工作項目,以及各種經常需要查閱使用的文件及資源。
  • Temp:暫存區,存放各種暫時物件,包括從網路上下載的安裝檔,以及檔案解壓縮後的資料夾等等。

專案區中的資料,應該經常與版本控制系統保持同步化,盡可能減少修改後的版本存在於自己電腦中的時間。即使個人的工作硬碟不幸遭遇毀損的狀況,也能夠由版本控制系統的伺服器中,重新取回整個專案的資料。而工作區,則用來存放各項試驗中的專案;例如在剛開始接觸 Boost.Pool 函式庫時,我就會在工作區裡開設一個測試資料夾命名為 TestBoostPool 進行各項測試,等到成果達到一定程度後,再將專案提交至版本控制系統以及專案區中存放。最後,在暫存區中的資料,則只是便利暫時性的使用,可以隨時將全部的內容清空刪除。

確立了磁碟槽中的工作目錄結構之後,接下來的關鍵要點就是在於遊戲專案的目錄結構規劃。最終規劃完畢的專案目錄結構,一定要達到便利而易用的目標,最好不要產生太深太多層的目錄,但又要能夠維持各個目錄之間的獨立性。一開始,先按照美術設計、企畫設計與程式設計的工作產物,大致上將目錄分為三項:

  • Asset:遊戲素材區,存放美術設計者製作的各種素材,以及音效、音樂、字型等等遊戲資源檔案。
  • Document:遊戲文件區,存放各項設計文件,包含企畫設計、美術設計以及程式架構的相關文件在內。
  • Source:遊戲程式碼區,存放遊戲專案的程式碼,其中再依不同子專案,分成不同的子目錄。

然後在最外層的資料夾,應該以遊戲專案的名稱,或者某個獨特的專案代號做為命名。按照上述的架構,所規劃出來的專案目錄結果如下所示:

<ProjectName>/

    Asset/
        Model/
        Script/
        Shader/
        Sound/
        Texture/
        
    Document/
        Art/
        Design/
        Programming/
        Reference/
        
    Source/
        <Project#1>/
        <Project#2>/
        <Project#3>/

為了便利於遊戲版本的發佈與包裝作業,最好能夠將完整可執行的遊戲資料,包含遊戲執行檔、函式庫檔與動態連結檔等,放置在一個獨立的資料夾中。所以在<ProjectName>目錄下,我們需要再新增一個 Build 目錄。這個目錄中的資料,是由專案執行流程所動態產生出來的結果,其中包含 Debug 與 Release 兩個子目錄分別存放兩種版本的資料。另外,為了預防兩種版本的執行檔被搞混,程式設計者可以在原來的執行檔名稱後,多加一個「_D」做為識別 Debug 版本的方式,例如將 Debug 版與 Release 版的執行檔分別命名為 Game_D.exe 與 Game.exe。

有些人或許會有疑問:「如果執行檔在 Build 目錄下,要怎麼順利存取 Asset 目錄中的遊戲素材?」其實很簡單,以 MSVC 的專案為例,只要在專案屬性的「除錯」頁面中,將「工作目錄」設定為 Build 所在的目錄,接著在程式中設定存取素材的檔案路徑,這樣一來,當程式設計者將專案建置完畢按下 F5 鍵時,就能夠正確無誤地執行程式了。

在開發與除錯階段時,將執行檔以及遊戲素材分開置放在 Build 與 Asset 目錄中,的確可以簡化一些繁瑣的資料搬移步驟,但是對於正式釋出的遊戲版本,程式設計者是否需要重新設定執行檔與素材檔案的相對路徑?並不需要。只要利用以下的方式,就可以一次滿足 Debug 與 Release 兩種建置版本的需求:

#ifdef _DEBUG
    // Path settings for debug build
    static const std::string ASSET_PATH = "../../Asset/";
    static const std::string MODEL_PATH = "../../Asset/Model/";
    static const std::string SCRIPT_PATH = "../../Asset/Script/";
#else
    // Path settings for release build
    static const std::string ASSET_PATH = "./Data/";
    static const std::string MODEL_PATH = "./Data/Model/";
    static const std::string SCRIPT_PATH = "./Data/Script/";
#endif

到目前為止,看起來一切都還不錯,但如果只是把遊戲專案分成 Asset、Build、Document 與 Source 四個目錄,仍然不足以達到最佳的專案目錄規劃成效。

除了內部自行開發的程式碼以外,很多時候在遊戲專案中,也會使用第三方團體所提供的程式碼,為了不與我們自己所寫的程式碼專案搞混,以利後續進行各種維護或者升級程序,所以應該將這些程式碼或函式庫獨立出來,在<ProjectName>目錄下增加一個 ThirdParty 子目錄放置這些專案。在個別的專案目錄下,可以再進一步分為 bin、include 與 lib 三個子目錄,分別存放可執行檔、表頭檔以及函式庫檔案。

另外一種處理第三方團體程式碼的方式,是直接安裝在系統槽的目錄,例如 C:\Program Files\ 之中。就我而言,如果像是 Boost C++ Libraries 或者 Lua 這一類比較固定不變,程式設計者也不會自行修改的函式庫,我就會直接安裝在系統槽目錄中;而如果是為了個別專案的特殊需求,有自行做出修改的第三方函式庫,就適合置放在專案資料夾的 ThirdParty 目錄中。

在 Visual Studio 的預設設定中,會將專案的 Project 檔 (*.vcproj) 存放在與原始碼檔案相同的目錄下。然而這樣的做法,對於具有跨平台需求或者更換程式環境需求的專案來說,可能會造成一些額外的困擾。因此我們可以把與程式專案工具相關的 Solution 檔 (*.sln) 與 Project 檔,分離到獨立的目錄中,在<ProjectName>目錄下新增 Project 子目錄,依不同的程式環境分成不同子目錄,例如 Msvc80、Msvc90、CodeBlocks 與 XCode 等等,就能夠非常便利地增加新的設定了。

有時候,我們會需要在不開啟程式專案工具的情況下,重新建置整個遊戲專案。為了要達到這樣的目標,會需要撰寫一些命令列指令、Shell Script,或是其他專門的建置工具語言。所以同樣可以在<ProjectName>目錄下,新增一個 Script 目錄,其中再依不同的作業系統平台,例如 Win32、Linux 與 Mac,分成不同的子目錄。

將專案的 Project 檔與建置 Script 抽離了以後,Source 目錄就會變得非常單純,依照不同的子專案分成數個子目錄,其中僅僅存有 *.h、*.cpp 以及 *.inl 檔案而已。另外,還可以將程式專案的「中介檔案目錄」指定到 Build 目錄中,因為建置過程中產生的 *.obj 等相關檔案,不需要保存下來,所以可以置放在動態產生的 Build 目錄中,更進一步保持 Source 與 Project 目錄的簡化與純淨。

最後,在<ProjectName>目錄下新增 Tool 子目錄,將一些經常會用到的工具程式,例如遊戲編輯器、角色檢視器、資料轉換器等等,置放於其中以利使用。

遊戲專案目錄規劃的最終版本如下:

<ProjectName>/

    Asset/
        Model/
        Script/
        Shader/
        Sound/
        Texture/
        
    Build/
        Debug/
        Release/
        
    Document/
        Art/
        Design/
        Programming/
        Reference/
        
    Project/
        Codeblocks/
        Msvc80/
        Msvc90/
        Xcode/
        
    Script/
        Linux/
        Mac/
        Win32/
       
    Source/
        <Project#1>/
        <Project#2>/
        <Project#3>/
        
    ThirdParty/
        <Library#1>/
        <Library#2>/
        <Library#3>/
            bin/
            include/
            lib/
            source/
            
    Tool/
        Converter/
        Editor/
        Viewer/

只要按照上述的步驟執行,完整的遊戲專案目錄就大功告成囉!一套簡單且易於使用的遊戲專案目錄結構,不只能夠省去許多手動搬移檔案以及刪除檔案的程序,更能協助我們把各種資料妥善分類,將它們分配存放到正確的歸所。請不要再讓你的文件、檔案、資料夾與專案,走失在電腦裡的某個角落了!

你覺得這樣的規劃如何?合乎你的使用需求以及專案現況嗎?或者還有什麼可以修改之處?歡迎提出來一起討論喔~ :D

8 thoughts on “遊戲專案的目錄結構規劃”

  1. 我有些習慣/想法可以討論一下:

    1. 一些 thirdparty 的 EXE/DLL,如果不是在 src/thirdparty 裡編譯出來的,會直接放到 project 裡,例如 D3DX 的 DLL、一些 .Net DLL ,適合 VC2008 用的版本會放在 project/bin/win32_vc80。並且在系統的 PATH 加上這個目錄。那麼不同的使用者/server checkout 時也會得到所需版本的 DLL/EXE。而項目裡生成的 EXE/DLL 也放到這個目錄,OBJ 等檔案則在臨時的 build 裡。

    現時這個科案的一個特例是 SWIG。因為 SWIG 不單有 EXE,還有其他檔案,所以我把它當作 compiler 一樣,需要自行安裝。

    為了跨平台而設的目錄名稱,我現時用 _,因為同一個平台可能支持平同的 compilers,同一個 compiler 又會支持不同平台。

    而如果是 static link 到項目的,又是開源的,我會放到 thirdparty 裡。例如 Lua 雖然是很穩定的代碼,但我會為每個 platform/compiler 設立 project/makefile,並且有機會要修改代碼,例如刪去不會在遊戲使用的 Lua libraries。

    2. 最大的問題,我覺得仍是否把 asset 和 source code 放到同一個 repository。我現時雖然也是把它們 (只是一些測試用的 asset) 放在一起,但是如果真的在 production 階段,repository 會不好管理。例如很可能會使用一些 build 工具,當有 check-in 時就自動執行 build,檢查每個 check-in 有沒有問題。另外,在看 change log 時不分開也很難看。

    我覺得比較奇怪的是在程式的 debug/release 模式下,使用不同的 asset。如果 asset 是有分製作過程中的,和最終的版本 (例如一些檔案會由 XML 轉為 binary 格式),那麼理想地是 debug 模式可以讀兩種版本,release 模式只可讀後者(或兩者)。不然 binary 格式會不能 debug。我目前是想把 asset 也需要 build 的概念,把來源 asset 轉換為平台特定的版本來執行。

    理想地,一個項目應該可分為項目特有的資料和多個項目共用的資料。例如引擎/工具的 source code 就可能被多個項目(同時)使用。項目的 asset (包括 artwork 等 script) 最好可以分開處理,最好有一些 asset management 的工具。我的 Mil 項目也希望將來加入一些 Asset Management 的方案。

    3. 我覺得一些項目的文件如果可以用一些 Content Management 的工具 (e.g. ShartPoint、MediaWiki) 等統一儲存會比較好。因為這些文件之間沒有很重要的依賴性,不需要 build 的過程,文件也比較小,沒有必要在每台電腦本地留下一個版本。我在家裡的項目則是用 Google Document。

    4. 總結一個簡單的規則也許是,跟據隊伍不同需求來規劃檔案的存取方式。不同職能的 team member 會有不同的需求,除了考慮程序編譯的架構,最重要是如何把資料在不同隊伍中有效地整合。一些團隊會有專門的隊伍去管理資料和流程。

    看到這麼切身的原創文章,我想到甚麼就寫甚麼,回應得比較雜亂無章,請不要見怪。

  2. 這篇很讚喔!(豎拇指)

    這個題材很切身, 每個人都會遇到,
    但是偏偏網路上討論的很少,
    這個 issue 我目前有看過的分析就只有在
    一書中的一小節

    我想大家都很好奇其他團隊或公司的 folder 結構是怎麼設計的
    有機會也來分享一下我所遇過的好了 ^^

  3. 這的確是個問題
    我以前就是隨便把東西丟在桌面上
    想說暫放一下沒關係
    結果桌面就像俄羅斯方塊一樣越來越滿(也不會消掉)

  4. >為了跨平台而設的目錄名稱,我現時用 _,
    oh. 大於小於符號被吃掉了… 應該是 [platform]_[compiler], 例如 win32_vc80. ^_^

  5. 我們公司的做法跟站長幾乎九成是一樣的耶!不過因為我們的art assets會先輸出成xml,再把xml轉成遊戲直接使用的binary files,所以artists做好的東西會先放在另外一個獨立的資料夾,轉好的xml放一個資料夾(轉xml是artist或designer手動),然後轉出來的最終檔案再放到遊戲的workspace裡。

    因為我們是做console game,我們最後run game所需的檔案都會放到一個叫dvd的資料夾,不管是ps2、wii還是xbox 360,遊戲的image或是iso檔就是用這個資料夾去製作了。Dvd裡面(理論上)只會有玩家會用到的東西,不過像PS2的dvd有4G多,要是我們的遊戲用不到那麼多空間,我們會在build iso的時候在dvd的內圈塞進dummy data,讓真正的遊戲燒在外圈上,因為外圈的讀取速度快多了(轉一圈就可以到比內圈多好幾倍的資料)。

  6. @Milo:
    嗯,的確會有「同一個平台支持不同的 compilers,同一個 compiler 又會支持不同平台」的狀況,這點我倒是沒有設想到。同意 [platform]_[compiler] 的作法!

    我沒用過 SWIG,不過我也同意對於比較複雜的函式庫,直接用安裝的方式就可以。而如果會自行修改,或者建立不同平台設定檔的開源專案,自然就應該放在 ThirdParty 目錄中。

    把 asset 和 source code 放在同個 repository 或者分開,兩種方法我都用過,我覺得是各有利弊。目前我也是採用將兩者放在一起管理的方式。

    有關於 Asset 管理的部分,在文章裡沒有解釋的很清楚,這裡再補充一下我的想法:

    除了將 text 轉為 binary 格式以外,還有另一種情況會有兩種不同狀態的資料存在——就是把原來單獨存在的多個檔案或者整個子目錄,打包成一個 Zip 格式的檔案。所以我認為,在專案 Asset 目錄中放的是 debug 用的素材檔案,可以是 XML、binary 或者其他格式,只要是能讓程式設計者方便進行除錯的格式即可。藉由檔案路徑的設定,遊戲程式在除錯時就可以順利讀取 Asset 目錄中的這些資料。

    而當要建置專案的 release 版本時,可以利用 post build event 的設定,在執行檔建置成功後執行一份自訂的腳本。在這份 Script 中,可以利用 Tool 目錄下的工具,批次化地自動將 Asset 目錄中的資料,全部轉換為最終的格式。而轉換後的資料,會直接產生在 Build 目錄中,以保持 release 版本的執行檔與遊戲資料的正確相對路徑。

    怎麼會見怪哩。有關 Asset/Content Pipeline Management 的討論議題真的非常少見,而且我也不太熟悉,所以能夠收到你這麼深入的分享真是求之不得啊~

    @yuxioz:
    對啊,我也很好奇怎麼很少見到書籍文章討論這個議題。

    請用力分享你看過的作法吧~(伸)XD

    @EYE:
    不會消掉的俄羅斯方塊!真是個超讚的比喻! XD

    不如來寫個程式,幫使用者自動清除桌面上連成一線的圖示好了。(誤)

    @lsk:
    沒想到相似程度這麼高。 @_@

    我也認同應該把美術製作完成的 raw asset,放在另外一個獨立的目錄中管理。因為由美術軟體存檔後產生的原始模型檔、動畫檔或者材質檔,不僅容量大而且數量也多,如果一起放進遊戲專案的目錄中,反而會造成管理上的困擾。所以應該只需要把轉換之後,可供遊戲程式直接使用的 game asset 丟進專案的 Asset 目錄中就可以了。

    原來在遊戲光碟片中燒進 dummy 資料的用意是這樣啊!又學到了一項新知識哩。 :D

    非常感謝各位的分享~ ^^

  7. 恰逢新學期開始就看到這篇文章,真是讓在下大受啓發!

    不過我以前所做的東西之中,asset還會繼續分爲working和complete兩個部分,美術人員可以一直在working目錄中工作,而最後遊戲運行則使用製作完成放在complete中的部分。

  8. @Wxy:
    我認為,你所提的這個作法應該比較適合使用在小型的遊戲專案上。這個方法的優點是可以將所有與遊戲專案相關的東西,全部收納在同一個 repository 中取用。但是這項優點也同時是缺點。對於程式與企畫人員來說,其實並不需要取得處於 working 狀態的美術資料。如果使用了這個作法,就會讓所有成員都必須取得與維護很大一包的專案資料夾。

    謝謝你的回應。 :)

Leave a Reply