使用Cocos進行2D和3D混合開發

這是異名上手cocos後,正式開發上線的第一款遊戲,用的引擎版本是v.2.2.0,遊戲一共有6個場景,其中一個3D場景,4個2D場景,在這裏做簡單的一個覆盤和回顧,因爲需要我們平臺的賬號登錄就不能給大家開放試玩了。

項目構建流程

目前來看暫時還沒有要引入第三方構建工具的必要。除了幾個配置的js和簡單的css,合起來就十幾K,引擎自身還支持按模塊拆分,打包之後引擎也做了混淆壓縮和命名hash處理,像精靈圖合併和圖片壓縮目前也有基於引擎的成熟解決方案。另外這個遊戲是屬於ipad端的遊戲,相對於微信小遊戲和H5小遊戲,一個是因爲它有足夠的代碼存儲空間,所以不涉及到資源的拆分問題,二個是因爲是頁遊,我們假定都是在WiFi場景下使用iPad,所以它對流量的要求也沒這麼苛刻。因此在這個項目,我只基於引擎做了下面這幾個常規處理:

  • 圖片生成圖集

  • 自定義發佈模版

  • 圖片做壓縮

圖集管理

圖集這裏我主要是利用了引擎自身提供的自動圖集功能,它會把當前文件夾下的所有 SpriteFrame作爲碎圖資源,所以我們只需要把不同圖集的資源劃分文件夾,然後在文件夾下生成自動圖集對象就可以了。當時在開發的時候其實我還是配套使用TexturePacker來處理一些小圖集,現在回過頭來看其實完全可以棄用它,自動圖集能夠更精簡我們的工作流,想要理解這個工具的同學可以看我的上一篇文章。

自定義發佈模版

針對發佈後的項目,我有兩個更改需求:一個就是更改網頁的title,另外一個就是更換引擎加載時的logo;這塊主要是可以通過自定義發佈模版來是實現,複製一份打包之後的index.html,然後更換裏面的logo鏈接和title,然後放到build-templates裏面再重新打包一次就可以了。因爲是原封不動地挪動build-templates裏面的文件去覆蓋打包之後的文件,所以在使用過程中有個問題就是當你需要打包vconsole進去的時候,你就得動手去修改模板了

圖片壓縮

這一塊我使用了社區裏面的一個插件,它 集成了pngquant 進行圖片壓縮,目前只能實現png壓縮,先打包,然後通過工具去遍歷打包之後的目錄下面的圖片,壓縮並覆蓋。另外因爲我司有自己的發佈系統,它對web項目有一套自己的打包和發佈流程,而引擎打包之後我需要做個重命名才能走我們的發佈流程,所以我還對這個插件做了二次定製,改造之後的插件界面如下,發佈之前只需要在構建後,按順序執行下面兩步就可以了:

場景加載

因爲這個遊戲有多個場景,還有一個3D場景,所以需要專門做場景的加載。我是直接在主場景上實現的,當玩家點擊在進入下一個場景的時候,通過cc.director.preloadScene去預加載下個場景,並且獲取加載加載的進度,然後渲染到頁面上,等到場景加載完畢之後就跳轉過去。

function toPreloadScene (sceneName: string, jump: boolean = false) {
    cc.director.preloadScene(sceneName, (completedCount, totalCount) => {
      this.loadProgressBar.width = (completedCount / totalCount) * this.loadProgressBox.width;
      this.loadProgressLabel.string = (completedCount * 100 / totalCount).toFixed(0) + '%';
    }, (error, asset) => {
      Store.stageInfo.find((item:checkpointItem) => item.sceneName == sceneName).isLoaded = true;
      jump && cc.director.loadScene(sceneName);
    });
  }

具體效果如下:

嚐鮮3D場景

遊戲本身是針對小朋友的,玩法比較簡單,這個場景的主要玩法是通過維持手指的點擊頻率,讓玩家角色順利到達終點????

做這個項目的時候,引擎已經開始支持3D模式,和我合作的設計師也有在私底下學習C4D,所以我們一拍即合決定嚐鮮一下。我是基於v2.2.0進行開發的,這個版本對3D的支持很有限,最新的v2.3.0版本出來後,對3D的支持更好了,所以下面的經驗僅供參考

模型導入

我直接把設計導出的.fbx模型拖進引擎,大部分材質就直接應用到畫面上來,部分材質的綁定可能會丟失,自己手動綁定就可以了。這塊的實踐過程我們主要遇到下面幾個問題:

  • 模型無法正常導入。這塊的排查原因就是模型裏面用來中文命名,把模型的中文命名改掉就可以了。

  • 相機節點無法同步。設計師其實已經在C4D裏面調好了相機的視角,導進引擎之後,相機節點不會自動綁定,所以後期需要我們自己重新調整。

  • 模型太大。同樣的場景,3D的模型大小可能是圖片的五六倍以上,爲了壓縮模型可以讓設計師把一些不必要的網格合併了。

