Advanced Spawning
概述
在Fusion中的生成,包括調用Runner.Spawn()
,Runner.SpawnAsync()
或Runner.TrySpawn()
。此調用建立了一個複製到所有其他同儕節點的網路實體。
- 在伺服器/客戶端模式(伺服器/主機/單一)中,只有伺服器可以生成物件,並且該生成會複製到所有客戶端。
- 在共享模式下,任何客戶端都可以調用
Spawn()
,但該調用會向共享伺服器生成一條訊息,然後共享伺服器會啟動實際的生成。
當調用或複製Spawn()
時,會生成一個網路物件(一個帶有NetworkObject
元件的Unity GameObject
)作為回應。遊戲物件的生成方式由與NetworkRunner
關聯的物件提供器確定。
物件提供器
物件提供器是一個INetworkObjectProvider
實作,它定義了Runner.Spawn()
與Runner.Despawn()
操作委任和解除Unity遊戲物件的管道。每個活躍中的NetworkRunner
都有一個關聯的INetworkObjectProvider
執行個體。預設下,這將是NetworkObjectProviderDefault
執行個體。
自訂物件提供器使用
建立自己的自訂INetworkObjectProvider
類別,可以精確控制生成的實際遊戲物件的生命週期,並且可以是有用的用例,例如;
- 池
- 在運行階段動態建立網路物件
- 進階處理單人玩家模式之間的遷移
指派物件提供器
有幾種方法可以為網路運行器設定物件提供器。
- 調用
Runner.GameStart()
時傳遞INetworkObjectProvider
的執行個體。
C#
runner.StartGame(new StartGameArgs(){ ObjectProvider = new MyObjectProvider()});
- 或;向網路運行器遊戲物件新增一個實作
INetworkObjectProvider
的元件。 - 或;兩者都不做,運行器將自動將
NetworkObjectProviderDefault
新增到其遊戲物件中。
INetworkObjectProvider實作
INetworkObjectProvider
介面需要實作以下兩種方法:
AcquireInstance()
:用於在NetworkRunner.Spawn()
已被調用時從池中取得物件。ReleaseInstance()
:用於從NetworkRunner.Destroy()
已被調用時從池中釋放與傳回物件。
網路物件提供器預設
NetworkObjectProviderDefault
是INetworkObjectProvider
的預設後援實作。如果您沒有在Runner.StartGame()
中傳遞的StartArgs
中指定提供器,運行器將在其遊戲物件中搜尋實作INetworkObjectProvider
的元件。如果找不到實作,運行器將回退到自動向其遊戲物件新增NetworkObjectProviderDefault
元件,然後將其用作物件提供器。
NetworkObjectProviderDefault
是一個非常基本的實作,它將透過具現化已傳遞的預製件的副本來生成物件以回應Spawn()
,而無需任何池。相反,Despawn()
調用是用Destroy()
處理的。
自訂池
物件池是一種常見的模式,用於最大限度地減少記憶體碎片化,並減輕CPU和垃圾收集器的負擔。
出於同樣的原因,儘管不是必需的,但建議在您的INetworkObjectProvider
實作中實作NetworkObject
的池化。
用於遊戲階段的物件池需要實作INetworkObjectPool
。此外,在啟動它之前必須知道它,並將其指派給傳遞給NetworkRunner.StartGame()
方法的StartGameArgs.ObjectPool
參數。如果沒有指定物件池,則將使用NetworkObjectPoolDefault
。
在運行階段提供自訂物件
為了在運行階段動態生成遊戲物件(而不是使用預製件生成),您需要建立一個自訂INetworkObjectProvider
類別,並覆寫AcquirePrefabInstance()
方法以:
- 使用
Instantiate()
、new GameObject()
或GameObject.CreatePrimitive()
建立遊戲物件。 - 如果遊戲物件上不存在
NetworkObject
元件,請新增一個。 - 新增任何所需的下層
GameObjects
和NetworkBehaviours
。 NetworkObjectBaker.Bake()
網路物件(必須在對網路物件結構進行所有更改後最後完成)。- 傳回
NetworkObject
執行個體作為結果。
在製作您自己的自訂INetworkObjectProvider
實作時,可以(在大多數情況下建議)使用NetworkObjectProviderDefault
作為基礎類別。
重要: 一旦生成並附加,網路物件就不能新增或刪除網路行為。生成之前,必須對網路物件上的網路行為進行任何自訂化。當然,仍然可以隨時新增或刪除任何非網路元件。
自訂物件提供器示例
此示例程式碼展示了在運行階段建立自訂網路物件,作為使用預製的預製件生成的替代方案。
C#
public class BakingObjectProvider : NetworkObjectProviderDefault
{
// For this sample, we are using very high flag values to indicate custom.
// Other values will fall through the default instantiation handling.
public const int CUSTOM_PREFAB_FLAG = 100000;
// The NetworkObjectBaker class can be reused and is Runner independent.
private static NetworkObjectBaker _baker;
private static NetworkObjectBaker Baker => _baker ??= new NetworkObjectBaker();
public override NetworkObjectAcquireResult AcquirePrefabInstance(NetworkRunner runner, in NetworkPrefabAcquireContext context, out NetworkObject result)
{
// Detect if this is a custom spawn by its high prefabID value we are passing.
// The Spawn call will need to pass this value instead of a prefab.
if (context.PrefabId.RawValue == CUSTOM_PREFAB_FLAG)
{
var go = GameObject.CreatePrimitive(PrimitiveType.Cube);
var no = go.AddComponent<NetworkObject>();
go.AddComponent<NetworkTransform>();
go.name = $"Custom Object";
// Baking is required for the NetworkObject to be valid for spawning.
Baker.Bake(go);
// Move the object to the applicable Runner Scene/PhysicsScene/DontDestroyOnLoad
// These implementations exist in the INetworkSceneManager assigned to the runner.
if (context.DontDestroyOnLoad)
{
runner.MakeDontDestroyOnLoad(go);
}
else
{
runner.MoveToRunnerScene(go);
}
// We are finished. Return the NetworkObject and report success.
result = no;
return NetworkObjectAcquireResult.Success;
}
// For all other spawns, use the default spawning.
return base.AcquirePrefabInstance(runner, context, out result);
}
}