最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

TomLooman_ActionRoguelike_第七章UMG和Player屬性

2023-08-10 08:17 作者:別叫我小紅  | 我要投稿

該專欄用于保存對TomLooman的ActionRoguelike項目的學(xué)習(xí)筆記,學(xué)習(xí)過程中的思考與記錄不一定準(zhǔn)確。


教程參考:https://github.com/tomlooman/ActionRoguelike

基于UE5.0的項目實現(xiàn):https://github.com/CarolBaggins2023/TomLooman_ActionRoguelike_Tutorial

2023_08_05

UMG和Player屬性:屬性組件,帶有數(shù)據(jù)綁定的UMG,基于事件的UMG,用屬性進(jìn)行傷害

?

我們希望用一個東西來表示角色和物體的血量之類的屬性,最直接的方法是將這些屬性作為角色類的成員變量,但是由此而來的一個問題是角色類將會變得非常大,難以維護(hù),而且這些屬性只能用在某一個類中,復(fù)用性差,所以我們將這些屬性抽象成一個屬性類。

這個屬性組件類繼承自Actor組件,和交互組件類一樣。BlueprintSpawnableComponent是類元數(shù)據(jù)說明符,表示組件類可由藍(lán)圖生成。

?

在類中,我們聲明一個成員變量表示生命值,并想要通過一個成員函數(shù)修改它。

定義了屬性組件類之后,我們要將其作為角色類的一個成員變量,這樣一來,角色類通過屬性組件類的成員變量,相當(dāng)于擁有了屬性組件類中定義的那些屬性。

?

目前為止,還沒有東西能觸發(fā)屬性組件類中修改血量函數(shù)的執(zhí)行。我們希望在角色被子彈擊中時改變角色血量,所以我們在子彈類中定義overlap事件。

注意,OnComponentBeginOverlap相當(dāng)于藍(lán)圖中的Event(事件),與組件相關(guān),當(dāng)有東西和這個組件overlap時觸發(fā)(be called)。OnActorOverlap是類的成員函數(shù),與整個類的實例相關(guān)。AddDynamic將UObject類的實例(SphereComp)和類成員函數(shù)(OnActorOverlap)通過動態(tài)多播代理(OnComponentBeginOverlap)綁定到一起。所以這一行代碼表示,一旦觸發(fā)了SphereComp->OnComponentBeginOverlap這一事件,就執(zhí)行函數(shù)ASMagicProjectile::OnActorOverlap。

?

觸發(fā)事件后執(zhí)行的成員函數(shù)定義如下,這里的函數(shù)形式繼承自Actor基類。UFUNTCION()讓類的成員函數(shù)變?yōu)閁Function類型,UFunction是一種C++函數(shù),可以被UE的反射系統(tǒng)識別。

在函數(shù)的實現(xiàn)中,我們首先判斷了觸發(fā)碰撞組件overlap事件的Actor非空,且不是發(fā)出子彈的角色(這一點解決了之前向右移動時發(fā)出子彈,子彈會在角色身上explode的問題)。

然后我們獲取觸發(fā)事件的Actor上的屬性組件成員,通過GetComponentByClass函數(shù)實現(xiàn),其函數(shù)原型為UActorComponent* GetComponentByClass(TSubclassOf<UActorComponent> ComponentClass) const,因此需要一個類作為函數(shù)參數(shù),可通過類名::StaticClass()獲得一個類的引用。同時,因為GetComponentByClass得到的UActorComponent*類型的對象,而不是我們想要的屬性組件類對象這里需要進(jìn)行以此類型轉(zhuǎn)換,用Cast實現(xiàn)。

再然后我們判斷觸發(fā)事件額Actor是否有屬性組件成員,如果有的話,執(zhí)行該屬性組件成員中修改血量的成員函數(shù)。

為什么這里要定義碰撞組件的overlap事件,而不是hit事件?因為比方在游戲射擊游戲中,我們不想讓子彈對友方也造成傷害,但是友方和敵方的碰撞屬性又一般是同樣的,也就無法通過碰撞屬性區(qū)分,此時就可以對友方和敵方都視為overlap,但在overlap事件中判斷OtherActor是友方還是敵方。

另外,我們要修改子彈與世界中其他物體的碰撞屬性,將我們希望子彈能對它造成傷害的類型的碰撞屬性設(shè)為overlap。否則,如果還是block,會出現(xiàn)教程中所說的,角色發(fā)射子彈時,角色會產(chǎn)生一個位移,因為此時角色和子彈是block的,會發(fā)生hit事件,所以角色會被子彈推動。

