TomLooman_ActionRoguelike_第五章藍(lán)圖
該專欄用于保存對(duì)TomLooman的ActionRoguelike項(xiàng)目的學(xué)習(xí)筆記,學(xué)習(xí)過程中的思考與記錄不一定準(zhǔn)確。
教程參考:https://github.com/tomlooman/ActionRoguelike
基于UE5.0的項(xiàng)目實(shí)現(xiàn):https://github.com/CarolBaggins2023/TomLooman_ActionRoguelike_Tutorial

2023_08_01
藍(lán)圖:藍(lán)圖的互動(dòng)與鑄造、在藍(lán)圖中制作動(dòng)畫、投射物藍(lán)圖與碰撞
另外自己實(shí)現(xiàn)了Character朝向隨攻擊變化(無動(dòng)畫)
?
我們創(chuàng)建了一個(gè)操縱桿的藍(lán)圖類,這個(gè)藍(lán)圖類直接繼承Actor類,不像之前那些藍(lán)圖類繼承我們?cè)贑++中創(chuàng)建的類。
我們先希望與操縱桿互動(dòng)能夠改變桿的角度。
?
因?yàn)椤芭c操縱桿互動(dòng),改變操作桿角度”這一功能與之前的“與寶箱互動(dòng),打開寶箱”邏輯相同,所以這里可以復(fù)用之前定義的Interface類,讓操縱桿類和寶箱類一樣繼承接口類。在藍(lán)圖中表現(xiàn)為添加一個(gè)接口,如下。

?
我們已經(jīng)在Character上的ActorComponent上定義了一個(gè)函數(shù),那個(gè)函數(shù)實(shí)現(xiàn)“檢測(cè)互動(dòng)物體是否實(shí)現(xiàn)了接口函數(shù)”和“在互動(dòng)物體上調(diào)用接口函數(shù)”兩個(gè)功能,所以這里我們要實(shí)現(xiàn)(重寫/覆蓋)Interface類中的接口函數(shù)。如下,添加藍(lán)圖中的Event(藍(lán)圖中的Event和C++中的函數(shù)類似,我們實(shí)現(xiàn)了這個(gè)Event就相當(dāng)于實(shí)現(xiàn)了接口函數(shù),藍(lán)圖中Event和Function的區(qū)別下面講)

