TrueSync, 多人完全同步物理引擎入門 (2)

在上一篇文章中, 我們已介紹了如何下載與安裝 TrueSync , 並且用最簡單快速的方式與 Photon Cloud 建立初步的連線, 讓二個 Client 加入到同一個遊戲室(Photon Room)裡面, 至此, 前置作業已準備完成了.

接下來, 本篇會介紹關於 TrueSync 中的網路完全同步的物件生成以及玩家間的輸入操控(User Input) 該怎麼設計以及它們是如何運作, 流程如下:

  • TrueSyncGlobalConfig 以及 TrueSyncManager 的設定
  • Player Prefab 的建立, 並且加上 TrueSync 的物理元件
  • 加上 Player 角色的移動控制程式
  • 讓網路上二個不同的 Client 可以完全同步(lockstep)的處理輸入的指令

TrueSync 參數設定(Global Setting)

Assets 裡的 TrueSync →Unity →Resources 目錄底下有個須要先設定的參數檔: TrueSyncGlobalConfig , 它可以用來設定一些 TrueSync 套件的初始參數值、我們遊戲中想選用 TrueSync 提供的 2D 或是 3D 物理引擎以及它的碰撞層次設定等. 目前我們使用是 2D 專案型式, 如下設定即可~

一般設定, 以及選用物理引擎

TrueSyncManager 的建立

目前的 Game2D 場景很簡單, 應該只有預設的 Camera, 如果有燈光的話, 因為用不到, 燈光可以先刪掉. 接著, 我們用 Unity Menu 裡的 GameObject → TrueSync → TrueSyncManager 的項目建立一個物件, 如下圖所示:

在場景中建立一個 TrueSyncManager 物件.

這個 TrueSyncManager 物件的功能很強, 也是 TrueSync 系統主要管理者, 所以場景一開始就要先載入它, 它會在場景裡透過底層的 PUN 與伺服器建立交互的連線機制、叫用 Photon TrueSync 提供的時脈更新(tick/frame update)與物理碰撞機制(Collision/Trigger)、初始化並載入代表玩家角色的物件等, 有了它就可以讓遊戲場景有完全同步(lockstep)系統模擬機制.

Player Prefab 的建立

當玩家一進入遊戲室中, 須要建立一個角色物件代表該名玩家, 我們稱該元件為 Player Prefab; 這裡我們使用上一篇引入的可愛角色鴨鴨來代表每個玩家, 先將其命名為 PlayerBox2D, 並且指定 Tag 為 Player (方便之後的碰撞判定), 參考設定如下:

代表 Player 的 GameObject Prefab

因為角色之間會須要有物理碰撞的反應, 以及可以接受 User Input 來控制移動, 所以要加上 TrueSync 特有元件: TSTransform, TSRigidBody, TSBoxCollider 等, 這些元件可在 Commponent → TrueSync → Physics 之中看到, 有 xxx 2D 後綴字眼的表示是適合 2D 場景的元件, 如下畫面:

TrueSync 所屬特有的物理碰撞元件, 有 3D 也有 2D 的類型.

因為目前示範專案是用 2D 場景, 所以我們選用的元件為: TSTransform2D, TSBoxCollider2D, TSRigidBody2D 這三個 TrueSync 提供的元件, 並且因為控制方式會由程式來操作, 所以 Is Kinematic 要打勾; 同時, 這個遊戲物件不會受重力引響, Use Gravity 不要勾選. 大致設定如下:

增加了 TrueSync 元件的 Player Prefab

把這個 Player Prefab 指定給上面建立好的 TrueSyncManager 元件之中, 以後只要這個場景一載入, TrueSync 就會自動的以這個 Player Prefab 來建立一個 GameObject(Clone) 來代表該名玩家~ 設定如下所示:

指定了一個代表玩家物件 (Player Prefab) TrueSyncManager

當然我們也可以用程式碼來建立這類型的遊戲物件, 像下一篇的子彈即是動態的以程式來生成; 而 TrueSyncManager 的方式, 則是提供我們比較方便的自動建立機制.

每一次當場景建立時, TrueSync 會為每個連到同一個遊戲室的 Client 建立玩家物件, 而因為 TrueSync 也有 offline 的執行模式, 所以我們可以先來簡單測試一下: 在 Unity 的 Game2D 這個場景中按下 Play, 會看到有個物件自動的生成: PlayerBox2D(Clone), 這樣就表示有設定成功, 如下所示:

TrueSync 自動將 Player Prefab 生成一個代表玩家的物件: PlayerBox2D(Clone)

在 Player Prefab 中增加個 TrueSyncBehaviour 元件

TrueSync 是個完全同步(lockstep)的系統機制, 也就是能同步的東西會像是使用者的輸入(User Input)這類型的數值. 在此系列的教學之中, 我們讓使用者能操控上面的鴨鴨, 所以我們得要加個元件來做這件事~

用來控制玩家角色的 TrueSyncBehaviour 元件

如上, 我們會繼承 TrueSyncBehaviour, 這樣我們就可以接受來自 TrueSync 的系統叫用、更新機制功能, 確保我們的物件元件的變動會在不同的 client 都能看到一樣的結果; 然後, 我們把這個 PlayerMovement2D 加到 PlayerBox2D Prefab 之中.

PlayerBox2D Prefab 增加了 PlayerMovement2D:TrueSyncBehaviour

