Spawning
簡介
為了使一個遊戲對象在網路上存在,它必須:
- 有一個
NetworkObject
組件;並且, - 使用
Runner.Spawn()
方法創建。
NetworkObject
被Fusion用來分配一個網路範圍內的唯一標識符,這樣所有的客戶端就能同意哪個事件是哪個,並正確地同步每個網路對象的狀態。Runner.Spawn()
方法告訴Fusion何時以及如何將一個新的對象事件添加到集體網路狀態中。
重要的是!
__NOT__使用Unity內置的GameObject.Instantiate()
和GameObject.Destroy()
方法來處理網路對象,這只會創建一個完全脫離Fusion模擬循環的本地遊戲對象,其網路狀態會被破壞。
Runner.Spawn
FusionNetworkRunner
事件上的Runner.Spawn()
方法模仿了Unity中的GameObject.Instantiate()
方法。可以提供給該方法的參數有:
- 一個
NetworkObject
類型的預制件。 - 一個位置
- 一個旋轉
- 一個
PlayerRef
,用來識別對該對象有輸入權限的客戶端。 - 一個
NetworkRunner.OnBeforeSpawned
類型的委托,在將對象複製到其他事件之前執行。
6.一個類型為NetworkObjectPredictionKey
的預測密鑰,以防這是一個預測的生成。
只有prefab是一個強制參數,其他的都是可選的。
C#
var obj = Runner.Spawn(prefab, Vector3.zero, Quaternion.identity, Runner.LocalPlayer, MyOnBeforeSpawnDelegate, key);
盡管任何客戶端都可以呼叫Runner.Spawn()
,但結果會因網路拓扑結構而不同。
- 在伺服器上,在托管或客戶/伺服器模式下,被催生對象的所有權(State Authority)被分配給伺服器,對象被創建並立即返回。客戶端將收到該對象作為下一個快照的一部分。
- 在托管或客戶端/伺服器模式的客戶端上,默認的操作是什麼都不會發生,呼叫將返回null。然而,Fusion支持創建一個臨時的本地對象以獲得即時反饋-這被稱為"預測生成"。
- 在共享模式下,State Authority總是被分配給呼叫者,同樣,該事件會立即返回。其他客戶端最終會收到該對象。
在這兩種情況下,Spawn應該從FixedUpdateNetwork
中呼叫,在前進和再模擬階段都可以呼叫。在重新模擬預測的本地生成期間,Fusion將正確返回相同的對象(假設預測密鑰匹配),而授權生成只發生在共享模式或主機上,兩者都不進行重新模擬。
輸入權限
輸入權限是通過向方法傳遞他們的PlayerRef
來分配給一個特定的客戶的;這是可選的。被賦予輸入權限的客戶端將能夠為該對象提供輸入數據,並且(除了主機或伺服器)被允許在GetInput()
中查詢該輸入結構。
如果該對象不需要輸入,或者沒有客戶端對其有輸入權限,可以傳遞null
代替。
OnBeforeSpawned
NetworkRunner.OnBeforeSpawned
參數可以接受一個與委托簽名相匹配的方法或lambda表達式。
C#
public delegate void OnBeforeSpawned(NetworkRunner runner, NetworkObject obj);
這個委托在對象被創建後,但在它被同步到所有事件之前被呼叫。這允許呼叫者在系統的任何其他部分能夠訪問該對象之前對其進行額外的自定義初始化。這是一個初始化自定義網路屬性的好地方。
C#
private void MySpawnFunction(){
Runner.Spawn(
_objPrefab,
Vector3.zero,
Quaternion.identity,
inputAuthority: null,
InitializeObjBeforeSpawn,
predictionKey: null
);
}
private void InitializeObjBeforeSpawn(NetworkRunner runner, NetworkObject obj)
{
var objSB = obj.GetComponent<ObjSimulationBehaviour>();
objSB.InitializeObjSettings(_currentExplosionForce);
}
生成
當Fusion創建一個新對象時,它將對該對象呼叫Spawned()
方法。Spawned()
呼叫返回是用來重置非網路變數和子系統的。例如,如果應用程式使用對象池,Spawned()
可能需要將值重置為其預制的默認值,因為對象已經被回收,不再有其默認狀態。所有的NetworkBehaviour
組件都執行了ISpawned
接口,因此從NetworkBehaviour
派生的每個組件都可以簡單地覆蓋和執行它。然而,SimulationBehaviour
派生的組件需要明確地執行ISpawned
及其方法,以便呼叫其Spawned()
方法。
Spawned()
在Fusion第一次得知對象的存在時被呼叫。這並不是與tick對齊的,也不一定是與該對象最初被授權對象催生的tick相近的地方。由於這個原因,Networked
狀態不應該在Spawned()
中初始化,也不應該在Spawned()
中發送RPC
。
- 對於在具有狀態授權的對象上的生成,
Spawned()
會在Runner.Spawn()
之後立即被呼叫。 - 對於具有輸入權限的對象上的預測生成,
Spawned()
在網路狀態被分配時(即生成被確認時)被呼叫。 - 對於代理(即對象既沒有輸入權限也沒有狀態權限的對象),
Spawned()
在Fusion收到一個本地不存在的對象的網路狀態時被呼叫。特別是一個晚期加入者很可能會收到很久以前產生的對象,他們的出生時間
的原始狀態不再具有相關性。
如果一個狀態應該在生成時被初始化,請使用提供給Runner.Spawn()
的生成前NetworkRunner.OnBeforeSpawned
呼叫返回。
解散
要移除一個網路對象,對該對象擁有狀態權限的對象可以呼叫Runner.Despawn()
。
解體
類似於Runner.Spawn()
對於執行ISpawned
的類來說,會觸發Spawned()
方法的呼叫,Despawn()
將導致執行IDespawned
的類被呼叫Despawned()
方法。
所有的NetworkBehaviour
都包含IDespawned
,而SimulationBehaviour
必須明確地添加它。
生成預測
預測生成之所以被命名為預測生成,是因為它讓非授權的客戶端預測一個對象的未來存在,並允許它模擬一個本地佔位符,直到適當的網路對象的存在被確認為成功或失敗。
有兩件事需要發生,才能使其發揮作用:
- 必須創建一個唯一的標識符,以允許客戶端將其臨時本地對象與未來的”實際”事件相匹配。這將使它能夠在確認生成後將本地對象升級為第一類網路對象。
- 客戶端必須將本地對象處理成這樣,直到它被確認或取消。
NetworkObjectPredictionKey
Runner.Spawn()
方法中的predictionKey
參數由4個字節組成。只要考慮到以下幾點,應用程式可以自由發明自己的鍵:
- 密鑰必須在客戶端和授權主機/伺服器上是相同的,因此它不能是隨機的或基於本地數據的。
- 對於玩家和當前的tick來說,它必須是唯一的。在密鑰中包括tick(或它的一些低端部分)和玩家的ID是明智的。
C#
var predictionKey = new NetworkObjectPredictionKey {Byte0 = (byte) Runner.Simulation.Tick, Byte1 = playerIndex};
N.B.: 如果客戶端在同一時刻預測性地產生了多個網路對象,則需要額外的訊息(例如一個共享的計數器)來區分不同的網路對象。
IPredictedSpawnBehaviour
當生成的遊戲對象處於預測狀態時,它不會成為Fusion模擬的一部分,其網路屬性也不會工作,因為還沒有任何狀態可供它們訪問。為了使預測的對象表現得像一個真實的對象,需要一些額外的邏輯。這個邏輯必須使用IPredictedSpawnBehaviour
接口來執行。
C#
public interface IPredictedSpawnBehaviour {
void PredictedSpawnSpawned();
void PredictedSpawnUpdate();
void PredictedSpawnRender();
void PredictedSpawnFailed();
void PredictedSpawnSuccess();
}
Spawned, Update, Render
前面的三個方法與Spawned()
、FixedUpdateNetwork()
和Render()
方法相匹配,這些方法在功能性的SimulationBehaviour
和NetworkBehaviour
組件中找到。它們的呼叫條件與這些方法相同,不同的是這些方法是在對象處於“預測”狀態時專門明確呼叫的。
事實上,這些方法的一個可能的執行是簡單地分別呼叫Spawned()
、FixedUpdateNetwork()
和Render()
,盡管應用程式需要考慮網路屬性不可用。一個解決方案是用預測生成的檢查來包裝網路化屬性,並維護兩組變數,如以下:
C#
[Networked]
public Vector3 networkedVelocity { get; set; }
private Vector3 _predictedVelocity;
public Vector3 velocity
{
get => Object.IsPredictedSpawn ? _predictedVelocity : networkedVelocity;
set
{
if (Object.IsPredictedSpawn)
_predictedVelocity = value;
else
networkedVelocity = value;
}
}
失敗和成功
PredictedSpawnFailed()
和PredictedSpawnSuccess
是專門針對預測的,當伺服器/主機確認spwn動作的成功或失敗時就會觸發。如果預測成功,PredictedSpawnSuccess()
被呼叫,否則PredictedSpawnFailed()
被觸發。
應用程式很少需要處理成功的生成,因為臨時對象只是被提升為一個完全聯網的對象,並繼續存在,就像什麼都沒有改變一樣;然而,預測失敗確實需要處理-最簡單的執行就是直接銷毀對象。
當對預測對象使用Runner.Despawn()
時,它需要一個額外的布林參數來表示有關對象處於預測狀態。
C#
public void PredictedSpawnFailed()
{
Runner.Despawn(Object, true);
}
Runner.Despawn()
必須被使用,因為Runner.Spawn()
通過Fusion Object Pool獲得了臨時對象,因此需要再次將其返回到池中。