Character Selection
概述
格鬥範例在Quantum中自己執行一個角色選擇模式以:
- 簡化從角色選擇到遊戲遊玩的過渡;及,
- 以一個完全確定性的方式來強化角色選擇規則。
注意事項: 這個方法可以用於任何基於規則的角色選擇的遊戲(比如:MOBA,基於英雄的射擊遊戲等等)。它也可以在回合結束時針對地圖選擇投票來被調整。
角色選擇
角色選擇的視覺效果回饋及規則集被分別執行。前者在Unity而後者在Quantum中完成。這個頁面將介紹角色選擇設定的不同方面,以及它們之間的關聯。
Unity
在Unity中的遊戲場景包含角色選擇的下列部分:
- 選擇UI;
- 角色選擇實體原型;及,
- 針對視覺效果回饋的回調,其反映玩家做出的選擇決定。
所有這些元素可以在CharacterSelect
物件下的場景中找到(Side Bar and UI Camera > Camera > Foreground UI Canvas > CharacterSelect
)。
選擇UI
使用角色檔案圖片來呈現角色選擇給玩家。這些包含:
- 一個圓形遮罩
- 可用角色的一個方形圖案
- 一個有顏色的圓圈以聚焦目前的選擇
選擇UI是完全被動的。它代表的選擇順序及可用角色,是由角色選擇原型所驅動,並且選擇聚焦是由角色選擇回調物件上的CharacterSelectCallback
指令碼所處理。
角色選擇原型
在Unity中的角色選擇UI P1及P2是純粹的視覺效果,並且不影響在Quantum側上的CharacterSelectSystem
中找到的底層邏輯。CharacterSelectSystem
所需的資訊,是分別由角色選擇原型P1及P2所持有。
角色選擇原型只由兩個元件所組成:
Entity Prototype
;及,CharacterSelect
元件。
任何及所有類型的實體原型都需要Entity Prototype
元件,而CharacterSelect
元件被特定地設計,以持有關於可選的鬥士及繫於選擇互動的聲音視覺效果回饋的資訊。
注意事項: 角色選擇原型用於建立一個實體,其只用於在Quantum中持有一個遊戲狀態,因此在Unity中它不需要任何視覺效果代表;因此,它確實有一個與它關聯的實體檢視。
在編輯器中可以設置的欄位是:
Fighter Index
:將能夠控制該角色選擇原型的玩家參照。在格鬥範例中,索引0及1將分別映射到玩家1及2。關於如何將玩家參照指派給玩家的更多資訊,請閱讀操作手冊 > 玩家中的文檔。Fighter Choices
:這個欄位持有一個鬥士資料資產的清單。各個鬥士資料資產代表一個可選擇的角色。 注意事項: 它們在清單中的順序是在Quantum中的角色選擇系統使用的選擇順序。因為選擇UI被解耦,因此需要手動地配對選擇UI中的角色檔案的順序及在角色選擇元件中的鬥士資料資產!Toggle Left / Right SFX
:當向左/右移動選擇時觸發的音效。Select SFX
:當玩家確認他們的角色選擇時觸發的音效。
所有其他欄位都由Quantum模擬中的角色選擇系統來設定及操控。
選擇的視覺效果
CharacterSelectCallback
指令碼處理所有玩家選擇視覺效果回饋。它繼承自SubscriptionBehaviour
基礎類別,其是一個簡單的公用程式上層類別,其處理與Quantum事件及回調訂閱(取消訂閱)的介面。
UI元素設定
在CharacterSelectCallback
指令碼開始工作之前,它需要知道在運行階段它可以/應該操控的UI元素。
首先,需要設定CharacterSelectSet
的數量;在格鬥範例中,它被設定為2,因為預設情況下是針對2名玩家。各個CharacterSelectSet
都有一個 主要物件,其是放置可選擇的角色圖片的物件(在目前的情況是P1及P2),以及一個 選擇元件 的清單,其是以面板形式放置在角色圖片旁邊的聚焦圓形。
注意事項: 角色圖片聚焦面板的排序必須與角色選擇元件中的鬥士選擇相同!
最後,CharacterSelectCallback
需要一個參照到 主要標題 及 觀看文字,其將在進行選擇時被顯示。
- 將呈現 主要標題 直到兩名玩家都已經完成他們的選擇程序。
- 在本機玩家已經確認他們的角色選擇,並且在等待遠端玩家確認他們的選擇時,將顯示 觀看文字。
邏輯
在CharacterSelectCallback
的情形,它正在註冊並且接聽CallbackUpdateView
回調。當模擬(Quantum)將控制權交給檢視(Unity)時,就會觸發這個特定回調。此時,指令碼從Quantum.Game
中獲取最後已驗證幀。然後該幀可以用於篩選元件、迭代結果的集合,以及輪詢它們的資訊。
注意事項: 可以從幀狀態 輪詢。然而, 不 允許從Unity 寫入 到它。這將是非確定性的,並且導致模擬中斷同步。
基於從幀輪詢來的資訊,CharacterSelectCallback
指令碼隨後可以啟用或停用必要的UI元素。
C#
protected override void SubscriptionDispatch(CallbackUpdateView result){
var frame = result.Game.Frames.Verified;
var charSelect = frame.Filter<CharacterSelect>();
bool isSelecting = false;
bool isSpectating = false;
while (charSelect.Next(out var e, out var cs)) {
var obj = characterSelectSets[cs.fighterIndex];
obj.mainObject.SetActive(cs.isSelecting);
for (int i = 0; i < obj.selectionComponents.Length; i++){
obj.selectionComponents[i].SetActive(cs.highlightedCharacterIndex == i && frame.Number % 10 < 5);
}
if (cs.isSelecting != true) continue;
if (result.Game.PlayerIsLocal(cs.playerRef)){
isSelecting = true;
} else {
isSpectating = true;
}
}
mainHeader.SetActive(isSelecting);
spectatingText.SetActive(isSpectating && !isSelecting);
}
Quantum
在Quantum側,角色選擇流程發生在CharacterSelectSystem
之中:
- 玩家被新增到可用玩家清單;
- 清單中的前兩名玩家被允許選擇他們的角色;及,
- 輸家被清單中的下一名可用玩家所取代。
玩家指派
接收一個玩家的RuntimePlayer
資料將觸發ISignalOnPlayerDataSet
信號。
C#
public void OnPlayerDataSet(Frame f, PlayerRef player){
var list = f.ResolveList(f.Global->playerOrderList);
list.Add(player);
var filter = f.Filter<CharacterSelect>();
while (filter.NextUnsafe(out var e, out var cs)) {
if (cs->isSelecting == true) continue;
list.Remove(player);
cs->playerRef = player;
cs->highlightedCharacterIndex = 0;
cs->isSelecting = true;
cs->lastInput = InputFlag.NONE;
break;
}
}
CharacterSelectSystem
執行信號並且新增觸發它的玩家到幀中持有的全域清單playerOrderList
。如果有一個可用的位置,他們將被指派對CharacterSelect
元件的控制權,並且因此被授與選擇角色參加下一場對戰的能力。如果沒有可用的位置,他們將必須等待他們的回合;請參見贏者留下章節。
選擇驗證
CharacterSelectionSystem
在Quantum側執行角色選擇規則。
系統首先確保在一個有效的空間之中完成選擇,方法是根據選擇索引在清單中迴圈被選擇的角色。
C#
if ((inputFlag & InputFlag.Left) != 0) {
cs->highlightedCharacterIndex--;
if (cs->highlightedCharacterIndex < 0)
cs->highlightedCharacterIndex = cs->fighterChoices.Length - 1;
f.Events.PlayAudioEffect(FPVector2.Left, cs->toggleLeftSFX);
} else if ((inputFlag & InputFlag.Right) != 0){
cs->highlightedCharacterIndex++;
if (cs->highlightedCharacterIndex >= cs->fighterChoices.Length)
cs->highlightedCharacterIndex = 0;
f.Events.PlayAudioEffect(FPVector2.Left, cs->toggleRightSFX);
}
各個角色有多個服裝。將套用在角色的服裝變數取決於用於確認角色選擇的動作按鈕。
C#
if ((inputFlag & InputFlag.LP) != 0) {
SelectionMade(f, cs, 0);
} else if ((inputFlag & InputFlag.LK) != 0) {
SelectionMade(f, cs, 1);
} else if ((inputFlag & InputFlag.HP) != 0) {
SelectionMade(f, cs, 2);
} else if ((inputFlag & InputFlag.HK) != 0) {
SelectionMade(f, cs, 3);
}
SelectionMade()
方法觸發確認事件,以更新檢視及發射ISignalOnFighterSelected
信號(由FighterSystem
執行)。ISignalOnFighterSelected
具現化被選擇的角色並且設定它(位置、旋轉、相同雙胞胎情況的服裝更換等等),並且設定f.Global->ready
以反映兩個玩家是否已經做出他們的選擇及他們的角色設定。
贏者留下
格鬥範例執行一個街機風格的「贏者留下」對戰;換句話說,輸家交出控制權,而在觀眾/等待清單上的下一位玩家被給予機會來獲得冠軍。玩家被選擇的順序由他們加入遊戲並且被新增到f.Global->playerOrderList
的順序來定義——請參見CharacterSelectSystem
中的OnPlayerDataSet
方法。
在NextMatchState
資產中處理這個流程。首先,它從對戰中移除了失敗的玩家,方法是調用CharacterSelectSystem.RemoveFighter
;這也針對該鬥士重新設定了CharacerSelect
元件,這樣新的玩家能夠選擇他們自己的角色。最後,它以ResetRoundState.ResetFighters
重新設定了剩下的鬥士到他們的原始位置。
當新的玩家已經選擇他們的角色,下一場對戰開始。
新增新的可選擇的角色
為了新增新的可選擇的角色,請遵循這些步驟:
- 在
CharacterSelect
元件的DSL定義中增加鬥士選擇陣列的大小——array<asset_ref<FighterData>>[4] fighterChoices
; - 在角色選擇P1及P2原型上的
CharacterSelect
元件中,新增角色的FighterData
資產到fighterChoices
欄位。 - 新增角色描述到選擇UI。
- 確保選擇UI中針對P1及P2的角色順序,匹配在
CharacterSelect
元件的fighterChoices
陣列中的FighterData
資產的順序。