斯坦福UE4C++課程P59-P62控制臺(tái)變量,游戲模式規(guī)則
首先我們?cè)赟GameModeBase添加OnActorKilled函數(shù),加上virtual關(guān)鍵字以便子類(lèi)能重寫(xiě)。
在attribute組件類(lèi)ApplyHealthChange函數(shù)添加判斷屬性組件擁有者死亡的邏輯:
OnActorKilled函數(shù):
我們以函數(shù)名RespawnPlayerElapsed設(shè)置定時(shí)器,函數(shù)RespawnPlayerElapsed:
僅僅是解除了controller和玩家角色的綁定,并且讓controller和從playerstart重生的玩家綁定。
現(xiàn)在有UI上的bug,玩家死亡后重生,血條將不再更新,因?yàn)樵赽eginplay時(shí)我們?cè)诮巧?lèi)藍(lán)圖創(chuàng)建了血條控件,而血條控件的事件圖表中,我們是用控件的construct來(lái)對(duì)血條百分比進(jìn)行控制,在血條首次被創(chuàng)建時(shí)正常,但玩家重生時(shí)并不會(huì)重新創(chuàng)建血條。這時(shí)新玩家用的仍是上一個(gè)死亡玩家的血條控件。這個(gè)問(wèn)題后面再解決。

下面我們學(xué)習(xí)用于調(diào)試和游戲平衡的控制臺(tái)變量
我們?cè)赟GameModeBase.cpp添加
ECVF_Cheat表示在游戲打包時(shí),該控制臺(tái)變量將被忽略。
在SpawnBotTimerElapsed函數(shù)開(kāi)頭判斷
表示如果該控制臺(tái)變量被禁用,將不能夠執(zhí)行剩下的函數(shù)體,即不能生成Bot。
接著在屬性組件類(lèi)添加第二個(gè)控制臺(tái)變量,表示給傷害乘以倍數(shù)(默認(rèn)為1.0f):
在ApplyHealthChange函數(shù)添加:
表示只要擁有屬性組件類(lèi)的Actor受到傷害時(shí)都會(huì)乘上該值。這比較粗糙,但是一個(gè)好的開(kāi)始,可以用來(lái)測(cè)試游戲中的傷害值是否合適。
最后一個(gè)控制臺(tái)變量添加在SInteractionComponent,之前我們?cè)谶@里添加了交互時(shí)的drawdebug的線和球(綠色表示交互成功,紅色失?。@個(gè)功能之前必須在代碼里通過(guò)注釋掉drawdebug的語(yǔ)句才能開(kāi)關(guān)。我們把它設(shè)置為控制臺(tái)變量,在控制臺(tái)開(kāi)關(guān),就方便多了:
現(xiàn)在打開(kāi)控制臺(tái),輸入su,會(huì)出現(xiàn)上面的三個(gè)控制臺(tái)變量。
現(xiàn)在我們給傷害乘5,輸入su.DamageMultiplier空格5,如果后面不加上傷害倍數(shù)值,將顯示當(dāng)前傷害倍數(shù)。

下面我們新建我們的第一個(gè)Blueprint Function Library,添加兩個(gè)靜態(tài)函數(shù)
tip:在C++中,如果ufunction函數(shù)的形參是引用(不是常引用),在藍(lán)圖中該函數(shù)節(jié)點(diǎn)將多出該形參的輸入引腳。
SGameplayFunctionLibrary.h
SGameplayFunctionLibrary.cpp
在魔法球C++類(lèi),把之前OnActorOverlap添加傷害的語(yǔ)句換為:
現(xiàn)在編譯,仍有一定問(wèn)題,魔法球打到AI身上,實(shí)際上是和膠囊體組件發(fā)生重疊,進(jìn)而觸發(fā)傷害,但是膠囊體組件是沒(méi)有辦法模擬物理的,所以我們必須要讓魔法球忽略掉膠囊體組件,和mesh進(jìn)行重疊(mesh可以模擬物理,可以被ApplyDirectionDamage作用)。
在AI角色C++類(lèi)構(gòu)造函數(shù)添加
表示我們把膠囊體組件對(duì)WorldDynamic通道設(shè)置為忽略,把網(wǎng)格體組件設(shè)置為可以觸發(fā)重疊事件。
現(xiàn)在,我們攻擊AI,只要AI開(kāi)啟了模擬物理(我們?cè)O(shè)置了AI死亡時(shí)會(huì)開(kāi),活著沒(méi)開(kāi)),就會(huì)被魔法球施加沖量作用:

