This page is a work in progress and could be pending updates.

Network Object

簡介

NetworkObjectGameObject分配了一個網路身份,所以它可以在網路上共享。任何被所有玩家看到並且是場景的動態部分的GameObject都應該帶有一個NetworkObject腳本。

Fusion提供了兩個NetworkObject基礎類別,用於派生自定義網路行為:

  • SimulationBehaviour仿真行為用於仿真相關的行為;以及,
  • NetworkBehaviour用於保持或追蹤[Networked]狀態的行為。

Back To Top

網路對象

在一個GameObject的根節點上有一個單一的NetworkObject腳本,足以控制整個層次結構。在執行時,NetworkObject會找到其層次結構中的所有SimulationBehaviourNetworkBehaviour

如果需要,NetworkObject可以被嵌套。在這種情況下,NetworkObject不會搜索到嵌套的子對象,而是讓子NetworkObject追蹤它下面的所有SimulationBehaviourNetworkBehaviour。一個典型的用例是一個由底盤和炮塔組成的雙人控制的坦克,駕駛員控制底盤的方向(移動),炮手控制炮塔(射擊)。

Back To Top

預制件/對象表

為了使Prefab參數對Fusion有意義(並確保所有客戶都同意到底是哪個預制件),預制件本身必須在Fusion註冊。在正常情況下,Fusion工具集會自動檢測這些預制件並註冊,但您也可以通過選擇NetworkProjectConfig並在Unity Inspector中按Rebuild Object Table來手動觸發這種檢測。

Back To Top

事件化/生成

NetworkObject預制件的事件化需要兩個步驟:

  1. 在編輯時:確保客戶必須有相同的NetworkProjectConfig資產,這與NetworkObject預制件的身份一致。這是通過在NetworkProjectConfig資產中執行Rebuild Object Table來執行的;以及,
  2. 在執行時:在NetworkRunner上呼叫Spawn(),將NetworkObject預制件作為參數。

IMPORTANT: 呼叫通用的UnityInstantiate()方法只會創建一個有問題的本地事件!

Back To Top

銷毀/解體

要銷毀一個NetworkObject,請呼叫Runner.Despawn()並將該對象作為參數傳遞。這將銷毀該對象以及所有的嵌套對象;要想只銷毀頂層對象,需要先手動去掉嵌套對象的父級。

Back To Top

授權

一個NetworkObject有兩種類型的權限(又稱所有權):

  • 輸入權限;以及,
  • 狀態權限。

Back To Top

輸入與狀態

輸入權限允許收集和發送該對象的用戶輸入。它也將使客戶端能夠在FixedUpdateNetwork()中獲得相同的輸入,以模擬和本地預測該對象的未來狀態。State Authority能夠根據輸入來讀取和模擬狀態,從而允許預測和真實狀態被同一代碼所突變。

換句話說,State Authority給出了對對象狀態的最終控制權,並直接決定了什麼會進入發送到所有其他客戶端的同步的delta快照。最終,擁有狀態權限的客戶端所設置的狀態將取代擁有輸入權限的客戶端所預測的狀態。

Back To Top

指派權限

遊戲代碼可以通過HasInputAuthorityHasStateAuthority屬性來檢查客戶端對一個給定的NetworkObject的權限類型。

當一個NetworkObjectRunner.Spawn()生成時,呼叫的客戶端可以將輸入權限分配給自己或任何其他客戶端。

  • 如果網路模式被設置為客戶端權限,客戶端也可以對該對象承擔狀態權限。
  • 如果網路模式被設置為專用伺服器或客戶主機,狀態權限將總是被分配在那裡。

Back To Top

模擬行為

所有的SimulationBehaviour都可以通過Object屬性訪問它們相關的NetworkObject

Simulation是Fusion更新網路狀態的方式。任何屬於或影響模擬狀態的行為都必須派生自SimulationBehaviour而不是MonoBehaviour

例如,坦克樣本中的LevelManager會產生動力裝置。雖然動力裝置是NetworkObject,但LevelManager只需要影響和了解模擬狀態來執行其行為。因此,繼承自SimulationBehaviour是正確的方法。

public class LevelManager : SimulationBehaviour
{
    [SerializeField] private float _powerupDelay;
    [SerializeField] private NetworkObject _powerupPrefab;

    private TickTimer _powerupTimer;

    public override void FixedUpdateNetwork()
    {
        // 所有關卡管理邏輯都發生在服務器端,所以如果我們不是伺服器,請您放心。
        if (!Object.HasStateAuthority) return;

        // LevelManager 的唯一職責是在計時器到期時生成電源
        if (_powerupTimer.ExpiredOrNotRunning(Runner)) {
            // 重置計時器,並檢查是否有空點來生成新通電
            _powerupTimer = TickTimer.CreateFromSeconds(Runner,_powerupDelay);
            SpawnPoint spawnPoint = GetSpawnPoint(SpawnPoint.Type.Powerup);

            if (spawnPoint != null) {
                Debug.Log("Spawning Powerup");
                NetworkObject powerupobj = Runner.Spawn(_powerupPrefab, spawnPoint.transform.position, spawnPoint.transform.rotation, PlayerRef.None);
                powerupobj.GetComponent<Powerup>().Init();
            } else {
                Debug.Log("Not Spawning Powerup - no free spot");
            }
        }
    }
}

如果該行為需要訪問[Networked]屬性,它必須派生自NetworkBehaviour

Back To Top

網路行為

一個NetworkBehaviour需要在同一個節點或一個父節點上有一個NetworkObject

