This document is about: FUSION 2
SWITCH TO

5 - Property Changes

概述

本章節介紹如何使用Networked Properties來透過網路同步玩家位置之外的其他資料。

已連網屬性

當您新增一個NetworkTransform元件到NetworkObjects的轉換,Fusion會同步此轉換。您的指令碼中的變數等其他狀態不會透過網路同步。若要透過網路同步狀態,需要[Networked]屬性。已連網屬性將其狀態從StateAuthority同步到所有其他客戶端。

如果客戶端更改其沒有StateAuthority的物件上的已連網屬性,則該更改不會透過網路同步,而是作為本機預測來應用,並且將來可以被來自StateAuthority的更改覆蓋。如果要在每個客戶端上更新已連網屬性,請注意只更新StateAuthority上的已連網屬性。

網路屬性的一個簡單例子是玩家的顏色。首先建立一個新的指令碼,並將其命名為PlayerColor。新增一個已連網屬性和一個公共欄位以參照物件的MeshRender到它。

在這個例子中,目標是在發射球時將玩家方塊變成白色,然後將其漸變為藍色。

宣告已連網屬性

為了觸發效果,主機端將切換已連網變數中的單一位元。由於在一次刷新中(由給定的玩家)最多只能生成一個球,因此每次新的生成都會將位元的值更改為與前一個刷新不同的值,從而觸發更改偵測。

在新增程式碼之前,請注意,這種設計 可能 會失敗——如前所述,更改 可能 不會被偵測到,尤其是如果它們是觸發器類型的。為了使其更具彈性,可以將bool替換為byteint,並在每次叫用時對其進行碰撞。最後,問題在於視覺效果有多重要,以及它消耗了多少頻寬。

開啟Player類別並且新增一個新的已連網屬性:

C#

[Networked]
public bool spawnedProjectile { get; set; }

定義已連網屬性時,Fusion將使用自訂程式碼來替換提供的getset虛設常式,以存取網路狀態。這意味著應用程式不能使用這些方法來處理屬性值的更改,並且建立分離的設定器方法只能在本機操作。

為了解決這個問題,可以透過ChangeDector偵測更改。新增新的ChangeDector到指令碼中,並在Spawned中對其進行初始化,如下所示:

C#

private ChangeDetector _changeDetector;

public override void Spawned()
{
    _changeDetector = GetChangeDetector(ChangeDetector.Source.SimulationState);
}

也新增一個材質欄位以參照方塊材質,並將該欄位設定為Awake

C#

public Material _material;

private void Awake()
{
    ...
    _material = GetComponentInChildren<MeshRenderer>().material;
}

然後以下列方式新增一個Render函數:

C#

    public override void Render()
    {
        foreach (var change in _changeDetector.DetectChanges(this))
        {
            switch (change)
            {
                case nameof(spawnedProjectile):
                    _material.color = Color.white;
                    break;
            }
        }
    }

這個程式碼迭代自上次調用ChangeDetector以來的Networked Properties發生的所有更改。因此,在這種情況下,是自上次Render以來。如果在任何客戶端上偵測到顏色值發生更改,則會更新MeshRenderer

這對於生成本機視覺效果,以回應狀態更改或執行不會直接影響遊戲遊玩邏輯的其他任務而言非常有用。這是一個重要的警示,因為如果重新模擬的話,屬性更改可能會發生多次(或者,準確地說,兩次,一次是在預測時,如果預測不正確,則再次發生),或者如果屬性在兩個值之間來回更改的速度快於網路狀態的發送速度(或者封包被卸除的速度),則可能會完全略過它。

對RPC等常見訊息使用更改回調的主要好處是,回調會在值更改的刷新之後立即執行,當遊戲處於完全不同的狀態時,RPC可能會稍後才到達。

線性插值顏色

顏色應在Render()中更新為從目前顏色到藍色的線性內插補點。這是在Render()而不是Update()中完成的,因為它保證在FixedUpdateNetwork()之後運行,並且它使用Time.deltaTime而非Runner.DeltaTime,因為它在Unity的渲染迴圈中運行,而不是作為Fusion模擬的一部分。

新增以下行到Render函數的末尾。

C#

  _material.color = Color.Lerp(_material.color, Color.blue, Time.deltaTime);

剩下要做的就是在調用Runner.Spawn()之後透過切換spawnedProjectile屬性來觸發回調:

C#

...
Runner.Spawn(_prefabBall, transform.position+_forward, Quaternion.LookRotation(_forward));
spawnedProjectile = !spawnedProjectile;
...

請記住,有兩個地方調用了Spawn(),並且兩者都應切換spawnedProjectile

但為何?

Q: 但為什麼不在調用生成時立即設定顏色呢?

雖然這對主機端和具有輸入授權的客戶端都有效,因為兩者都是基於玩家的輸入進行預測的,但對代理不起作用。

Q: 但是,如果顏色是一個已連網屬性,只在Render()中的所有客戶端上本機應用,那麼肯定就不需要更改檢測器了?

這確實有效,但需要在主機端上製作動畫,這會產生很多不必要的流量。通常,視覺效果應由狀態授權觸發,然後在每個客戶端上自主運行。儘管每個人都喜歡火花,但沒有人在乎某個火花是朝著一個方向還是朝著另一個方向飛濺。

下一章是 主機端模式基礎6 - 遠端程序調用

Back to top