斯坦福UE4C++課程P66-P69游戲標(biāo)簽GameplayTags
FGameplayTag:(F開頭表示是結(jié)構(gòu)體)
FName wrapped in Struct 用結(jié)構(gòu)體封裝的FName
Alternative for:bool,enum,F(xiàn)Name 能夠被bool、enum、FName替代
created via Project Settings 通過項(xiàng)目設(shè)置創(chuàng)建
Editor support to easily select GameplayTags 從編輯器可以輕易處理GameplayTags
FGameplayTagContainer(Wrapped TArray of GameplayTags)FGameplayTagContainer使用TArray作為GameplayTags的容器
Extensively used by GAS 在GAS中被廣泛使用
作者給了一些GameplayTags的學(xué)習(xí)資源,主要是官方文檔。
首先我們到ActionRoguelike.Build.cs構(gòu)建文件把GameplayTags模塊包含到項(xiàng)目中,這樣我們就能在C++中使用GameplayTags了。
在SActionComponent類中添加
權(quán)限給滿,因?yàn)槲覀兩院笠谒{(lán)圖里使用它,純粹為了方便。
這里我們不使用指針,所以必須包含該結(jié)構(gòu)體的頭文件(編譯器需要知道該變量的大?。绻侵羔?,其大小是固定的,就無需包含頭文件。
FGameplayTagContainer中可以加是否正在沖刺、是否正在攻擊、是否被擊倒的這些信息。然后其他系統(tǒng)可以從這個容器中獲取到這些信息,玩家目前在做什么,然后以此為依據(jù)去做決定。
下面我們在SAction類添加
在Action啟用時,我們把GrantTags中的所有Tag都放入ActiveGameplayTags中;停用時全部移出:
BlockedTags先不加,我們在SActionComponent類TickComponent函數(shù)添加debug信息,看看效果再說:
我們讓ActionComponent附加在的Actor名字、因Action發(fā)出而附加的Tags打印出來。
編譯,進(jìn)入project settings->GameplayTags->Add New Gameplay Tag,新建兩個tag:
"Action.Sprinting"、"Action.Attacking"

到Action_Sprint藍(lán)圖,發(fā)現(xiàn)右側(cè)Tags多了Grants Tags、Blocked Tags
調(diào)整如下:
、

表示我們希望沖刺時會生成Action.Sprinting的標(biāo)簽,且會受到Action.Attacking的阻止(沖刺時不能攻擊)。
在其他SAction繼承藍(lán)圖類也調(diào)整tags:

表示發(fā)射projectile的三種action都算Attacking,且沖刺時無法施放。
現(xiàn)在我們攻擊時(魔法球、Dash、黑洞),debug信息出現(xiàn)在左上角。


接下來我們添加沖刺時阻止攻擊、攻擊時阻止沖刺邏輯。
在SAction的public下添加
函數(shù)體:
在SActionComponent添加判定,如果CanStart函數(shù)返回false,就不能開始Action。
我們還添加debug信息,表示如果當(dāng)前不能開始此Action,就跳過當(dāng)前循環(huán),判定下一個Action,并打印失敗的Action名字
如果存在不能開始的Action,因?yàn)槲覀冊赟Action_ProjectileAttack類中寫了,一旦startAction函數(shù)執(zhí)行,最后一個語句會執(zhí)行stopAction。但存在不能開始的Action的話,我們就沒有添加過tag,執(zhí)行stopAction移除tag會出現(xiàn)錯誤。
所以我們在SAction添加一個bool變量,和獲取它的函數(shù):
StartAction_Implementation函數(shù)末尾加上
StopAction_Implementation函數(shù)末尾加上
并在開頭添加
表示如果從未執(zhí)行過StartAction,就不要執(zhí)行StopAction。(PV操作的感覺??)
現(xiàn)在編譯運(yùn)行游戲,攻擊時沖刺,打印"Failed to run:Sprint";沖刺時攻擊,打印"Failed to run:PrimaryAttack"。
但現(xiàn)在攻擊時仍能攻擊。
在CanStart函數(shù)開頭添加:
表示,如果一個action正在執(zhí)行(還未執(zhí)行stopAction),那么就不應(yīng)該重復(fù)執(zhí)行該action。
現(xiàn)在仍有一個問題,就是扔魔法球時雖然不能扔魔法球了,但可以扔Dash和黑洞出去。
我們只需添加blocktag即可:

現(xiàn)在攻擊時,會阻擋攻擊action。

意思就是,一個action執(zhí)行,要執(zhí)行另一個,將會把其信息打印在屏幕上并失敗。

下面我們用游戲標(biāo)簽添加門和鑰匙系統(tǒng)。
首先到project settings新建三個tag

更改leverBP如下:(Tag提升為變量,命名KeyCard)

在寶箱TreasureChest藍(lán)圖的Interact事件后添加(之前寫的開寶箱的邏輯不變)

Has Tag節(jié)點(diǎn)判斷Active Gameplay Tags中是否有Required Key Card作為Branch條件,表示如果tag匹配,才能開寶箱。Exact Match表示,只有tag完全一致才返回true。
我們可以給場景中實(shí)例化的寶箱分配三種顏色之一的tag,同理給杠桿lever分配tag?,F(xiàn)在只有l(wèi)ever的tag和寶箱的tag相同,才能在和lever交互后獲取到相應(yīng)tag,才能開相應(yīng)寶箱。
如果我們?nèi)∠催xExact Match,表示Required Key?Card屬于Active Gameplay Tags中的一類Tag就行。舉個例子,我們?nèi)∠催xExact Match,然后一個箱子設(shè)置Required Key?Card為KeyCard,一個設(shè)置KeyCard.Blue,和lever獲取到KeyCard.Blue,那么只要和lever交互,就能夠開這兩個箱子(一個完全匹配(Exact Match,KeyCard.Blue==KeyCard.Blue),一個只是一類(KeyCard.Blue屬于KeyCard,也能開))。