取得使用者的輸入, 並將其排入佇列中

為了要達成物理碰撞完全同步的設計要求時, 我們無法使用 Unity 原本的設計來取得所有的玩家輸入, 或是同時同步地更動遊戲中的狀態數值.

Lockstep System 完全同步系統, 其工作原理為: 先取得所有的玩家輸入, 然後同時地在各自的玩家的機器上, 執行所有的物理碰撞模擬, 基本上就會在每個 frame 的更新之間, 產生同樣的結果.

所以, 我們必須從 Unity 中, 在某個 tick (frame or update) 時點, 先收集使用者的輸入, 將其排入到 TrueSync 的佇列輸入系統中, 之後 TrueSync 隨即會分送至所有的遊戲端, 包含本機端, 然後在接下來的某個 tick (frame or update) 時點, 將佇列中的值取出來使用.

TrueSync 會呼叫 OnSyncedInput, 我們把 unity 中的玩家輸入, 放入 TrueSyncInput 之中

像上面我們在 OnSyncedInput 中, 取得 Unity 的輸入, 接著將其排入 TrueSyncInput 佇列之中, 而這個 OnSyncedInput 也只有 local player 本機玩家才會被 TrueSync 系統叫用到. 其它遠端玩家也會在他們的機器中被 TrueSync 叫起來執行這幾行程式, 所以不會相衝突. 而且 TrueSync 還會負責分送交換所有玩家之間的輸入數值.

很重要的一點, 我們會把 float 浮點數轉換成 FixedPoint (FP) 定點數, 這也是 TrueSync 特別提供的, 如此才會確保在所有的平台、所有的系統、所有的數列計算時, 其結果皆是相同一致的.

移動玩家角色 (OnSycedUpdate)

現在, TrueSync 系統輸入佇列中已經設定了數值, 並分送至所有 client, 所以接著可以來移動玩家角色(鴨鴨)了. 我們會在 OnSycedUpdate 中做這件事, 它會在某個特定的時點 tick (frame), 由 TrueSync 系統叫起來執行:

TrueSync 會呼叫 OnSyncedUpdate, 把 TrueSyncInput 中的數值, 取出來做一些事

上面的程式中, 我們從 TrueSyncInput 的佇列中取得了 FixedPoint(FP) 型態的輸入數值, 將其與 TrueSync 系統所提供的跨平台且一致的 DeltaTime 以及速度因子(如: speed = 10) 做運算.

目前我們只用到上下以及左右的移動, 所以只要加上變動的位置就好; 
要注意的是, 這裡用的是 TrueSync 的元件 TSTransform 來設定數值, 而非 Unity 原本的 transform 唷~

tsTransform (2D) 是繼承自 TrusSyncBehaviour 即會有的一個 TSTransform (2D) 元件的前置參考變數.

這些步驟是必要的, 使用 TrueSync 系統提供的元件, 才可以確保遊戲中所執行的所有程式碼運算的結果有一致性, 而 Unity 之中(或是其它遊戲引擎)大多數的子系統都是無法做到這點的~

TrueSync 內含了很多的元件, 並且參考了 Unity 的 API 設計, 為的就是讓我們可以很簡單的開發出 Lockstep System 類型的遊戲, 例如: 二組特別設計而且開源(open source)的可預測物理碰撞引擎(2D & 3D), 一組 Transform 元件 (TSTransform), FixedPoint type (FP) 定點數系統, Seeded Randoms (TSRandom) 固定種子型的亂數系統, 以 FP 定點數計算的數學程式庫
( Math Lib: TSVector, TSMatrix, TSQuaternion … ) 等等~

至此, 目前我們完成了一些事, 像是從使用者取得輸入值, 排入佇列, 由 TrueSync 分送至每個 client, 然後在隨後的某個時點 tick 取出輸入值並計算後, 變動玩家角色的位置.

快速簡單的 2D 場景設定

因為是 2D, 簡單設定一下 Camera 就好, 場景中甚至連燈光都不用打.😅

簡單的 2D 場景 Camera 設定
簡單的場景, 連燈光都可以省去~ 😅

測試

我們先來看一下至此所有的流程是否可以成功運作, 請在 Build Settings 的面版中, 加入或勾選 Tutorial2D/Menu2D 以及 Tutorial2D/Game2D 這二個場景, 並且 Menu2D 是第一個選中的場景( Unity 在最開始時會載入它).

建置面板設定

建立好之後, 執行它即為 Player 1,
另外也在 Unity 中按下執行鍵(Play) 即為 Player 2,
正常的情況是可以看到二個 Client 都可以連線到 Photon Cloud, 並且在 Master Client 的畫面中可以看到個 Start 按鈕, 按下它, 二個 Client 就會同時進入 Game2D 這個場景, 並且分別可以用上下左右鍵控制各自的鴨鴨移動~

二個 client 分別可以用上下左右鍵, 來控制各自的鴨鴨移動~

如果成功的話, 我們就可以進入下一部份囉~ 👏🏻

我們將會繼續介紹如何完全同步的發射子彈, 動態地建立物件以及刪除物件, 同步網路數值(遊戲狀態)等等~🎯

如果測試時, 發現上面的流程沒成功的話呢? 😓 沒關係, 馬上到我們的粉絲團來發問吧~

官方粉絲團: https://www.facebook.com/photoncloudtw/

Stay Tuned!