天天看點

前端?請叫我遊戲開發

本文轉載自公衆号:流利說技術團隊

前端?請叫我遊戲開發

一、背景

半年前項目組決定開發一款遊戲化的 app。但是組内還沒有遊戲開發人員。作為一個前端工程師,還算熟悉号稱“鎮後端”、“鎮用戶端”的 JavaScript。遂果斷跳入遊戲開發的坑中。這篇文章從比較 general 的方面對比了前端開發和遊戲開發的一些差別,算是這段時間工作的一個總結,希望更多前端小夥伴們也可以嘗試下遊戲開發。

二、引擎選擇

市面上存在的遊戲引擎有很多,比如 Unity,Cocos2d-x,Egret 等。最終選擇了 Cocos Creator,原因主要有幾下幾點:

  • 跨平台,平台無關的代碼共用,可以提高開發效率
  • 使用 JavaScript/TypeScript 作為開發語言,對于前端比較友好。對 ES201X 文法支援比較完善。
  • 1.x 版本(筆者開始做時還未釋出2.0版本)是基于 Cocos2d-x 實作的。文檔資源和社群環境都比較成熟,便于上手。
  • 編輯器提供了內建式的開發、設計環境。最大程度上提高了程式員和設計師的合作效率。
  • 支援多種平台,可移植性比較好
本文以下部分提到的”遊戲“均指 Cocos Creator 引擎,其他遊戲引擎可能會有所不同。

三、遊戲和前端開發的差別

1、共同點

  • 主要程式設計語言都是 JavaScript/TypeScript,JavaScript 中常用的 deisgn pattern 都可直接使用
  • 使用 prettier 進行代碼風格管理,使用 tslint/eslint 對進行代碼品質管理
  • 使用 jest 進行單元測試
  • 基于元件。元件在各種前端架構中很常見,同樣遊戲中也是基于元件封裝隔離各個子產品的。當然遊戲中的元件跟前端架構中的元件還是有很大差別的,後面會專門介紹。
  • PM還是會不斷地改需求😂😂

2、不同點(技術方面)

資源(Asset)

遊戲的代碼倉庫裡資源占了很大一部分比例,這些資源的組織和使用方式跟前端還是差别很大的。

  • 遊戲工程裡幾乎所有的東西(包括代碼、檔案夾)都是資源。這裡的資源是面向引擎而言的,而非面向開發者的。
  • 遊戲中用的資源種類非常多,對于不同的資源的加載使用方式略有不同。前端通常使用不同的 HTML 标簽來加載對應的 DOM 來處理,遊戲裡主要是通過不同的内置元件來。
  • 遊戲中所有的

    ./assets

    目錄下的資源和檔案夾,編輯器都會自動建立一個對應的

    .meta

    檔案。該檔案定義了該資源的 uuid、版本資訊等。這裡需要注意一個常見的問題,git 不會管理空檔案夾,遊戲編輯器卻會為這個空檔案夾建立一個 meta 檔案,此時遠端工程可能會有一個沒有資源與之對應的 meta 檔案,進而導緻 ci 編譯失敗。
  • 遊戲中使用資源的方式有兩種。靜态索引和動态加載。前者通過在渲染的節點元件上設定屬性,指向資源的id,在節點加載的過程中加載資源。後者是在代碼中根據資源路徑動态加載。動态加載的資源必須位于工程的

    resource

    目錄下。因為引擎打包時預設會打包所有

    resource

    目錄下的資源和其他目錄中被靜态引用的資源。前端(web環境)的資源都是通過網絡加載的,是以不存在這個問題。

工程目錄結構

遊戲中一種比較常用的工程組織結構是按照資源的種類進行劃分。比如

/imgs

/audios

。這種組織方式在比較小的項目中問題不大,但在大的項目中會有如下缺點:

  • 多人修改同一目錄,非代碼檔案(

    .meta

    .fire

    .prefab

    )的沖突會增多,解決起來非常難受
  • 過于扁平,不能反映出項目子產品的關系,疊代幾次後項目會變得非常混亂
  • 在我們的實踐中其實借鑒了前端項目中的工程目錄方式,
  • 這裡

    assets

    相當于前端工程慣例中的

    src

    目錄;
  • foundation

    相當于

    services

    或者

    utils

    ,放置一些公用(全局)的類庫。
  • modules

    相當于

    pages

    ,存放的是場景(相當于前端的頁面)。
  • resources

    則相當于

    components

    ,存放是在頁面中引用的元件。每個元件目錄下都有對應的腳本、圖檔、音頻等資源。