?
我們實(shí)現(xiàn)了調(diào)用接口函數(shù)后旋轉(zhuǎn)操縱桿,還希望這個(gè)操縱桿能控制其它東西,這里我們用操縱桿控制爆炸桶爆炸。
這就需要在操縱桿實(shí)現(xiàn)的接口函數(shù)中,調(diào)用爆炸桶中的ForceComp->FireImpulse(),類似Projectile擊中爆炸桶后的邏輯。
所以我們?cè)诒ㄍ暗乃{(lán)圖中創(chuàng)建一個(gè)事件,在該事件中執(zhí)行FireImpulse()。需要注意的是,如果像這樣在藍(lán)圖中調(diào)用成員變量(ForceComp),在成員變量的UFUNCTION中必須要BlueprintReadOnly或者BlueprintReadWrite的Specifier。

那么怎么在操縱桿實(shí)現(xiàn)的接口函數(shù)中調(diào)用爆炸桶的事件呢?我們可以將爆炸桶作為操縱桿的一個(gè)成員變量,然后就自然地相當(dāng)于在成員函數(shù)中調(diào)用成員變量的成員函數(shù)。在藍(lán)圖中,我們創(chuàng)建一個(gè)Component,并選擇其類型為ExplosiveBarrel,這樣就相當(dāng)于在操縱桿類中有了一個(gè)爆炸桶的成員變量。(教程中這里創(chuàng)建了Actor類型的變量,所以后面要多一步CastToExplosiveBarrel)

但是我們只知道這個(gè)成員變量是爆炸桶類型的,不知道它與哪個(gè)爆炸桶綁定,相當(dāng)有成員變量,但是這個(gè)成員變量還沒實(shí)例化。我們可以用一個(gè)已有的爆炸桶實(shí)例初始化該成員變量。在藍(lán)圖中,我們先勾選InstanceEditable,然后就能在內(nèi)容瀏覽器里操縱桿實(shí)例的Details里選擇要將哪個(gè)爆炸桶實(shí)例作為成員變量了。(用類似的方法可以實(shí)現(xiàn)一排操縱桿控制一排其它物體,比如爆炸桶,類似于游戲中的機(jī)關(guān)解密)


這樣我們就實(shí)現(xiàn)了與操縱桿交互,控制爆炸桶爆炸。
?
藍(lán)圖中事件與函數(shù)的主要區(qū)別是:
(1)???? 函數(shù)可以有返回值,事件沒有返回值(兩者都可以有輸入)

(2)???? 事件可以綁定到事件句柄上,例如計(jì)時(shí)器

?
?
之間我們通過改變寶箱蓋子的Rotation模擬寶箱被打開,這樣缺乏一個(gè)打開的過程,所以我們?cè)谒{(lán)圖中模擬開箱子的動(dòng)畫效果。注意,這里只是用藍(lán)圖模擬動(dòng)畫效果,并不想Character的攻擊動(dòng)作那樣導(dǎo)入了MontageAnimation。
和與操縱桿的交互一樣,這里我們依然是實(shí)現(xiàn)ItemChest中Interface類部分的接口函數(shù)。需要注意的是,寶箱的藍(lán)圖類是從C++類繼承而來的,所以子類中又定義了一遍成員函數(shù),相當(dāng)于覆蓋了父類的成員函數(shù)。因此,C++的ItemChest類中實(shí)現(xiàn)了接口函數(shù)就相當(dāng)于無效了。
同樣的,要在藍(lán)圖中使用LidMesh,要在C++的ItemChest類LidMesh成員變量前的UFUNCTION中加入BlueprintReadOnly的Specifier。

在Timeline的TimeTrack中,相比以前的開門等任務(wù),我們更細(xì)致地模擬了寶箱開啟的動(dòng)畫。

我們?cè)趯毾涞乃{(lán)圖中新增了StaticMesh組件并增加了粒子效果,來模擬真實(shí)開寶箱的效果

粒子效果要取消AutoActivate(否則粒子效果在BeginPlay后就產(chǎn)生,而不是在交互后產(chǎn)生),并且在Timeline結(jié)束后觸發(fā)。

?
?
另外,在debug時(shí),我們可以選擇我們的關(guān)注對(duì)象

還可以觀察藍(lán)圖中某些值的變化。

?
?
之前我們的Projectile擊中(發(fā)生碰撞)后,會(huì)停留在碰撞點(diǎn)。現(xiàn)在我們要實(shí)現(xiàn)Projectile擊中后(1)觸發(fā)粒子效果(2)消失。在藍(lán)圖中的實(shí)現(xiàn)如下,后面一部分一部分地解釋。

先是最基礎(chǔ)的實(shí)現(xiàn),觸發(fā)ComponentHit事件后,Spawn一個(gè)Emmiter(發(fā)射器),Emitter會(huì)播放attach父組件并跟隨父組件的指定效果(這里就是我們的粒子效果)。使用Projectile的Location和Rotation,并在指定粒子效果播放結(jié)束之后自我銷毀。

?
我們調(diào)整了Projectile碰撞的profile,然后發(fā)現(xiàn)由于Projectile是在Character手的位置Spawn的,所以一Spawn就產(chǎn)生了碰撞。因此我們要讓從Character生成的Projectile忽視Character(包括Character的Component)。
簡(jiǎn)單地來想,在Projectile的Collision profile中將Character的類型設(shè)為Ignore就可以了。但是我們只想讓從Character生成的Projectile忽視Character,而不想讓其他Projectile也忽視Character。(自己的魔法飛彈不會(huì)對(duì)自己造成傷害,但別人的魔法飛彈可以)
因此我們要有一個(gè)變量表示Projectile是由誰Spawn的。Projectile是在C++的Character類的ASCharacter::PrimaryAttack_TimeElapsed
成員函數(shù)中Spawn的,使用GetWorld()->SpawnActor<AActor>(ProjectileClass, SpawnTM, SpawnParams);語句。那么通過在Spawn的額外參數(shù)SpawnParams
中添加SpawnParams.Instigator = this;(這里的this就是生成Projectile的Character),就可以得到Projectile是由誰生成的。
?
接著就可以在上面的藍(lán)圖中補(bǔ)充兩部分。
(1)Projectile的碰撞組件始終忽視它自己的Instigator

(2)當(dāng)Projectile Hit到的不是自己的Instigator(我們的Character)時(shí),才進(jìn)行后面的觸發(fā)粒子效果和自我銷毀。(實(shí)際上這里的!=判斷是冗余的,因?yàn)橛辛耍?)之后,Project忽視Character,也就不會(huì)發(fā)生Hit)

?
?
經(jīng)過上面對(duì)Projectile的修改后,我們可以實(shí)現(xiàn)一個(gè)發(fā)射魔法飛彈的炮臺(tái),藍(lán)圖如下

SetTimerByEvent設(shè)置了一個(gè)定時(shí)器,每隔一段時(shí)間執(zhí)行給定的delegate(委托,在這里就是下面的自定義事件OnTimeElapsed)(通過Looping打鉤實(shí)現(xiàn)循環(huán),否則就像Character攻擊動(dòng)畫后生成Projectile那樣,只執(zhí)行一次)。
自定義事件中執(zhí)行的SpawnActor和C++中類似,指定Actor的類別以及Spawn的Transform。
可以看出由這個(gè)炮臺(tái)Spawn的Projectile是會(huì)HitCharacter的,因?yàn)榇藭r(shí)Projectile的Instigator不是Character,而是這個(gè)炮臺(tái)。
