[UE5 ゲーム制作 備忘録]一定時間立つと減り続ける酸素ゲージUIの実装
冒頭話
ゲームには体力バーやスタミナバーなどを基準に
サバイバルゲームでは空腹値などいろんなステータスがありますよね。
自分自作ゲームでは、
空中の世界で生きるプレイヤーを作っています。
はじめに
富士山なんかを例にします。
標高の高すぎる山へ登ると当然、酸素は薄くなります。
宇宙に行くと酸素はゼロなので、生身で生活するのは99%不可能。
(絶対に不可能とはいいません)。
それで思いついたことが
【一定時間経つと酸素ゲージが徐々に減っていくシステム】。
これを導入します。
今後酸素を補給するアイテム、装備、装置などが
欲しいところですね。
酸素UIを作成する
ContentBrowserからWidgetBlueprintを作成して
CanvasPanelの下にProgressBarとimageを配置します。
位置やサイズの調整をして一旦左下に置きますか。
ここでC++ Classを作成します。
.hファイルと.cppファイルが作成できたら以下のコードを入力しました。
Overlay.h ファイル
#pragma once
#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "Overlay.generated.h"
UCLASS()
class ALBAGAIM_WORLD_API Overlay : public UUserWidget
{
GENERATED_BODY()
public:
void SetOxygenLevelProgressBar(float Percent);
private:
UPROPERTY(meta = (BindWidget))
class UProgressBar* OxygenLevelProgressBar;
};
Overlay.cpp
#include "HUD/Overlay.h"
#include "Components/ProgressBar.h"
void USlashOverlay::SetOxygenLevelProgressBar(float Percent){
if (OxygenLevelProgressBar) {
OxygenLevelProgressBar ->SetPercent(Percent);
}
}
ひとつひとつ説明すると長くなるので省きますが、
要はUIのOxygenLevelProgressBarの[Percent]という値(0~1)を
パソコンに記憶させて置きました。
これでUIの設定は終わり!。
ゲーム画面にUIを表示する
今度は画面上にこのUIを出したいため、
新たにHUD Classを作成します。
このようなコードを書きます
.h ファイル
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/HUD.h"
#include "AlbaHUD.generated.h"
class UOverlay;
UCLASS()
class ALBAGAIM_WORLD_API AAlbaHUD : public AHUD{
GENERATED_BODY()
protected:
virtual void BeginPlay() override;
private:
UPROPERTY(EditDefaultsOnly, Category = "Slash")
TSubclassOf<UOverlay> AlbaOverlayClass;
UPROPERTY()
UOverlay * AlbaOverlay;
public:
FORCEINLINE UOverlay * GetAlbaOverlay() const { return AlbaOverlayClass; }
};
.cpp ファイル
#include "HUD/AlbaHUD.h"
#include "HUD/Overlay.h"
void AlbaHUD::BeginPlay(){
Super::BeginPlay();
UWorld* World = GetWorld();
if (World) {
APlayerController * Controller = World -> GetFirstPlayerController();
if (Controller && AlbaOverlayClass) {
AlbaOverlay = CreateWidget<UOverlay>(Controller, AlbaOverlayClass);
AlbaOverlay ->AddToViewport();
}
}
}
言葉で説明するの大変なんで、
GPT4プラグインのCode Coplitさんを使いますね。
教えて!ジーピーティーえもーん!
GPTの説明
このHUD(ヘッドアップディスプレイ)が開始された時に
初期化タスクを実行するために使用されます。
親クラスのBeginPlayメソッドを呼び出します。
現在のゲームワールドを表すUWorldオブジェクトへのポインタを取得します。
ワールドオブジェクトが有効かどうかを確認します。
ワールドから最初のプレイヤーコントローラーを取得します。
PlayerControllerとAlbaOverlayClassが有効であるかを確認します。
AlbaOverlayClassを使用して、PlayerControllerに関連付けられたWidgetのインスタンスを作成し、AlbaOverlayに保存します。
作成したウィジェットをゲームのビューポートに追加し、画面上に表示します。
このBeginPlayメソッドは、ゲームの開始時またはAlbaHUDオブジェクトがゲームワールドでライフサイクルを開始する時に、HUDウィジェットを初期化して表示します。 …etc
ふむふむ。要はゲーム画面に作ったUIを
表示するってことだな!(IQ3)。
ってことで作ったクラスをブループリントに設定していきます。
まずはWigetBluePrintにOverlayクラスを設定。
BluePrintを開いて右上のGraphに移動すると
Class SettingがあるのでそこからDetailsを見ると、
ClassOption→ParentClassから作ったクラスを設定できます。
次にゲーム画面に表示するHUDのブループリントを
作成します。
GameMode Blueprintを開いてClassesにHUD Classがありますが、
この中に作ったHUD BluePrintをセットします
GameModeの設定はWorld Settingsの GameMode Overrideで上書きができます。HUD ClassにカスタマイズされたBP_HUDがありましたね。
実行すると画面に出てきましたが、まだこれでは
画面だけ表示してゲージは動きません。
酸素ゲージを動かす
酸素ゲージはプレイヤーと連動しなくてはいけないので
最初から作っているCharacterクラスに設定していきます。
ここからは必要メソッドのみを記述します。
.h
#pragma once
#include "CoreMinimal.h"
#include "Components/TimelineComponent.h"
#include "EchoCharacters.generated.h"
class UOverlay;
UCLASS()
class ALBAGAIM_WORLD_API AEchoCharacters : public ABaseCharacter{
GENERATED_BODY()
public:
AEchoCharacters();
virtual void Tick(float DeltaTime) override;
protected:
virtual void BeginPlay() override;
UPROPERTY(BlueprintReadWrite, Category = "CharacterSate");
bool bIsDead = false;
private:
UPROPERTY();
UOverlay* AlbaOverLay;
UPROPERTY(EditAnywhere, Category = "Oxygen")
float OxygenLevel = 300.f;
UPROPERTY(EditAnywhere, Category = "Oxygen")
float MaxOxygenLevel = 300;
// 酸素ゲージが減少する間隔(秒)
float OxygenDecreaseInterval = .1f;
// 酸素ゲージが減少する量
float OxygenDecreaseAmount = .1f;
FTimerHandle OxygenTimerHandle;
void UpdateOxygen();
void SetOxygenHUD(const float MaxOxygenLevel);
void InitializeSlashOverlay();
void SetHUDHealth();
};
.cpp
#include "Characters/EchoCharacters.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "Components/StaticMeshComponent.h"
#include "HUD/AlbaHUD.h"
#include "HUD/AlbaOverlay.h"
#include "Components/AttributeComponent.h"
#include "DrawDebugHelpers.h"
void AEchoCharacters::BeginPlay(){
Super::BeginPlay();
InitializeSlashOverlay();
// タイマーを設定して酸素ゲージを定期的に更新
GetWorld()->GetTimerManager().SetTimer(OxygenTimerHandle, this, &AEchoCharacters::UpdateOxygen, OxygenDecreaseInterval, true);
}
//パーセンテージ値を正規化 300/300 = 1
void AEchoCharacters::SetOxygenHUD(const float Oxygen)
{
if (AlbaOverLay ) {
AlbaOverLay ->SetOxygenLevelProgressBar(OxygenLevel / Oxygen);
}
}
//体力バー
void AEchoCharacters::SetHUDHealth(){
if (AlbaOverLay && Attributes) {
AlbaOverLay ->SetHealthBarPercent(Attributes->GetHealthPercent());
}
}
//HUDの初期化
void AEchoCharacters::InitializeSlashOverlay()
{
APlayerController * PlayerController = Cast<APlayerController>(GetController());
if (PlayerController) {
ASlashHUD * AlbaHud = Cast<AAlbaHUD>(PlayerController->GetHUD());
if (SlashHud) {
AlbaOverLay = AlbaHud ->GetAlbaOverlay();
if (AlbaOverLay) {
AlbaOverLay ->SetOxygenLevelProgressBar(OxygenLevel);
if (Attributes) {
AlbaOverLay ->SetHealthBarPercent(Attributes->GetHealthPercent());
}
}
}
}
}
//酸素ゲージUIを更新
void AEchoCharacters::UpdateOxygen(){
// 酸素レベルを減少させる
OxygenLevel = FMath::Max(OxygenLevel - OxygenDecreaseAmount, 0.0f);
SetOxygenHUD(MaxOxygenLevel);
if (OxygenLevel <= 0.0f && !bIsDead){
bIsDead = true;
OxygenLevel = 0.0f;
// 死亡処理など
HandleDamage(100.f);
SetHUDHealth();
Die();
}
}
InitializeSlashOverlay()のSetOxygenLevelProgressBarをセットして
AlbaOverLay ->SetOxygenLevelProgressBar(OxygenLevel);
BiginPlayには二つを呼び出しています。
InitializeSlashOverlay();
// タイマーを設定して酸素ゲージを定期的に更新
GetWorld()->GetTimerManager().SetTimer(OxygenTimerHandle, this, &AEchoCharacters::UpdateOxygen, OxygenDecreaseInterval, true);
これで最後にUpdateOxygen()の中身を書くことによって
一定時間(この場合5分)経過するとプレイヤーが倒れる
仕組みができました。
//酸素ゲージUIを更新
void AEchoCharacters::UpdateOxygen(){
// 酸素レベルを減少させる
OxygenLevel = FMath::Max(OxygenLevel - OxygenDecreaseAmount, 0.0f);
SetOxygenHUD(MaxOxygenLevel);
if (OxygenLevel <= 0.0f && !bIsDead){
bIsDead = true;
OxygenLevel = 0.0f;
// 死亡処理など
HandleDamage(100.f);
SetHUDHealth();
Die();
}
}
ほかにも死亡処理や体力バー処理など、設定されていますが、
SetOxygenHUD(const float Oxygen)とUpdateOxygen()のメソッド二つが
プレイヤーに追加した酸素ゲージ連動部分です。
実行するとこのようになりました。
これで酸素ゲージの追加が成功しました。
余談
この発想自体は私思いついた内容ですが、
C++のコードはほとんどChatGPT4のプラグイン
CodeCopilotさんが作ってくれたものを動かしました。
多少何回もバグったりしましたが、その度にフィードバックを貰って
結果的にうまくいったので、近頃AI Chatには世話になっています。
AI + 検索 + 自己判断 で解決できなくなった時がきたら、
人間の詳しい方に質問するというサイクルが今のやり方なん
じゃないかなーと個人的に思っています。
特にこういうシステム系統などですね。
以上です