見出し画像

[UE5 ゲーム制作 備忘録]一定時間立つと減り続ける酸素ゲージUIの実装



冒頭話


ゲームには体力バーやスタミナバーなどを基準に
サバイバルゲームでは空腹値などいろんなステータスがありますよね。

自分自作ゲームでは、
空中の世界で生きるプレイヤーを作っています。

はじめに


富士山なんかを例にします。

標高の高すぎる山へ登ると当然、酸素は薄くなります。
宇宙に行くと酸素はゼロなので、生身で生活するのは99%不可能。
(絶対に不可能とはいいません)。

それで思いついたことが
【一定時間経つと酸素ゲージが徐々に減っていくシステム】。
これを導入します。

今後酸素を補給するアイテム、装備、装置などが
欲しいところですね。

酸素UIを作成する


ContentBrowserからWidgetBlueprintを作成して
CanvasPanelの下にProgressBarとimageを配置します。
位置やサイズの調整をして一旦左下に置きますか。

ここでC++ Classを作成します。

UserWidget Clas.s

.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を作成します。

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(ヘッドアップディスプレイ)が開始された時に
初期化タスクを実行するために使用されます。

  1. 親クラスのBeginPlayメソッドを呼び出します。

  2. 現在のゲームワールドを表すUWorldオブジェクトへのポインタを取得します。

  3. ワールドオブジェクトが有効かどうかを確認します。

  4. ワールドから最初のプレイヤーコントローラーを取得します。

  5. PlayerControllerとAlbaOverlayClassが有効であるかを確認します。

  6. AlbaOverlayClassを使用して、PlayerControllerに関連付けられたWidgetのインスタンスを作成し、AlbaOverlayに保存します。

  7. 作成したウィジェットをゲームのビューポートに追加し、画面上に表示します。

このBeginPlayメソッドは、ゲームの開始時またはAlbaHUDオブジェクトがゲームワールドでライフサイクルを開始する時に、HUDウィジェットを初期化して表示します。 …etc



ふむふむ。要はゲーム画面に作ったUIを
表示するってことだな!(IQ3)。

ってことで作ったクラスをブループリントに設定していきます。
まずはWigetBluePrintにOverlayクラスを設定。

BluePrintを開いて右上のGraphに移動すると
Class SettingがあるのでそこからDetailsを見ると、
ClassOption→ParentClassから作ったクラスを設定できます。

BluePrintOpen→Graph→ClassSetting→Parent→ Overlay.cpp

次にゲーム画面に表示するHUDのブループリントを
作成します。

Search→クラス名の検索


GameMode Blueprintを開いてClassesにHUD Classがありますが、
この中に作ったHUD BluePrintをセットします

BP_GameMode → HUD Class

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 + 検索 + 自己判断 で解決できなくなった時がきたら、
人間の詳しい方に質問するというサイクルが今のやり方なん
じゃないかなーと個人的に思っています。

特にこういうシステム系統などですね。

以上です








いいなと思ったら応援しよう!