Tanknarok
概述
Fusion Tanknarok範例說明了如何建立一個小型的多人競技場式坦克遊戲,在Hosted Mode
下執行,有一個授權的伺服器(無論是獨立的應用程式還是同時執行伺服器和客戶端的單一應用程式),或者在Shared Mode
下,每個客戶端對自己的對象有權限,由一個客戶端控制”共享”對象。
這個例子中的遊戲可能在Steam上看起來很熟悉,因為遊戲邏輯、音效和精彩的圖形介面是由我們在Polyblock Studios的朋友提供的。這個例子只是移植到Photon Fusion上的實際遊戲的一小部分;原始遊戲是用Photon Bolt製作的。
Fusion Tanknarok樣本展示了如何實現類似於物理學的效果,而不需要將預測網路系統與不確定的實體物理引擎(如PhysX)混合在一起而產生的復雜情況;如果有需要,Fusion完全支持同步Unity實體。
在您開始之前
使用3D模板創建一個新的Unity項目,確保將顏色空間設置為”線性",在
Project Settings > Player > Other Settings > Color Space
。
在下載和導入樣本之前,確保項目中包含Unity後期處理包。
- 導向到
Window > Package Manager
; - 選擇
Packages: Unity Registry
; - 搜索”後期處理";並,
- 安裝該軟體包。
截圖
下載
版本 | 發佈日期 | 下載 | ||
---|---|---|---|---|
1.1.7 | 2023年6月28日 | Fusion Tanknarok 1.1.7組建202 |
聚焦點
- 支持共享和托管模式
- 延遲補償的雷射廣播
- 預測生成
- 對象池
- 完整的遊戲循環
專案項目
在執行演示之前,必須為Photon雲創建一個Fusion應用程式ID,並復制粘貼到PhotonAppSettings
資產中。應用ID可以從Photon Dashboard中創建。請確保創建一個__Fusion__應用ID,而不僅僅是一個實時ID。
Photon應用設置資產可以從Fusion選單Fusion > Realtime Settings
中選擇。
簡單地將生成的App Id粘貼到App Id Fusion
字段中。
文件夾結構
Tanknarok派生樣本的代碼在/Scripts
文件夾中,其中Utility
子文件夾用於一般用途的實用程序,FusionHelpers
文件夾用於非本例專用的Fusion實用程序。
剩下的Tanknarok
文件夾包含實際的遊戲代碼,分為以下子文件夾:
- 音效 - 音效和音樂
- 相機 - 相機放置代碼
- 關卡 - 所有的關卡邏輯和行為,動力裝置和其他非玩家項目
- 玩家 - 所有的坦克控制、武器和子彈邏輯,以及坦克的可視化和效果
- UI - 用戶介面組件
在主文件夾中,主要入口是GameLauncher
類和頂層管理器GameManager
、LevelManager
和PlayerManager
。
快速Fusion入門
Fusion通過NetworkObject
組件來識別網路狀態;任何具有網路狀態的遊戲對象必須有一個NetworkObject
。NetworkObject
本身只是給遊戲對象分配了一個網路範圍內的身份,實際的網路狀態存儲在從NetworkBehaviour
派生的組件中。Fusion中有幾個默認的行為,例如,NetworkTransform
可以同步Unity Transform。
與物理行為如何在FixedUpdate()
中轉換物理狀態類似,NetworkBehaviour
在FixedUpdateNetwork()
方法中轉換其網路狀態。這與渲染幀率無關,也與網路的更新無關,是在一個固定的時間步長上發生,被稱為tick。每次更新都是根據前一個tick的狀態來進行。當網路確認了某個時間點的狀態後,Fusion會將對象狀態回滾到該時間點,並重新應用從該時間點到當前時間點之間對FixedUpdateNetwork()
的所有中間呼叫。
因為當前的本地tick總是領先於最後確認的tick,所以更新被稱為”預測",而應用已驗証的狀態和隨後重新執行FixedUpdateNetwork
方法被稱為”回滾”和”重新模擬"。
一個組件有可能在沒有任何網路狀態的情況下成為仿真的一部分,但是它應該派生自SimulationBehaviour
而不是NetworkBehaviour
以減少消耗。請注意,由於重新模擬,FixedUpdateNetwork()
方法可能會在每一幀中被呼叫很多次-這在專門處理網路狀態時不是問題,因為它會被重置,但在對非網路狀態應用delta變化時要小心。
關於模擬、預測和網路對象的更多訊息,請查閱Fusion手冊。
GameLauncher
坦克遊戲的主要用戶介面是由GameLauncher
類別處理。
一旦選擇了遊戲模式,GameLauncher將呼叫FusionLauncher.Launch()
來建立一個會話。FusionLauncher
將回應Fusion連接事件並呼叫所提供的呼叫返回,以生成初始網路對象。
- 遊戲管理器(在托管模式下由主機生成,在共享模式下由主客戶端生成)。
- 玩家(由主機在托管模式下生成,或在共享模式下由各客戶端生成)
準備就緒
當玩家連接時,玩家的坦克會立即生成,並且可以在”大廳”模式下完全控制,在那裡他們可以在等待其餘坦克生成的同時進行遊戲。
遊戲本身不會開始,直到所有連接的玩家表示他們已經準備好。這個邏輯在所有的客戶端上執行,但只有擁有GameManager
實例的StateAuthority
的客戶端可以下載關卡。
N.B.: 在這個簡化的例子中,關卡與其說是”下載”不如說是”啟用",因為兩個關卡從一開始就在初始場景中。
下載是通過一個遠程過程呼叫完成的。呼叫者生成隨機的關卡索引,並將其傳遞給所有的客戶端,以確保每個人都能下載相同的關卡。
C#
if (Object.HasStateAuthority) {
RPC_ScoreAndLoad(-1,0, _levelManager.GetRandomLevelIndex());
}
RPC本身被定義為
C#
[Rpc(sources: RpcSources.StateAuthority, targets: RpcTargets.All, InvokeLocal = true, Channel = RpcChannel.Reliable)]
private void RPC_ScoreAndLoad(int winningPlayerIndex, byte winningPlayerScore, int nextLevelIndex)
{
...
}
關卡過渡
從大廳到關卡的過渡和反之亦然,由LevelManager
在TransitionSequence()
結合程序中處理。過渡本身完全在本地時間執行,但是當它被觸發(由RPC_ScoreAndLoad
遠程程序呼叫)以及當它終止(通過設置playState
到LEVEL
,因為這是一個網路屬性,只能由GameManager
狀態權限設置)時,在客戶端之間進行同步。
在遊戲結束時,關卡轉換將返回大廳,並以同樣的方式顯示贏家,完成循環並將遊戲返回到就緒狀態。
輸入處理
Fusion使用Unity的標準輸入處理機制來捕獲玩家的輸入,將其存儲在一個可以通過網路發送的數據結構中,然後在FixedUpdateNetwork()
方法中根據這個數據結構來工作。在這個例子中,所有這些都是由InputController
類別實現的,盡管它把實際的狀態變化交給了Player
類別。
射擊
本例中的坦克有4種不同的武器,有兩種不同類型的命中檢測。
- 瞬間命中
- 彈射
分別由HitScan
和Bullet
類實現。兩者都使用對象池、預測生成和延遲補償這三個重要的Fusion功能。
Object Pooling
為了避免在生成新對象時出現幀數下降,建議重新使用舊對象,而不是一直銷毀和實例化新對象。這對任何遊戲都是如此,尤其是Unity,甚至對Fusion也是如此。
為了促進這一點,Fusion允許應用程式指定一個鉤子來提供和收集回收的遊戲對象。
對象池必須實現NetworkObjectPool
,它基本上有一個方法從池子裡根據預制件獲取一個對象,還有一個方法將一個對象返回到池子裡供重新使用。
預測性再生
預測生成允許客戶預測一個新的聯網對象的創建,創建一個臨時的本地佔位符,直到該創建被國家當局確認。一旦確認,Fusion會自動將佔位符提升為實際的聯網對象。
需要手動處理的是_失敗的預測。這可以是簡單的摧毀佔位符(記住,它只是一個Unity對象);應用程式可以自由實現各種形式的淡出或失敗的可視化。
此外,由於佔位符沒有狀態,應用程式將需要在預測階段管理運動,而不涉及訪問任何網路屬性。
延遲補償
在本地,每個玩家看到的是他們自己(輸入授權)對象的預測未來版本,以及其他客戶對象的互推或外推版本;它們都不與伺服器看到的完全一致。因此,一個快速移動的物體,例如一顆子彈,很可能會在每台機器上擊中不同的東西。發射子彈的人是最有可能注意到不對勁的人。然而,與此同時,伺服器應該擁有對命中檢測的權力,以避免一個耍賴的客戶簡單地決定他們已經成功命中了。
為了解決這個問題,Fusion支持延遲補償的光線投射。這些本質上是射線投射,即使在伺服器上執行,也會尊重客戶端在拍攝時看到的情況。顯然,有很多快照插值魔法在幕後進行,以使其發揮作用;幸運的是,對於開發者來說,實現和使用延遲補償光線投射與使用常規Unity光線投射一樣簡單。
唯一需要知道的是,它有自己的碰撞器對象類型,稱為HitBox
。HitBox
應該是對象層次結構中完全包含的HitBoxRoot
節點的兄弟姐妹或子女系統。這允許Fusion在對子節點進行更昂貴的檢查之前,對根節點進行快速排除。
由於性能原因,HitBox
不應該應用於靜態實體。靜態環境仍然需要阻止射線投射,因此延遲補償的射線投射也可以選擇檢查Unity碰撞器。
需要注意的是,延遲補償對動態(即移動的)PhysX碰撞器不起作用;此外,Unity沒有提供區分靜態和動態碰撞器的光線投射查詢。因此,為了過濾PhysX碰撞器,只獲得靜態結果,建議將HitBox
/HitBoxRoot
和PhysXCollider
(如果兩者都需要)放在動態對象的不同層。