Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

This article is a Private article. Only a writer and users who know the URL can access it.
Please change open range to public in publish setting if you want to share this article with other users.

GameplayTagとSubsystemを使用して効率的にイベントフラグを管理する

Last updated at Posted at 2025-12-16

この記事はK3 Advent Calendar 2025の17日目です。

ゲーム制作ではイベントが発生済みかどうかを管理したい場面が多々あります。
・このアイテムは既に入手済み? 
・このNPCと会話済み? 
・このステージはクリア済み? などなど。
規模の小さいゲームであればイベント管理クラスを作って、単に各イベント毎にフラグ用の変数を作って管理すれば良いだけなんですが、少し規模が大きくなるとフラグの数がとんでもないことになります。

【地獄のbool連打】
image.png

SubsystemとGameplayTagを活用することで、通常のフラグを使うよりはるかにわかりやすくイベントの発生状態を管理できます!

イベント管理用Subsystemの作成

まずは発生したイベントを管理するためのSaveGameとSubsystemを作成します。
SaveGameの方は、名前をEventSaveGameとしておきます。
Subsystemの方ですが、レベル単位でイベントの発生状態を管理したいならWorldSubsystem、ゲーム全体でイベントの発生状態を管理したいならGameInstanceSubsystemを継承してください。
ここでは、名前はEventManagerSubsystemとしておきます。

では、以下のようなコードを書いていきます。

EventSaveGame.h
#pragma once

#include "CoreMinimal.h"
#include "GameplayTags.h"
#include "GameFramework/SaveGame.h"
#include "EventSaveGame.generated.h"

UCLASS()
class MYPROJECT_API UEventSaveGame : public USaveGame
{
	GENERATED_BODY()

public:
    //発生済みのイベントタグを格納するコンテナ
	UPROPERTY(BlueprintReadOnly)
	FGameplayTagContainer ExcutedEventTags;
	
};

※EventSaveGame.cppはデフォルトから変更が無いため割愛

EventManagerSubsystem.h
#pragma once

#include "CoreMinimal.h"
#include "GameplayTags.h"
#include "EventSaveGame.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "EventManagerSubsystem.generated.h"

UCLASS()
class MYPROJECT_API UEventManagerSubsystem : public UGameInstanceSubsystem
{
	GENERATED_BODY()

	virtual void Initialize(FSubsystemCollectionBase& Collection);

public:
    //発生済みイベントタグの情報を保存するSaveGame
	UPROPERTY(BlueprintReadOnly)
	UEventSaveGame* EventSaveGame;

    //イベントが発生済みか確認する関数
	UFUNCTION(BlueprintCallable)
	bool CheckEventExcuted(FGameplayTag EventTag);

    //発生済みイベントを登録する関数
	UFUNCTION(BlueprintCallable)
	void RegisterExcutedEventTag(FGameplayTag NewEventTag);

    //発生済みイベントを取り除く関数
	UFUNCTION(BlueprintCallable)
	void RemoveExcutedEventTag(FGameplayTag EventTag);
	
};
EventManagerSubsystem.cpp
#include "EventManagerSubsystem.h"
#include "Kismet/GameplayStatics.h"

void UEventManagerSubsystem::Initialize(FSubsystemCollectionBase& Collection) 
{
	Super::Initialize(Collection);

	if (UGameplayStatics::DoesSaveGameExist("EventData", 0)) 
	{
		EventSaveGame = Cast<UEventSaveGame>(UGameplayStatics::LoadGameFromSlot("EventData", 0));
	}
	else 
	{
		EventSaveGame = Cast<UEventSaveGame>(UGameplayStatics::CreateSaveGameObject(UEventSaveGame::StaticClass()));
	}
}

bool UEventManagerSubsystem::CheckEventExcuted(FGameplayTag EventTag)
{
	return EventSaveGame->ExcutedEventTags.HasTag(EventTag);
}

void UEventManagerSubsystem::RegisterExcutedEventTag(FGameplayTag NewEventTag) 
{
	EventSaveGame->ExcutedEventTags.AddTag(NewEventTag);
	UGameplayStatics::SaveGameToSlot(EventSaveGame, "EventData", 0);
}


void UEventManagerSubsystem::RemoveExcutedEventTag(FGameplayTag EventTag)
{
	EventSaveGame->ExcutedEventTags.RemoveTag(EventTag);
	UGameplayStatics::SaveGameToSlot(EventSaveGame, "EventData", 0);
}

コードについては見たままです。
発生済みのイベントと紐づいたGameplayTagを、EventSavegameのGameplayTagContainerに格納することで、イベントが発生済みかどうかを簡単に確認可能なようにしています。

使い方

一例として、1度拾ったら2度と出現しないアイテムの実装を行います。

まず、Actorを継承したBP_Itemを作り、変数としてGameplayTag型のPickupEventTagを用意します。InstancedEditableにしておきましょう。
image.png

次に、適当な当たり判定を用意してそこにプレイヤーが当たったらPickupを行うようにBPを組みます。
image.png

Pickupイベント内で、アイテムを拾った際の処理を記述した後、EventManagerSubsystemにPickupEventTagを追加するようにします。最後にDestoryActorでアイテムを消滅させます。
image.png

そして、拾った次回以降のプレイでアイテムが出現しないように、Beginplayで、イベントが発生済みかどうかを確認して、発生済みなら即座にDestoryActorでアイテムを消滅させるようにします。
image.png

では、実際にアイテムを配置して拾ってみましょう。
レベルにBP_Itemを3つ置き、PickupEventTagをそれぞれEvent.ItemPickup.Test.1、Event.ItemPickup.Test.2、Event.ItemPickup.Test.3として実行します。
まずEvent.Item.Test.1を設定したものだけ拾って、実行を終了します。もう一度実行するとそれだけが消えているのがわかると思います。セーブデータを消去しない限り何度実行しなおしても2度と出現しません。

この挙動自体は普通にフラグを大量作成することでも実現可能ですが、このやり方をすることでアイテムやイベントが増えてもイベント管理クラスのコードを変更してフラグを増やす必要が無いです。さらに、GameplayTagは階層的に管理することができるため、フラグの乱立に比べてどの場面でどのようなイベントがあるのかが非常にわかりやすいです。
image.png

みなさんもGameplayTagとSubsystemを活用して、快適なイベント発生管理ライフを送りましょう!

0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?