現(xiàn)在,AI死亡時(shí),其身體(膠囊體)仍會(huì)阻擋玩家,我們?cè)O(shè)置當(dāng)它死亡時(shí),膠囊體設(shè)置為無(wú)碰撞。但這意味著地板不能與其碰撞了,所以同時(shí)我們?cè)O(shè)置AI不能動(dòng)了:
在SAICharacterC++類(lèi)NewHealth<=0死亡時(shí):
另外,我們和寶箱、生命藥水interact時(shí),是以角色眼睛是否看向目標(biāo)為標(biāo)準(zhǔn)的,這在第一人稱(chēng)視角游戲是很好的方法,但第三人稱(chēng)游戲以我們的十字準(zhǔn)星方向看去為標(biāo)準(zhǔn)會(huì)好一點(diǎn)。所以我們?cè)赟Character類(lèi)添加:
表示我們使用相機(jī)為視線出發(fā)點(diǎn),而不是角色眼睛。
剩下的工作,我們把上帝模式時(shí)改成只能回血、不能掉血,所以在屬性組件類(lèi)ApplyHealthChange函數(shù)開(kāi)頭換成
最后,我們修復(fù)玩家死亡重生時(shí),血條不被重新創(chuàng)建的問(wèn)題。
新建playercontroller藍(lán)圖類(lèi)PlayerControllerBP,把玩家角色藍(lán)圖beginplay時(shí)創(chuàng)建顯示主HUD的節(jié)點(diǎn)移到PlayerControllerBP的beginplay節(jié)點(diǎn)后。在GameModeBP中選擇Player Controller Class為PlayerControllerBP。
這沒(méi)有解決掉問(wèn)題,但比原來(lái)好一點(diǎn)。

最后是作業(yè)5:

首先新建繼承自PlayerState的credits類(lèi),命名為SPlayerState:
這里需要了解Gameplay架構(gòu)(GameMode、GameState、PlayerController等),其中可以了解到PlayerState可以根據(jù)玩家Controller獲得。我們根據(jù)這一點(diǎn)來(lái)獲取SPlayerState中的Credits。
SPlayerState.h
SPlayerState.cpp
在SGameModeBase類(lèi)的OnActorKilled函數(shù)加上:
當(dāng)玩家殺死AI時(shí),才會(huì)給編號(hào)為0的玩家加上10分。
我們讓Credits在UI實(shí)時(shí)更新,給分?jǐn)?shù)綁定函數(shù):

現(xiàn)在玩家殺死AI就會(huì)漲10分,玩家死亡、AI被AI殺死都不會(huì)漲分。
下面我們給Health Potion、Coins新建一個(gè)基類(lèi)SPowerupBase,繼承自AActor和ISGameplayInterface:
SPowerupBase.h
SPowerupBase.cpp
改寫(xiě)的Health Potion類(lèi):
新建SCoinPickUp類(lèi),與其交互可加Credits:
給coin新建藍(lán)圖類(lèi),分配mesh,放入場(chǎng)景中?,F(xiàn)在與其交互,會(huì)增加20分(默認(rèn)),且其被銷(xiāo)毀。
最后我們用EQS讓Coin和Health Potion在場(chǎng)景里隨機(jī)生成。
這里可以借鑒之前隨機(jī)生成AI的方法,即新建EQS找隨機(jī)生成點(diǎn),然后新建繼承自EQSTestingPawn的藍(lán)圖類(lèi),EQS選擇前者(純粹方便查看隨機(jī)生成點(diǎn)),然后在SGameModeBase中生成AI。
所以我們?cè)赟GameModeBase頭文件添加:
在StartPlay函數(shù)中寫(xiě)生成邏輯:
OnCoinQueryCompleted函數(shù)和OnHealthPotionQueryCompleted函數(shù)分別生成硬幣和生命藥水:
現(xiàn)在存在問(wèn)題,就是多個(gè)硬幣/藥水可能重疊在一個(gè)生成位置,后續(xù)找解決辦法。