assets
├── foundation
│   ├── common
│   ├── lib
│   ├── protos
│   └── utils
├── modules
│   ├── activity
│   └── roadmap
└── resources
    ├── activities
    ├── roadmap
    ├── sessions
    └── start
           

元件設計

用慣了 React、Vue 之類的前端架構之後,元件就是一個輸入資料、輸出UI的子產品,開發的過程中很少去操作 Component,component 的建立銷毀幾乎都由架構完成。開發者的工作重心更多的是如何将多個代表元件的元素(Element)拼接起來。他們之間通過 props 或者事件(Vue)來通信就好了。元件的開發和使用都是偏聲明(函數)式的,更關注資料的流動。

而在遊戲中的元件,隻有在其他元件擷取到了這個元件的引用并使用元件的方法使之完成某個任務才有意義,是偏面向對象的,更關注邏輯的依賴。

其次。前端架構的元件裡需要關注UI的渲染,即輸出的(虛拟)DOM結構。而遊戲元件隻專注邏輯,渲染則需要開發者手動在編輯器中建立節點,遊戲引擎通過解析節點樹及綁定在節點上的腳本元件來完成的。

舉個🌰

遊戲腳本元件

class Prarent extends cc.Component {
    son: Son = null
    letSonRun () {
        this.son.run()
    }
}
class Son extends cc.Component {
    label: cc.Label = null
    run () {
        this.label.string = 'Running'
    }
    stop () {
        this.label.string = 'Stopped'
    }
}
           

React 元件

class Parent extends React.Component {
    letSonRun () {
        this.setState({ isSonRun: true })
    }
    render () {
        <Son isRun={this.state.isSonRun}>
    }
}

class Son extends React.Component {
    render () {
        return this.props.isRun ? <div>Running</div> : <div>stopped</div>
    }
}
           
前端?請叫我遊戲開發

樣式/适配

  • 前端的樣式是通過 CSS 實作的,遊戲的樣式則是建立在一套坐标位置體系和一些内置的元件上的。
  • web 頁面中元素的位置在自然狀态下是流式排布,即結構中後來的元素在已有元素的後面(下面)。而遊戲中則需要每個元素都給定一個基于笛卡爾坐标系的位置。預設位置是(0, 0)
  • 相對于 css 的

    font-

    屬性,遊戲提供了 Label 和 RichText 元件
  • 相對于 css 的定位屬性,遊戲提供了 Widget 元件
  • 前端針對移動端的适配方案多采用自适應彈性布局。除了可以使用塊級元件的流體特性外,還可以使用 FlexBox、Grid,em 等實作更為精細複雜的适配。而遊戲中的彈性是天然自帶的。在遊戲工程中需要設定頂層節點(Canvas)的設計分辨率,并指定是按照高度還是寬度适配,這裡先假定按照高度适配。設定完成後,最終 canvas 的高度就會撐滿螢幕,然後寬度按比例縮放。當然這種适配方式隻能根據螢幕做等比縮放,還是很有局限性的。比如多個子元素在固定大小父元素下的彈性布局,遊戲提供了 Layout 元件(類似 flexBox)解決這個問題。在進行更為精細的螢幕适配和彈性布局,需要手動對節點進行縮放。

測試

前端的測試生态已經很完善了,從單元測試、內建測試、到 E2E 有一整套的工具可以拿來用,不同的架構也會有響應的測試工具包提升寫測試用例的效率。但是遊戲社群内幾乎未找到現成的測試架構、工具。

基于遊戲引擎跨平台的特性,我們正在試驗一種測試方案:

  • 把測試分成兩種類型:一種是跟 UI 無關的純邏輯功能測試;另一種是需要驗證 UI 的測試。
  • 對于第一種情況。我們可以借助前端現有的測試架構來做單元測試。
  • 對于第二種情況又可以分成兩種情況來做。
  • 對 Native 平台沒有依賴的子產品完全可以将代碼跑在浏覽器環境下做內建測試
  • 對 Native 平台有依賴的的子產品可以搭建一個 Electron 環境,模拟 Native 環境,完成內建測試