渲染效果

初次導入模型之後,我們發現渲染的效果差強人意,下圖左邊是我們實際的效果,右邊是C4D內的渲染效果

上面這張對比圖可以看出幾個問題:

  • 人物以及場景的渲染不到位。這個確實是我們缺乏經驗,模型導出前需要設計師先給模型烘焙貼圖,後面再導出,前後對比效果就能夠提升不少。

  • 環境光不到位。這個沒辦法了,暫時只能將就

  • 沒有陰影。引擎其實已經是支持設置陰影了的,但是嘗試之後我們認爲有無陰影對效果影響不大,所以我爲了節省模型大小,就讓設計師把場景上所有的東西都合併了,就沒有專門去設置陰影。

還有一個問題就是2.2.0版本還不支持天空盒,但是對我們的影響不大,因爲調整了相機之後,用戶只能看到正前方,所以我就用了一個純色的面板放在前面代替。

模型操控

在3D模式下調整相機視角和位置的操作是相對來說比較困難的,之前的項目中也實現過一個2.5D的場景,所以也這裏也分享兩個快捷鍵,一個就是調整物體的時候,點擊住屏幕,在視圖左上角會出現wasd(上下左右)的提示,可以通過鍵盤上wasd這四個按鍵來微調方向,相對來說會便捷很多

還有一個就是調整相機的角度,如果我們根據預覽效果去調整相機的位置,實際操作起來有一定的麻煩,有一個隱藏的組合快捷鍵control+shift+f可以把我們場景編輯器的畫面同步成相機的預覽,它可以讓我們更直觀得調整相機效果

玩法實現

核心玩法就是在點擊回調中動態改變玩家的位置,這裏面還涉及到兩個問題,多個相機的應用以及相機跟隨。首先在佈局的時候,Canvas節點和Main camera主要是用來顯示2D物體,所以我把2D的物體都放Canvas節點下。而3D模型我會新建一個獨立節點來存放,然後新建一個相機來專門拍攝3D物體。這樣子我們就有兩個相機了,這裏需要理解一下關於相機的幾個知識點:

  • 遊戲引擎輸出畫面的時候會把多個的攝像機的圖像疊加起來;

  • 攝像機會繪製屬於自己渲染分組裏面的物體,所以我們需要合理分組給每一個攝像機選取拍攝具體要拍攝的物體

  • 根據攝像機的成像原理,有兩種模式的攝像機。在3D模式下,我們需要那種近大遠小的效果,這種錐形的成像模式指的就是透視投影模式;而正交投影則適用於2D模式。

  • 攝像機有一個depth屬性, depth小的先繪製到屏幕, depth大的後繪製到屏幕。2D的UI界面在遊戲的最上層,所以我們要將拍攝UI攝像機的 depth調到所有相機的最高處;

  • 攝像機有—個clearFlag,如果你設置了,當他繪製畫面的時候就會清理屏幕。因此我們只給第一個攝像機也就是拍攝2D的攝像機設置 clearFlag;後面拍攝3D模型的相機就不再設置了,不然會把前面攝像機繪製的內容清除掉。

然後玩家的視角跟隨,我是直接把拍攝它的相機節點放到玩家節點內,這樣改變玩家位置的時候,相機也就會跟着移動了

2D滑行遊戲

這個遊戲裏面有一個場景的玩法是滑行遊戲,玩家通過左右按鈕控制人物的方向,在滑行的過程中需要繞過特定的障礙物,然後到達終點就算遊戲結束。如下圖所示:

選擇參照物

滑行其實有兩種是實現方式,主要看是應該選誰爲參照物

  • 相機不動,畫布移動(相對以屏幕爲參照物)

  • 畫布不動,相機移動(相對以畫布爲參照物)

理論上兩種方式都只是參照物不同而已,最終實現的效果應該是一樣的,並沒有誰優誰劣,看需求而定。但是如果遇到要使用物理引擎,選擇的時候需要考慮剛體的位置同步。比如父子兩個元素:子爲剛體,父不是,希望父移動的時候子也跟着移動,但是引擎的實現效果是父移動的時候子不動,原因如下:

  • 物理組件類似一個獨立的容器,它的位置只和物理世界裏面它的位置有關與非物理世界的位置變化無關。因此父動的時候,因爲父不是物理組件,它在物理世界中的位置是不變的,雖然畫面上父移動了,但是子是物理組件,子不會跟着動

  • 硬要動,可以讓子記錄一個初始位置,然後在update的時候,重設node節點的位置(覆蓋其初始值),同時手動調用syncPosition來更新它在物理世界的位置。(之前實現的時候效果並不理想,但是引擎重新編譯後暫時無法復現)

    onLoad () {
        this._pos = this.node.position;
    },
    
    update (dt) {
        this.node.position = this._pos;
        this.getComponent(cc.RigidBody).syncPosition(true);
    }