這一P我們學(xué)習(xí)用GameplayTags抵擋攻擊(parrying mechanic)。
首先添加tag:Status.Parrying。
在魔法球C++類OnActorOverlap函數(shù)添加
現(xiàn)在測試,我們先到角色類藍(lán)圖Action組件,設(shè)置Active Gameplay Tags添加Status.Parrying,這樣角色一出生就有這個Tag,便于測試。然后我們給魔法球勾選Status.Parrying,表示給C++的ParryTag賦值為Status.Parrying。
現(xiàn)在AI攻擊玩家,玩家會反彈魔法球:

下面我們給AI角色添加Action組件。頭文件添加ActionComp,構(gòu)造函數(shù)初始化組件。
AI藍(lán)圖勾選Status.Parrying,讓AI一開始也有此Tag?,F(xiàn)在AI發(fā)射魔法球,打到玩家反彈,反彈到AI又會反彈,直到反彈到打不中二者,而擊中墻壁為止。玩家攻擊AI也是 一樣。
我們稍微修改,讓玩家按R能夠有反彈能力1秒。
新建Action_Parry,繼承自SAction(首先要在C++給Stop Action函數(shù)添加BlueprintCallable,讓我們可以在藍(lán)圖調(diào)用;且需要調(diào)用父類節(jié)點(diǎn),實(shí)現(xiàn)基類功能):

在Input->Action Mapping,新建名為Parry的Action映射,鍵為R(黑洞改為E,交互改為F)。我們?nèi)∠催x玩家和AI的開局就有ParryTag,給玩家添加Action_Parry的能力(在Default Actions數(shù)組新添加Action_Parry)。

在角色藍(lán)圖把R鍵和Parry動作綁定(C++中就是BindAction):

現(xiàn)在玩家按R,就開啟了1秒內(nèi)的反彈能力。
最后作者加了AI受傷害時,傷害數(shù)字的顯示,之前我做過了。
還有AI血條的位置可以用之前做的World Offset來調(diào),我們給z方向上加90,讓血條被創(chuàng)建在AI頭上。