NetworkBehaviour是一個可以攜帶同步狀態的SimulationBehaviour;這就是為什麼它必須要有一個相關的NetworkObject的原因。

Back To Top

網路狀態

在內部,Fusion保留了一個單一的內存緩沖區,代表了當前本地tick的整個網路狀態。該狀態通過標記[Networked]的屬性暴露給遊戲代碼。

要定義和序列化一個網路狀態的值,在一個屬性前面添加[Networked]屬性。Fusion將自動用它自己的高性能數據緩沖器和delta壓縮來處理標記的屬性。 屬性設置器和獲取器在編譯時被替換為自定義代碼,以消除內存分配消耗並提供最佳性能。

public class PlayerBehaviour : NetworkedBehaviour {
  [Networked] public float Health { get; set; }
}

在編譯時,Fusion將用訪問實際網路狀態數據的代碼取代空的get/set stubs。直接訪問消除了內存分配的消耗,提供了最佳的性能。不要手動執行它們。

對緩沖區的直接訪問也意味著每當有變化發生時,屬性會立即反映出該變化。

要編寫影響網路狀態的邏輯,請覆蓋並執行FixedUpdateNetwork()

public override void FixedUpdateNetwork() {
    Health += Runner.DeltaTime * HealthRegen;
}

Back To Top

限制條件

[Networked]屬性必須遵守這些約束條件:

  • 該屬性必須有一個空的get/set對;
  • 它只能包含以下類型:
    • 原始類型(完整列表見下文)
    • 統一類型(完整列表見下文)
    • 執行INetworkStruct並在頂層定義的結構(即不能嵌套在類中)
    • 指向網路結構的指針
    • 使用[Capacity]屬性設置最大長度的字符串(默認為16)
    • 網路對象
    • NetworkBehaviour子類
    • NetworkArray<T>,最大長度使用[Capacity]屬性設置(默認為1)
  • 對於布林值,使用NetworkBool而不是bool-C#在不同的平台上對布林值不執行一致的大小,所以使用NetworkBool來正確地將其序列化為一個位元。

可用的原始類型有:

  • byte, sbyte
  • Int16, Int32, Int64
  • UInt16, UInt32, UInt64
  • float
  • double

可用的Unity類型是:

  • Vector2, Vector3, Vector4
  • Quaternion
  • Matrix4x4
  • BoundingSphere
  • Bounds
  • Rect
  • Color
  • Vector2Int, Vector3Int
  • BoundsInt
  • RectInt
  • Color32

Back To Top

屬性

通過使用[Accuracy]屬性,可以控制單個數字類型屬性的準確性。

public class PlayerBehaviour : NetworkedBehaviour {
  [Networked, Accuracy(0.001)]
  public float Health { get; set; }
}

[Capacity]用於定義NetworkArray和字符串的最大長度;設置它們的最大長度是Fusion能夠對這些類型進行聯網的必要條件。

[Capacity(14)] string MyString { get; set; }
[Capacity(8)]  NetworkArray<byte> MyArray { get; }

Back To Top

對OnChanged的反應

當網路屬性被Fusion改變時,遊戲代碼可以被觸發。要寫反應式代碼,使用[Networked]屬性中的(OnChanged)參數。

public class Powerup : NetworkBehaviour{
    [Networked(OnChanged = nameof(OnTypeChanged))] public Type type { get; set; }

    // 必須是 public static void
    public static void OnTypeChanged(Changed<Powerup> changed){
        changed.Behaviour.OnTypeChanged();
    }

    private void OnTypeChanged(){
        // 對“類型“的值變化做出反應的一些邏輯
    }
}

除了指定呼叫返回名稱外,還可以控制呼叫返回在哪些機器上執行:

  • OnChangedLocal(默認為false):設置為true,可以在改變屬性的機器上呼叫事件鉤。
  • OnChangedRemote(默認為true):設置為false,在改變屬性的機器上呼叫事件鉤。

Back To Top

覆寫方法

一個NetworkObject有許多額外的生命周期方法,所有這些方法都可以在執行SimulationBehaviourNetworkBehaviour時被覆寫。

功能 說明
FixedUpdateNetwork() Fusion的固定時間步長呼叫返回。用於遊戲邏輯核心。
Spawned() 生成後呼叫返回。
Despawned(bool hasState) 在網絡對象消失之前呼叫。
---
bool hasState:如果行為的狀態仍然可以訪問。
Render() 後模擬幀渲染呼叫返回。在所有模擬完成後執行。在 Fusion處理物理時代替Unity的更新。

Back To Top

FixedUpdateNetwork

FixedUpdateNetwork()- 簡稱FUN()- 在模擬新的狀態時,從一個tick到下一個tick被呼叫。FixedUpdateNetwork()以固定的時間間隔被呼叫;這些間隔的時間步長在NetworkProjectConfig資產中的Simulation > Tick Rate中定義。時間步長可以通過Runner.DeltaTime屬性從任何SimulationBehaviourNetworkBehaviour中獲取。

FixedUpdateNetwork()可以為同一個狀態轉換多次呼叫,以根據從伺服器收到的更新(基本事實)重新模擬當前預測的狀態。對於[Networked]屬性來說,重新模擬是透明的,因為Fusion會在呼叫FixedUpdateNetwork()之前重置網路狀態,以達到重新模擬的目的。

IMPORTANT: 常規的本地非網路狀態變數(例如類別成員)將不會被重置,只是將其視為一個額外的前向狀態進展。

To Document Top