在起初調研階段其實我有用到物理引擎,但是後來仔細一想,我的這個遊戲場景其實只要使用到碰撞組件就可以了,所以就沒有引入物理引擎,最終選擇了相機不動,移動畫布的實現。

背景無限滾動

同時因爲賽道的距離可能會很長,所以背景的畫布節點應該還是通過程序去動態拼接比較好,具體的實現方式其實就是很經典的輪播圖。

這裏有兩個適配注意點,一個是背景圖的高度不能是768,因爲每次距離疊加之後還是會產生誤差,如果剛好背景圖的高度就是屏幕的高度的話,這個小誤差就會產生很明顯的空白間隔線,所以最好就是768是內容區,然後上下得各留10px空白像素區,這樣子的話挪動背景圖片的時候就不會有漏出的情況了。還有一種情況因爲我的背景節點有賽道,所以我的背景節點是不能做拉伸的,因爲拉伸之後就有真實檢測的位置和視圖不一致的情況。那就只能有一張固定大小的背景圖,但是當用戶屏幕寬度很長的時候,左右就會產生黑邊,因此我們做了一個處理,就是底層節點做成widget拉伸的純色的節點,然後賽道圖是固定大小的圖片,賽道圖片的兩邊顏色剛好可以和純色接壤,這樣子就算用戶的屏幕很寬也不會有黑白了。

檢測繞過障礙物

因爲遊戲的玩法裏面要求統計玩家繞過障礙物的次數,所以我需要精準地檢測玩家是否繞過了該障礙物,前期定了兩個方案,一個是劃分圓形的碰撞區域,然後統計玩家經過了哪些碰撞區域。第二個方案就是把圓形的碰撞區域簡化成三條射線。

目前選用技術方案二,因爲射線是物理系統的一個子功能,如果引入物理系統話會和上面的滾動方案有衝突;而且射線沒有debug視圖,調試起來也會比較麻煩;後面物體移動之後還會涉及到物理世界座標和節點位置座標的轉換。所以在項目中我就使用矩形碰撞框來模擬碰撞射線,具體效果如下

其他備忘

在需求的實現過程中,我還遇到了一些比較典型的現象,也記錄一下解決方案

Safari全屏模式斷網後,卡死

在Safari全屏模式下,如果自動判斷斷網刷新或者自動跳轉鏈接的話,是會卡死的,全屏模式無法退出,同時因爲斷網了,也無法刷新和跳轉新網頁,解決方法就是判斷斷網後,先手動退出全屏,然後再刷新網頁和跳轉鏈接

if (navigator.onLine === false) {
  cc.screen.exitFullScreen();
  window.location.reload();
}

Animation恢復到動畫的初始狀態

設置動畫到第一幀的時間,再停止

this.longPressBtn.getComponent(cc.Animation).setCurrentTime(0);
this.longPressBtn.getComponent(cc.Animation).stop();

字體模糊

本質原因是因爲字體的底層也是texture,它的渲染模式和畫面上其他的圖片的渲染模式不一致,所以在混合渲染的時候位圖字體就會產生模糊現象,目前在論壇上看很多人都有反映情況但是並沒有根本的解決方法

  • 如果是label 可以通過添加labelOutline 組件設置邊框爲0.3 來欺騙視覺

  • (推薦)無論是label 還是richText 都可以通過設置大字體,然後設置scale來達到視覺效果

    • 如果本身字號大的話可能縮放之後字體邊緣會有部分被裁。

    • 如果設置了label 模式爲固定寬度縮放模式的話也會有問題,因爲字體變大,引擎自身會先做縮放,這個時候再疊加縮放就會更小了,因此這個時候不建議

  • 社區內也有人進行過引擎層面的嘗試

打包前正常,打包後不正常

去看看項目的打包模塊設置,看看有沒有把對應的模塊打包進去

該系列其他文章

  • 遊戲引擎開發入門實踐

  • 使用cocos進行2D和3D混合開發

原文鏈接: https://juejin.im/post/5e75f4cbe51d4526d87c949f

一個遊戲多份收益,助力開發者技術精進副業掙錢,我是工程師經人:張曉衡,歡迎與我建立鏈接!


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章