Git & Code Review

  • 遊戲工程中會有很多的大體積資源檔案,比如圖檔、音頻甚至是視訊。這些檔案的 git 變動會導緻我們的遠端倉庫體積快速膨脹。是以我們使用

    git-lfs

    來管理這些大檔案。它将你所标記的大檔案儲存至另外的倉庫,而在主倉庫僅保留其輕量級指針。 那麼在你檢出版本時,根據指針的變化情況下更新對應的大檔案,而不是在本地儲存所有版本的大檔案。
  • 在資源部分我們提到導入資源時,會為每個資源建立 meta 檔案。同時在遊戲項目中還有場景(fire)檔案、預制(prefab)檔案。這些檔案都是編輯器為我們建立的,雖然都為文本,但是可讀性比較差,在 Merge Request 中的進行 Code Review 的意義不大。這裡我們采用 @melon 同學開發的 chrome 插件 gitlab-reviewer 折疊某些字尾的檔案内容,隻閱讀代碼和配置檔案等有價值的部分,極大地提高了 code review 的效率。

周邊工具

遊戲的工具鍊比較長,尚未窺得全貌,這裡隻說幾個平時用的比較多的吧。

  • TexturePacker Texutre 是遊戲裡面對圖檔的稱謂。顧名思義,這個工具就是打圖集。圖集在遊戲裡的意義前面已經說明了。
  • Tiled Map Editor 是一款開源的地圖編輯器。主要用于規劃地圖上的點位、路徑及相關的資料。有地圖的遊戲應該都離不開
  • dragBones 一款骨骼動畫設計工具,主要供設計師使用,開發隻需要了解即可
  • particle designer,一款粒子編輯器,主要給設計師用
  • Hiero 位圖編輯器,用于生成字型位圖
  • tiny-png CLI 圖檔壓縮工具,可以批量壓縮圖檔,減少包體積利器
  • ffmpeg 音視訊處理工具,主要用于壓縮音頻,減少包體積利器

3、不同點(非技術層面)

與設計師的合作

通常對于前端來說,設計師給出互動稿和視覺稿就可以動工了。但是對于遊戲來說還遠遠不夠。各種粒子特效、幀動畫都需要設計師來做。好在遊戲編輯器提供了一套對設計師友好的設計工具。設計師可以直接在編輯器中進行設計,設計輸出可以推送到倉庫中直接給開發使用,避免了開發從設計到開發的重複實作過程。

但是開發和設計師事先規定一套良好的設計規範仍然非常重要。

  • 命名規範。設計師命名都比較寫意,從開發角度 camelCase, PascalCase, kebab-case 還是要提前規範好,所有的資源檔案名應能直接看出其用途
  • 元件拆分。每次新的需求需要開發和設計師共同定義要拆分的子產品的動效。避免開發過程中對設計師已經做好的特效再重新拆分重組,浪費時間。
  • git 規範。沒錯,我們的設計師會用 git,但他們可能在這個項目之前并沒有用過,是以所有 git 規範都應該提前定義好,git-flow 和 commitlint 都要提前設定好😂

四、後記

經過将近半年的疊代,我們的項目已經走過了5個版本,疊代也日趨穩定(插播一條廣告,各大應用市場搜尋“ 少兒流利說 ”即可看到我們的産品)。這是一個 Team 團結合作的結果,在這裡感謝所有為遊戲項目貢獻力量的開發小夥伴們:

  • 我們的 Tech Leader 在項目初期在架構上以其深厚的項目經驗給出了很多寶貴的指導和建議,讓項目在初期少走很多彎路。在日常工作裡也負責很多跨部門的協調工作,可以讓我們更專注在開發上,感謝🙏;
  • 我們的 Team 後來也慢慢有了更多、更專業的遊戲開發工程師,給後來的遊戲開發提供了強有力的支援,也給我個人的遊戲開發技能提升提供了很多幫助,感謝🙏;
  • 我們遊戲還是運作在 Native 平台上的,用戶端的同學在完成産品需求外,在遊戲的編譯、打包工具方面提供了大力支援。現在偶爾也來支援寫遊戲,感謝🙏;
  • 最後,還有和我一樣身為前端工程師還奮戰在遊戲開發前線上的小夥伴,和你們一起寫代碼、code review 是一件非常開心的事,感謝🙏。
前端?請叫我遊戲開發

文章已于2019-03-12修改

繼續閱讀