?

有了角色的屬性之后,我們自然地想將屬性,比如血量,顯示在屏幕上,這里就要用到UMG,之前做準(zhǔn)心時使用過。

這里我們放置了一個Text表示屬性值,一個ProgressBar對屬性值進(jìn)行可視化,還用了一個HorizontalBox,這樣我們就不用自己布局進(jìn)行對其了。

我們可以將Text綁定到某個函數(shù)上,將函數(shù)返回值作為Text顯示的值。需要注意的是,這個綁定的函數(shù)是在每個Tick都會執(zhí)行的,所以可能開銷較大。

該函數(shù)的藍(lán)圖表示如下,

GetOwningPlayer的函數(shù)原型是virtual?APlayerController?* GetOwningPlayer() const,獲得的是PlayerController。GetOwningPlayerPawn的函數(shù)原型是APawn?* GetOwningPlayerPawn() const,獲得的是玩家控制的Character。

這里想要獲得角色的屬性組件成員的血量成員,還有一種方法,利用Cast將APawn*轉(zhuǎn)換為玩家控制的Character的類型,然后直接訪問成員變量。因為GetComponentByClass的做法需要遍歷Actor的組件,所以可能效率低一些。

另外,一開始對Character是否為空的判定是有用的,因為游戲一開始時角色可能還沒生成。

?

我們之前提到了這與Text綁定的函數(shù)會在每個Tick都執(zhí)行,所以它除了低效之外,還無法告知我們屬性變化的時間信息。如果我們想要在屬性變化,比如受傷減血時,顯示一些動畫,比如屏幕閃爍紅光,就需要一個類似Event事件的東西來觸發(fā)。

就像子彈類中做的那樣,觸發(fā)OnActorOverlap事件后,執(zhí)行被overlap的對象中的某個函數(shù)。但是現(xiàn)有的事件中并沒有能夠表示“角色的屬性組件成員中的血量成員發(fā)生變化”的事件,所以我們要自定義一個“事件”(C++中的委托在概念好像可以這么解釋)。



?

這個委托(Delegate)不一定要定義在屬性組件類中,當(dāng)委托被多個類使用時,也可以定義在一個單獨的頭文件中,然后在要使用該委托的文件中include即可。

(1)


從名字可以看出這里聲明了一個有四個參數(shù)的動態(tài)多播委托。第一個值是委托的名字,之后是參數(shù)。注意,和函數(shù)的參數(shù)列表不同的是,委托中的參數(shù)類型和參數(shù)用逗號隔開。

?

類似于函數(shù)類,我們聲明之后還需要實例化。我們在屬性組件類中將其實例化為一個成員變量。

同時,從它使用UPROPERTY中也可以看出,委托并不是一個函數(shù),而是更像一個類。

BlueprintCallable讓我們在UI中也可以調(diào)用這個事件。

(2)

?

目前為止,還無人觸發(fā)這個事件。因為這個事件是通知其它對象“角色血量改變”這一事實的,所以我們應(yīng)該在改變血量的函數(shù)中觸發(fā)該事件,這里的“觸發(fā)”對于委托來說就是“廣播”。

可以看出,委托的廣播與函數(shù)調(diào)用的形式類似,或者說類似于調(diào)用類的成員函數(shù),需要傳入委托聲明時要求的參數(shù)。

這里的InstigatorActor暫時為空,后續(xù)會補(bǔ)充。觸發(fā)該事件的就是這個屬性組件本身,所以第二個參數(shù)為this。

(4)


此時我們可以發(fā)現(xiàn),就像碰撞組件有OnComponentHit那些事件一樣,屬性組件中出現(xiàn)了我們自定義的OnHealthChanged事件。

我們也可以生成事件的藍(lán)圖節(jié)點,參數(shù)就是我們廣播傳入的參數(shù)。當(dāng)觸發(fā)該事件后,就進(jìn)行事件節(jié)點后的藍(lán)圖執(zhí)行流。

?

當(dāng)我們把“角色血量變化”構(gòu)建為一個事件,我們就可以在之前的UI中調(diào)用了。

首先看以下的藍(lán)圖部分,在這里我們先獲取玩家控制的Character,然后獲取Character中的屬性組件成員變量,因為屬性組件成員變量有我們想調(diào)用的FOnHealthChanged委托的示例OnHealthChanged。再然后是關(guān)鍵的一步,我們將這個委托綁定到另一個事件上,那么當(dāng)委托OnHealthChanged被觸發(fā)時,就會觸發(fā)所綁定的事件。

