網路功能網格可視化腳本最佳做法
概觀
在 Mesh 中,大部分的場景屬性預設會自動在連線到相同會議室的所有客戶端之間共用。 例如,場景對象的轉換位置和旋轉、元件的啟用狀態或 TextMeshPro 的文字。
根據經驗法則,預設會自動共用具有下列實值類型的元件屬性和 Object 變數:
不會共用集合類型(清單和集合)和場景對象參考。
在 Mesh 中存取或修改屬性的視覺腳本節點會以標籤,指出它們是否為「由所有用戶端共用」或「此用戶端的本機」:
如果您已使用上述其中一個實值類型來宣告物件變數,則預設也會共享物件變數:
Mesh 不支持場景變數,但您可以在環境中使用獨立 變數 元件來隱藏可從任何特定 腳本計算機 元件獨立共用的變數。
如果您不想自動共享屬性或物件變數,您可以將本機 腳本範圍 元件新增至場景。 這會讓此遊戲物件及其任何子系區域的所有場景屬性和腳本變數。
秘訣:您可以在網格 101 教學課程的第 3 章中,看到數個範例,其中著重於可視化腳本的本機腳本範圍元件。
針對您只在單 一腳本機器中使用的本機腳本變數,最好是使用 Graph 變數,這些變數永遠不會由 Mesh 在客戶端之間共用。
透過 Mesh 視覺化文稿共用可提供下列保證:
保證最終一致性:所有客戶端最終都會到達相同的共享狀態。
保證每個元件的不可部分完成性:相同更新中相同場景元件(或相同 Variables 元件)的所有更新都會以不可部分完成的方式套用至每個用戶端。
但是:
沒有排序保證:一個用戶端套用至數 個不同 場景元件的更新可能會以不同的順序抵達不同的用戶端。
沒有時間軸保證:Mesh 會盡最大努力在客戶端之間復寫狀態變更,但網路狀況可能會延遲部分或所有用戶端上任何指定狀態更新的抵達。
沒有數據粒度保證:任何用戶端都可能不會看到共享狀態的所有個別累加式更新。 當網路狀況強制 Mesh 伺服器進行速率限制更新時,就會發生這種情況。 當用戶端延遲加入會議室時,也會發生此情況。
狀態為 shared--not 事件
您無法使用 Mesh Visual Scripting 來傳送或接收明確的網路訊息。 這在一開始可能會出人意料,但它有助於建立網路架構,以便輕鬆地統一處理運行時間變更和晚期聯結。 場景屬性和腳本變數中沒有共享狀態,而不是訊息。
您的腳本可以統一的方式響應共享狀態更新,不論這些更新是由本機腳本或使用者、另一個共用相同會議室體驗的用戶端,或是在您甚至自行加入之前已加入會議室的其他用戶端。
無法明確傳送網路訊息表示您必須開始考慮 取得更新 的共享狀態,而不是 導致狀態更新的共用事件。 共用事件是共享狀態更新的結果,而不是相反的。
幸運的是,Mesh Visual Scripting 可讓您的視覺腳本輕鬆響應狀態更新。 使用 On State Changed 事件節點,並將其左側輸入與您想要觀察是否有變更的任何腳本變數或元件屬性連接,每當任何連線到它的變數或屬性變更其值時,事件節點就會觸發您的腳本(連接到其右側)。
這適用於共享狀態和本機狀態。 不論所觀察到的變數或屬性是由本機用戶端、遠端用戶端,甚至是遠端用戶端在本機用戶端加入會議室之前,還是遠端用戶端變更,On State Changed 事件都會觸發。
使用 狀態變更 來回應狀態變更是有效率的:沒有閑置頻寬或效能成本。 您可以讓任意數目的視覺腳本以這種方式被動接聽狀態更新,而不會對環境的幀速率或頻寬使用造成負面影響。
延遲聯結
當用戶端加入已連接其他客戶端的會議室時,就會發生晚期聯結。
在晚期聯結時,Mesh 會從伺服器接收會議室的目前狀態,例如,已經在會議室中,以及其虛擬人偶目前所在的位置,並快速準備加入用戶端的共用環境的本機版本,使其符合會議室中每個人共享的狀態。
大部分而言,Mesh Visual Scripting 會執行相同的動作。 在剛加入用戶端之前,在會議室中變更的任何共用元件屬性和可視化腳本變數都會在本機更新以符合共享狀態,然後觸發觀察到這些屬性或變數的任何 On State Changed 事件節點。
晚期聯結者不會重新執行共用事件--它們會取得共享狀態。
從本機客戶端的觀點來看,環境一律會從載入您上傳至 Mesh 的場景之後,從其初始狀態演變而來。 在晚期聯結的情況下,第一個狀態變更可能會大於本機用戶在進行中會話中與會議室互動時所發生的情況,但原則上情況完全相同。
這一切都發生在環境載入之前,它甚至從黑色淡入。 只要用戶實際看到環境並與其互動,就已完成晚期聯結。
讓本機狀態遵循共享狀態
通常,用戶可以在環境中觀察到的「共享狀態」實際上是由 Mesh 和本機狀態直接共用的狀態組合,而該狀態是由視覺腳本所建立,以響應會議室中發生的事件。 例如,當使用者翻轉環境 (共享狀態) 中的參數時,視覺腳本可能會變更 skybox (本機狀態) 的色彩。 您可能會想要直接套用本機變更(更新 skybox 色彩),以回應與交換器互動的使用者。 不過,即使互動事件發生在目前會議室中的所有用戶端上,任何稍後加入會議室的用戶端都不會因為事件發生時沒有該事件而獲得該事件。 相反地,您應該 讓本機狀態遵循如下的共享狀態 :
- 當用戶互動時(例如,翻轉參數),讓此觸發程式成為 更新共用變數的本機 事件(例如切換的開啟/關閉狀態)。
- 使用 [狀態變更 ] 觀察共用變數。
- 當 On State Changed 事件觸發時(因為共用變數變更其值),請套用您想要的任何本機變更(例如,更新 skybox 色彩)。
如此一來,本機狀態 (skybox 色彩) 會遵循共享狀態 (參數的狀態)。 其好處在於,它適用於翻轉交換器之本機用戶端、同時存在於會議室中的所有其他遠端用戶端,以及稍後加入會議室的任何未來用戶端,都能運作。
讓本機狀態遵循共享狀態:最佳做法
本機事件:例如,觀察 Mesh Interactable Body 元件的 Is Selected Local 屬性的 On State Changed 事件節點:
- 🆗 可以將私人的本機狀態變更為用戶端。 這些狀態變更會嚴格保留在本機用戶端上,當客戶端離開會話時,它們就會消失。
- 🆗 可以變更共享狀態。
- ❌無法將本機狀態變更為跨用戶端保持一致。 本機事件只會在一個用戶端上執行,因此在客戶端之間保持本機狀態一致所需的更新不會發生在任何其他用戶端上。
共用事件:例如, 附加至共用物理觸發程式碰撞程式的 On Trigger Enter 事件節點:
- 🆗 可以變更暫時效果的局部狀態:例如,粒子效果或短音訊效果。 只有在發生共用事件時,才會出現在會議室中的用戶端才能看到本機效果;任何稍後加入會議室的用戶端都不會。
- ❌無法將本機狀態變更為跨用戶端保持一致。 共用事件只會在發生時存在的用戶端上執行,但稍後加入會話的用戶端將不會重新執行。
- ⛔ 不得變更共享狀態。 由於共用事件會在所有用戶端上執行,所以所有用戶端所做的任何作業都會及時非常接近。 根據變更的性質,它最終可能會重複數次(例如,分數計數器可能會遞增多個,以回應單一事件)。
在 [狀態變更 ] 事件上,觀察共用變數或共用元件屬性中的共享狀態 :
- 🆗 可以將本機狀態變更為與客戶端之間的共享狀態 一致。 若要讓這在所有用戶端都能以可重複且一致的方式運作,您必須將觀察到共享狀態的每個可能新值轉譯為本機狀態,而不只是一些櫻桃挑選的狀態轉換(例如 已選取 為 true)。
讓本機狀態遵循共享狀態:範例
在此範例中,此環境中有兩個互動式按鈕:一個標示為 “Star”,另一個標示為 “Sponge”。 選取任一按鈕應該執行兩件事:
- 將對應的標籤儲存在名為 ObjectKind 的共用字串變數中。
- 將對應場景對象的參考儲存在名為 ObjectRef 的本機 GameObject 參考變數中。
以下是兩個腳本流程,每個按鈕各一個。 每個都會接聽一個按鈕之 Mesh Interactable Body 元件的共用 Is Selected 屬性,並根據選取的按鈕更新 ObjectKind 和 ObjectRef:
一切似乎都沒問題,但僅適用於選取其中一個按鈕時已經在會議室的使用者。 任何加入會話的使用者稍後會在共享環境的本機版本中找到不一致的狀態:根據 最近選取的按鈕正確設定 ObjectKind ,但 ObjectRef 會維持 Null。
這兩個腳本流程有什麼問題?
首先,請注意,這些腳本流程是由共用事件觸發,因為它們都接聽每個按鈕的共用 Is Selected 屬性變更。 這似乎很合理,因為它是在所有用戶端上更新本機 ObjectRef 變數的唯一方法。
但是:
- 共用事件不得變更共享狀態 ,但這些腳本流程正在更新共用 的 ObjectKind 變數。
- 共用事件無法將本機狀態變更為跨用戶端保持一致,但這些腳本流程正在更新本機 ObjectRef 變數,我們想要在所有用戶端上保持一致,就像 ObjectKind 一樣。
因此,目前設定的方式,我們實際上不應該做任何我們需要按鈕執行的事情。
擺脫此問題的唯一明顯方式,就是讓觸發這些流程的事件成為本機事件。 我們可以這麼做,方法是讓 On State Changed 事件節點觀察 [已選取的本機] 屬性,而不是 [已選取]。
事件現在為本機,這表示...
- 本機事件可以變更共享狀態 ,因此我們現在可以安全地更新共用 的 ObjectKind 變數,而且其值將由 Mesh Visual Scripting 的內建網路功能自動在用戶端之間共用。
- 本機事件無法將本機狀態變更為跨用戶端 保持一致,因此我們仍然無法更新這些腳本流程中的本機 ObjectRef 變數。 我們必須找到另一種方式。
這就是這兩個腳本流程在這些變更之後的外觀:
我們該怎麼做才能設定本機 ObjectRef 變數,使其與這個變數保持一致? 幸運的是,這兩個腳本流程已經建立了一些我們可以遵循的共享狀態:共用 的 ObjectKind 變數。 我們只需要使用 On State Changed 事件來觀察此變數,並根據其值更新本機 ObjectRef 變數:
這是一個很好的方法,因為 觀察共享狀態的 On State Changed 事件可以變更本機狀態,使其與它一致。 這適用於按下按鈕的用戶端、同時存在於相同會議室中的所有其他用戶端,以及稍後將加入會話的所有用戶端。
網路陷阱
高頻率共用更新
根據預設,網格視覺腳本幾乎會共用整個場景狀態。 這非常適合共用,但它也可以偶然偷偷溜進,並造成不必要的網路負載。 例如,下列腳本流程會將轉換輪替的備援更新淹沒網路。 不過,由於所有用戶端都會同時執行,因此任何遠端更新都不會對本機的任何用戶端產生實際影響:
在此情況下,您應該使用本機 腳本範圍 將轉換元件設為每個用戶端的本機。 此外,您可能應該使用 Animator 元件,而不是 On Update 腳本流程來開始使用。
從 Mesh Toolkit 5.2411 起,Mesh 可視化腳本診斷面板和內容 效能分析器 (CPA)會顯示這種建構的「高頻率共用更新」警告。
在 [開始] 上,在每個用戶端上執行
您可能會想要將 On Start 事件視為在工作階段啟動時執行的動作,但在加入工作階段時,實際上會在每個用戶端本機觸發它。 它非常適合用來初始化本機狀態:
不過,當您嘗試使用 On Start 來初始化共享狀態時,您會發現每當任何人加入工作階段時,共享狀態將意外地重新初始化給每個人:
Mesh Visual Scripting Diagnostics 面板(從 Mesh Toolkit 5.2410 起)和內容 效能分析器 (CPA) (從 Mesh Toolkit 5.2411 起)會顯示「會話聯結的共用更新」警告。當偵測到此情況時,會顯示「會話聯結的共用更新」警告。
共用的類型為 -- 但變數指派不是
基於安全性和安全性考慮,共用的視覺腳本變數是強型別。 這表示您在您宣告之腳本變數的 [變數] 元件中選取的類型會定義客戶端之間將同步處理的確切實值類型。
不幸的是,當您更新變數的值時,Unity Visual Scripting 會完全忽略變數的宣告類型。 例如,很容易不小心將 Float 型別值儲存在針對 Integer 類型宣告的變數中。 在本機用戶端內,您的視覺腳本不會注意到此錯誤,因為 Visual Scripting 會在需要時自動將錯誤的 Float 轉換為預期的 整數 。 不過,在跨用戶端同步處理此值時,Mesh Visual Scripting 無法採用相同的自由:「最終一致性」保證會排除在正式發行前小眾測試版中的任何值轉換,而安全性和安全性考慮使得無法接受與遠端用戶端不同的實值類型,而不是針對變數宣告的值類型。
例如,請考慮這個名為 MyIntegerVar 的共用變數宣告:
以下是更新此變數的文稿流程:
可能會發生什麼錯誤? 不幸的是,此範例中使用的隨機 | 範圍腳本節點有兩種類型:一個會產生隨機 Integer 值,另一個會產生隨機浮點數值。 節點選取器面板中這兩個腳本節點之間的差異很微妙:
因此,如果您不小心在該處選取錯誤的隨機 | 範圍腳本節點,您的腳本可能會意外地將 Float 值儲存在 Integer 類型變數中,但該錯誤的 Float 值不會復寫到任何其他用戶端。
請記住,這是您建立的共用變數似乎已停止共享的潛在原因。 未來版本的 Mesh Visual Scripting 可能會在偵測到腳本時警告這類腳本錯誤。