Input
概述
在格鬥遊戲中的輸入需要玩家之間的快速傳輸、精確度及在一定時間內追蹤狀態輸入流的能力,而不是簡單地耗用它。
這個頁面將介紹Unity捕捉了哪些玩家輸入,以及隨後如何在Quantum中評估及計算這些值。
輸入架構
通常建議輸入架構盡可能小,以保持較低的頻寬。在格鬥範例中,透過將單一列舉InputFlag
作為輸入的一部分,將其推向了極限。
chsarp
input {
InputFlag inputFlag;
}
InputFlag
列舉定義明確地指派兩個值的幂,從而允許使用位元類的操作來操控InputFlag
。這讓InputFlag
能夠同步持有多個輸入值。
C#
// Input flags used to represent the directional and button inputs
enum InputFlag {
NONE = 0,
Left = 1,
Right = 2,
Down = 4,
Up = 8,
LP = 16, // light punch
LK = 32, // light kick
HP = 64, // heavy punch
HK = 128, // heavy kick
Directions = 15, // Used by the systems to extract the input directions from the input struct
Buttons = 240, // Used by the systems to extract the button input from the input struct
}
Unity側
格鬥範例執行兩個不同的輸入介面,行動裝置(UI按鈕)及電腦(鍵盤及遊戲搖桿)。取決於組建所運行的平台,一個或其他輸入將被列入考慮。
雖然輸入介面各有不同,在不同裝置間的輸入邏輯維持相同。
輸入邏輯
在QuantumDemoInput.cs
指令碼中設定輸入邏輯,並且其訂閱到PollInput
回調。
鍵盤輸入
在QuantumDemoInput.cs
中的KeycodeSet
架構中設定的各個按鈕,都由一個布林值來代表。當布林值評估為真,輸入架構中的InputFlag
參數持有的值將相應地更新。
C#
if (UInput.GetKey(Left)){
input.inputFlag |= InputFlag.Left;
}
行動裝置輸入
行動裝置輸入被分為兩個部分;一個UI指令碼名為MobileJoystick
,其處理按鈕的按下值,及QuantumDemoInput.cs
中的邏輯,其持有參照到所有活躍中的MobileJoystick
指令碼並且在輪詢輸入時耗用它們的值。
MobileJoystick
呈現了兩個旗標參數,以允許在所有8個方向上的移動;Quantum.InputFlag.Up
及 Quantum.InputFlag.Right
旗標代表對角線右上。如果flag1
及flag2
被設定為相同的值,那麼它被認為是獨一無二的輸入。不論各個UI按鈕的設定為何,位元類的操作允許值指派保持相同。
C#
public class MobileJoystick : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler{
public Quantum.InputFlag flag1;
public Quantum.InputFlag flag2;
public Quantum.InputFlag FlagValue { get; private set; }
public void OnPointerEnter(PointerEventData eventData){
FlagValue = flag1 | flag2;
}
public void OnPointerExit(PointerEventData eventData){
FlagValue = Quantum.InputFlag.NONE;
}
}
QuantumDemoInput
簡單地迭代已知MobileJoystick
指令碼的陣列,並且使用一個位元類,或由將各個按鈕持有的值相加在一起。
C#
for (int i = 0; i < mobileButtons.Length; i++){
input.inputFlag |= mobileButtons[i].FlagValue;
}
Quantum側
在模擬側(Quantum),需要在多個幀之間追蹤輸入,以允許在多個幀上的序列輸入觸發的連擊以及需要持續按下按鈕的蓄能攻擊。
使用InputCommandData
資產在編輯階段定義輸入序列,並且在運行階段使用InputBuffer
元件來追蹤它。
輸入命令資料
InputCommandDataBase
是一個抽象資產類別。它用於定義具體資產,該資產將特定地執行TestCmdPair()
以滿足它們的需要。
C#
public abstract unsafe partial class InputCommandDataBase{
/// The parameter set for the forward and backward version of the input. Should be a fixed point parameter.
public CAParameters forDirectionParameter;
public CAParameters backDirectionParameter;
/// The forward and backward version of the command
public InputFlag[] forDirectionInputSequence;
public InputFlag[] backDirectionInputSequence;
public void TestCmdPair(Frame f, InputBuffer* i, int side, CustomAnimator* a, ref QList<InputValue> dSeq);
protected abstract bool TestCmd(Frame f, InputBuffer* iB, int side, InputFlag[] flagArray, ref QList<InputValue> directionalSequence);
}
在格鬥範例中包含的具體執行方式是:
InputCommandData
;及,ChargeInputCommandData
。
InputCommandData
用於啟用由玩家執行輸入序列而觸發的特別移動。ChargeInputCommandData
用於定義輸入序列,該序列透過持有相同的輸入,在多個幀上蓄能。
輸入緩衝
輸入作為幀的一部分,需要分別追蹤各個玩家的輸入。完成這一點的最方便及靈活的方法是在各個玩家控制的實體上都有一個專門用於此目的的元件;這是格鬥範例使用的方法。
InputBuffer
元件用於追蹤3個不同的事情:
- 輸入方向;
- 按下的按鈕;及,
- 輸入已經被按下或放開的時間量。
InputBuffer
由InputBufferSystem
更新。
C#
component InputBuffer{
list<InputValue> directionalSequence;
list<InputValue> buttonSequence;
array<InputTimer>[8] inputTimers;
}
InputValue
類型是在DSL中定義的簡單架構。它含有InputFlag
的複本以及它關聯的幀數。因為方向的輸入及動作按鈕序列被分別操控,它們在InputBuffer
元件中也被分別追蹤;透過保持兩個InputValue
清單來完成這點。
C#
struct InputValue{
InputFlag inputFlag;
Int32 frame;
}
使用InputTimer
架構來追蹤一個特定輸入已經被按下或放開的時間量。
C#
struct InputTimer{
FP timeDown;
FP timeUp;
}
輸入緩衝系統
InputBufferSystem
有兩個目的:
- 在輸入架構中檢查目前的
InputFlag
;及, - 更新
InputBuffer
元件。
在評估目前輸入的輸入旗標之前,分開方向的及按鈕輸入。
C#
InputFlag dirFlag = input->inputFlag & ~(InputFlag.Buttons);
InputFlag btnFlag = input->inputFlag & ~(InputFlag.Directions);
這個分離有助於追蹤玩家為了觸發移動集而採取的動作。首先UpdateInput()
更新InputBuffer
元件中的InputTimer
,方法是檢查一個輸入是否被按下:
- 按下:針對該輸入的
InputTimer.TimeDown
被增加。 - 未按下:如果該輸入在該幀期間沒有被按下,並且已經超過它的重新設定容限,則重新設定針對該輸入的
InputTimer.TimeDown
。這樣做是為了實現蓄能移動(比如:蓄能幾幀,然後按下前進及一個按鈕)。
在更新所有InputTimers
之後,InputBufferSystem
以這些新值來更新CustomAnimator
的參數。
最後一步,InputBufferSystem
提取由InputBufferComponent
持有的輸入序列清單。目前輸入的方向值與元件中持有的先前的InputValues
進行檢查,並且如果兩個值不同的話則進行更新。然後,在所有與各個玩家的鬥士關聯的InputCommandData
資產之中,該清單作為參數被傳送到TestCmdPair()
方法。
這個執行方式結合了前述的角度,以允許玩家來輸入一個附有更改之間的容限(預設大約10幀)的輸入序列。
舉例而言,如果玩家的鬥士持有InputCmd_QuarterCircle
資產,這將觸發QCF_F
動畫參數,並且在下列的值組成輸入序列的情況下執行關聯行為:
C#
{frame = 80, value = 4} // Down
{frame = 90, value = 6} // Down + Right
{frame = 100, value = 2} // Right
然後這將註冊為一個正的輸入序列,因為命令緩衝是32個輸入。
觸發特殊移動
在編輯階段在Unity動畫工具中設定特殊移動,並將其嵌入到 CustomAnimatorGraph
資產之中。在運行階段,InputBufferSystem
簡單地更新CustomAnimator
元件的參數。
如果當CustomAnimatorUpdater
在CustomAnimator
元件上調用Update()
時,滿足了所有過渡參數(請參見CustomAnimatorSystem
)那麼觸發移動。在離開目前的狀態並且進入新的狀態時,針對相應的狀態來發射SignalOnAnimatorStateExit
及ISignalOnAnimatorStateEnter
。
注意事項: 在InputBufferSystem
中更新的參數都屬於FixedPoint
類型。這樣做是為了讓在幾個幀之內不執行新的輸入的玩家,不會意外地基於先前的幀快取的輸入,來執行攻擊。
舉例而言,只要同時滿足下列條件,可以從任何狀態來觸發Uppercut_LP狀態:
- Z軸上的運動(ZMotionF)大於0且小於0.15;
- 輕拳輸入時間大於0且小於0.15;
- 角色目前位於地上(也就是InAir為偽);及,
- 角色目前處於允許它們執行一個特殊攻擊的狀態(也就是CanSpecialAtk為真)。
當FixedPoint
參數在InputBufferSystem
中持續更新時,則直接在系統中更新,或作為傳送到InputCommandData
之中的輸入序列的結果來更新;另一方面,布林值參數是FighterAnimatorState
的一部分,並且在角色第一次進入目前狀態時設定。