(3)

OnHealthChanged委托綁定的事件就是要修改我們血條和血量文字。以下是用ProcessBar實現(xiàn)血條時的藍(lán)圖。

?

上面涉及到了UI中的EventConstruct和EventPreConstruct,兩者的區(qū)別在于EventConstruct在我們運行游戲時才運行,而EventPreConstruct在設(shè)計時就運行。例如,如果我們在EventConstruct修改Text的文字,只有游戲運行時我們才能看到Text的變化,而如果我們在EventPreConstruct,我們在設(shè)計頁面就能看到Text的變化。

?

關(guān)于UE的委托(Delegate),最重要的就是上面內(nèi)容中的(1)(2)(3)(4),其中(3)和(4)的出現(xiàn)順序并沒有反。

根據(jù)這個例子,總結(jié)委托的使用步驟如下,

1. **在頭文件中聲明委托**:在您的類的頭文件中,使用 `DECLARE_DYNAMIC_MULTICAST_DELEGATE_FourParams` 宏來聲明您的委托類型。例如:

?

```cpp

DECLARE_DYNAMIC_MULTICAST_DELEGATE_FourParams(FOnHealthChanged, AActor*, InstigatorActor, USAttributeComponent*, OwningComp, float, NewHealth, float, Delta);

```

?

2. **聲明委托實例**:在您的類中,聲明一個具體的委托實例。這將成為您在需要時綁定和觸發(fā)事件的實例。例如:

?

```cpp

UCLASS()

class YourClass : public UObject

{

??? GENERATED_BODY()

?

public:

??? FOnHealthChanged OnHealthChangedDelegate;

};

```

?

3. **綁定函數(shù)到委托**:在適當(dāng)?shù)臅r候,您可以將函數(shù)綁定到委托實例上。這些函數(shù)將在委托被觸發(fā)時執(zhí)行。(在上面的例子中,我們沒有綁定函數(shù),而是綁定了一個事件,下面會有綁定函數(shù)的例子)例如:

?

```cpp

OnHealthChangedDelegate.AddDynamic(this, &YourClass::HealthChangedFunction);

```

?

4. **觸發(fā)委托**:在適當(dāng)?shù)臅r候,調(diào)用委托實例的觸發(fā)函數(shù),以執(zhí)行已綁定的函數(shù)。例如:

?

```cpp

OnHealthChangedDelegate.Broadcast(InstigatorActor, OwningComp, NewHealth, Delta);

```

?

在上述步驟中,`HealthChangedFunction` 是您要執(zhí)行的函數(shù),而 `InstigatorActor`、`OwningComp`、`NewHealth` 和 `Delta` 是函數(shù)的參數(shù)。

?

通過這些步驟,您可以使用聲明的委托類型來實現(xiàn)事件觸發(fā)、回調(diào)和監(jiān)聽機(jī)制,以響應(yīng)特定的事件情況。請注意,這只是一個基本的使用示例,具體的實現(xiàn)可能會根據(jù)您的需求而有所不同。

?

我們現(xiàn)在想給血量Text的UI做個動畫。

在設(shè)計界面可以方便地給某個Widget添加動畫的track,在track中可以選擇某個時間點,然后插入該時間點的Widget。例如在0時刻有Text的默認(rèn)形態(tài),我們在1時刻插入了Text的放大形態(tài)(修改Transform的Scale),然后動畫系統(tǒng)會進(jìn)行類似插值的操作,最終我們就會看到從0時刻到1時刻Text逐漸變大的動畫效果。

然后我們可以非常方便地在藍(lán)圖中用PlayAnimation調(diào)用這個動畫,


TomLooman_ActionRoguelike_第七章UMG和Player屬性的評論 (共 條)

分享到微博請遵守國家法律
灵武市| 肃宁县| 龙口市| SHOW| 高淳县| 庆安县| 丰镇市| 乌兰察布市| 浦县| 涿州市| 丰顺县| 阜阳市| 湟中县| 通州市| 绥宁县| 大冶市| 上杭县| 桃园市| 望都县| 保德县| 浦县| 琼结县| 潞西市| 斗六市| 湛江市| 花垣县| 延津县| 贡山| 恩平市| 民县| 嘉定区| 涡阳县| 子长县| 旺苍县| 油尖旺区| 云龙县| 托克托县| 安乡县| 衡东县| 清远市| 呼图壁县|