Input
概述
輸入是Quantum核心架構的關鍵組成部分。 在確定性網絡庫中,系統的輸出是固定的,並且在給定特定輸入的情況下是預先確定的。 這意味著,只要網路中所有用戶端的輸入相同,輸出也將相同。
在DSL中定義
可以在任何DSL檔案中定義輸入。 例如,一個有移動方向和單個跳轉按鈕的輸入結構體看起來像這樣:
Qtn
input
{
button Jump;
FPVector3 Direction;
}
服務器負責批次處理並發送完整勾選集的輸入確認(所有玩家的輸入)。因此,這個結構應該盡可能地保持最小的大小。
命令
確定性命令是Quantum的另一種輸入路徑,可以有任意數據和大小,這使得它們非常適合特殊類型的輸入,如“購買此物品”、“傳送到某個地方”等。
Unity中輪詢
要向Quantum類比發送輸入,請在Unity內部輪詢。為此,請在遊戲場景中的MonoBehaviour內訂閱PollInput
回調。
C#
private void OnEnable()
{
QuantumCallback.Subscribe(this, (CallbackPollInput callback) => PollInput(callback));
}
然後,在回調中,從輸入源讀取並填充輸入結構。
C#
public void PollInput(CallbackPollInput callback)
{
Quantum.Input i = new Quantum.Input();
var direction = new Vector3();
direction.x = UnityEngine.Input.GetAxisRaw("Horizontal");
direction.y = UnityEngine.Input.GetAxisRaw("Vertical");
i.Jump = UnityEngine.Input.GetKey(KeyCode.Space);
// convert to fixed point.
i.Direction = direction.ToFPVector3();
callback.SetInput(i, DeterministicInputFlags.Repeatable);
}
注意:這裡的浮點到定點轉換是確定性的,因為它是在與類比共享之前完成的。
優化
儘管Quantum 3 delta壓縮了輸入,但為了獲得最佳頻寬,使原始Input
數據盡可能緊湊仍然是一種很好的做法。 以下是一些優化它的方法。
按鈕
Button
類型在Input DSL定義中使用,而不是使用布林值或類似的資料類型來表示按鍵。 這是因為它每個實例只使用一個位元,因此在可能的情況下使用是有利的。 雖然它們在網絡上只使用一個位元,但在本地它們將包含更多的遊戲狀態。 這是因為單個位元僅代表當前幀中是否按下了按鈕,其餘資訊在本地計算。
按鈕定義如下:
Qtn
input
{
button Jump;
}
從Unity腳本輪詢按鈕值時的一個重要細節是輪詢當前按鈕狀態,即是否在當前幀按下按鈕。 通過這種管道,Quanutm會自動設定內部内容,允許用戶在類比程式碼上輪詢特定狀態,如WasPressed
, IsDown
和WasReleased
。
這意味著,在Unity中,您不需要設定特定的狀態,如GetKeyUp()
或GetKeyDown()
,因為使用這些狀態實際上是有問題的,因為Unity的運行速度與Quantum不同,因此其中一些狀態會遺失,使輸入感覺響應性降低。
因此,在Input結構中設定button
的值時,始終輪詢當前按鈕狀態,如下所示:
C#
// In Unity, when polling a player's input
input.Jump = UnityEngine.Input.GetKey(KeyCode.Space);
按鈕的狀態也可以在Quantum類比程式碼中更新,這對於類比機器人等非玩家實體的按鈕變化特別有用,如果用戶選擇使用Input結構和button
類型來更新機器人實體。 為了實現這一點,有必要在類比程式碼每幀中設定按鈕的狀態,如下所示:
C#
// In Quantum code
input.button.Update(frame, value);
這樣,特定狀態(按下、關閉、釋放)也會在內部生成。 不每幀更新按鈕狀態會導致這些狀態被錯誤設定。
編碼方向
在典型的設定中,運動通常使用方向向量來表示,通常在DSL
檔案中定義如下:
Qtn
input
{
FPVector2 Direction;
}
然而,FPVector2
由兩個'FP'組成,佔用16個位元組的數據,這可能是發送的大量數據,特別是在同一個房間裏有許多用戶端的情況下。
優化它的一種方法是擴展Input
結構並將方向向量編碼為Byte
,而不是每次發送完整的向量。 其中一種實現管道如下:
首先,我們像普通一樣定義我們的輸入,但不是包含一個用於方向的FPVector2
,而是用一個Byte
替換它,在那裡我們將存儲編碼版本。
Qtn
input
{
Byte EncodedDirection;
}
接下來,以擴展組件的管道擴展輸入結構體(請參閱:添加功能):
C#
namespace Quantum
{
partial struct Input
{
public FPVector2 Direction
{
get
{
if (EncodedDirection == default)
return default;
Int32 angle = ((Int32)EncodedDirection - 1) * 2;
return FPVector2.Rotate(FPVector2.Up, angle * FP.Deg2Rad);
}
set
{
if (value == default)
{
EncodedDirection = default;
return;
}
var angle = FPVector2.RadiansSigned(FPVector2.Up, value) * FP.Rad2Deg;
angle = (((angle + 360) % 360) / 2) + 1;
EncodedDirection = (Byte) (angle.AsInt);
}
}
}
}
此實現允許與以前相同的用法,但它只佔用單個位元組,而不是16個位元組。 它通過使用Direction
内容來實現這一點,該内容會自動對EncodedDirection
中的值進行編碼和解碼。