【Unreal Engine】Game Frameworkの理解:GameMode / GameState / PlayerStateの役割と使い分け

作成: 2026-02-07

UEのGame Frameworkの中核であるGameMode・GameState・PlayerStateの3クラスの役割を比較解説。マルチプレイヤーでの情報フローとC++実装スケルトンも紹介します。

概要

動作確認環境: UE 5.4+

「GameMode、GameState、PlayerState…名前は似ているけど何が違うのかわからない」――これはUE開発を始めた多くの方が最初にぶつかる壁の1つです。特にマルチプレイヤーゲームでは、どのクラスがどこに存在し、何が複製されて何がされないのかを正しく理解していないと、サーバー・クライアント間のデータ同期で苦労します。

この3クラスの役割分担を理解すれば、シングルプレイヤーでもマルチプレイヤーでも、堅牢で拡張性の高いゲームロジックを構築できます。

3クラスの比較

まず全体像を把握しましょう。

クラス存在場所役割複製
GameModeサーバーのみゲームのルール定義・実行されない
GameStateサーバー+全クライアントゲーム全体の状態管理される
PlayerStateサーバー+全クライアント各プレイヤーの状態管理される

GameModeがサーバーのみに存在するのが最も重要なポイントです。これはゲームのルール(勝利条件、スポーン位置、参加条件など)をクライアント側で改変させないための設計です。クライアントが知る必要のあるゲーム状態は、GameStateやPlayerStateを経由して同期されます。

GameMode:ルールブック

GameModeはゲームのルールそのものを定義するクラスです。「審判」や「ルールブック」と考えるとイメージしやすいでしょう。

主な管理内容:

  • プレイヤーの参加・退出時の処理(ログイン/ログアウト)
  • Pawnのスポーン位置・タイミング・条件
  • マッチの開始・終了条件
  • ゲームモード固有のルール(デスマッチ、CTFなど)

AGameModeBase vs AGameMode

UEにはGameModeの基底クラスが2つあります。新規プロジェクトのデフォルトは AGameModeBase です。

クラス特徴推奨用途
AGameModeBaseシンプルで軽量シングルプレイヤー、非マッチベースのゲーム
AGameModeマッチステートマシン搭載チームデスマッチ等のマッチベースマルチプレイヤー

AGameModeAGameModeBase を継承し、以下の6つのマッチステートを管理するステートマシンが組み込まれています: EnteringMapWaitingToStartInProgressWaitingPostMatchLeavingMap(異常時は Aborted)。マッチの開始待機やポストゲーム画面の制御に使います。

GameModeはプロジェクト設定、レベルのワールド設定、またはURL引数として設定でき、レベルごとに異なるGameModeを使い分けることも可能です。

GameState:スコアボード

GameStateはゲーム全体の現在の状態を管理し、全クライアントに同期するクラスです。GameModeが「ルールブック」だとすれば、GameStateは「スコアボード」や「タイマー」のようなものです。

主な管理内容:

  • ゲーム経過時間(GetServerWorldTimeSeconds() でサーバー同期された正確な時間を取得)
  • 接続プレイヤーリスト(PlayerArray プロパティ)
  • チームスコアなど、特定のプレイヤーに紐付かないゲーム全体の情報
  • ゲーム開始状態(HasBegunPlay()

GameStateはあくまでゲーム全体に関わる状態を管理するためのものです。個々のプレイヤー固有のデータ(個人スコアや名前など)は、次に説明するPlayerStateで管理するのが適切です。

PlayerState:個人の成績表

PlayerStateはゲームに参加している個々のプレイヤーの状態を管理するクラスです。全クライアントに複製されるため、たとえばスコアランキングUIで他プレイヤーの情報を表示するといった用途に直接利用できます。

組み込みのプロパティとメソッド:

  • プレイヤー名: GetPlayerName()
  • スコア: GetScore() / SetScore()
  • Ping: GetPingInMilliseconds()
  • プレイヤーID: GetPlayerId()
  • ボット判定: IsABot()

GameStateの PlayerArray にはPlayerStateのインスタンスが格納されています。サーバーや各クライアントはこの配列を通じて、接続中のすべてのプレイヤーの状態を参照できます。

情報フローの実例

3クラスがどのように連携するのかを、具体的なシナリオで見てみましょう。

プレイヤーが敵を倒してスコアを獲得する場合:

  1. プレイヤーのPawnが敵を倒す
  2. PlayerController(またはPawn)がサーバーに「敵を倒した」イベントを通知
  3. サーバー上の GameMode がルールに基づいて「スコアを加算する」処理を実行
  4. GameModeが該当プレイヤーの PlayerState を取得し、スコアを更新
  5. PlayerStateのスコア変数は Replicated 設定なので、サーバーでの変更が自動的に全クライアントに伝播
  6. 各クライアントのUIがPlayerStateの変更を検知し、スコア表示を更新

このように、GameModeがサーバーサイドでルールを執行し、その結果をGameState/PlayerStateを介して全クライアントに伝達するのが基本的な設計パターンです。

C++実装スケルトン

実際に3クラスをカスタムして使う場合の最小限の実装例を示します。

カスタムGameMode

// MyGameMode.h
#pragma once
#include "GameFramework/GameModeBase.h"
#include "MyGameMode.generated.h"

UCLASS()
class AMyGameMode : public AGameModeBase
{
    GENERATED_BODY()

public:
    AMyGameMode();

    void OnPlayerScored(APlayerController* Scorer, int32 Points);

    virtual void PostLogin(APlayerController* NewPlayer) override;
    virtual void Logout(AController* Exiting) override;
};
// MyGameMode.cpp
#include "MyGameMode.h"
#include "MyGameState.h"
#include "MyPlayerState.h"

AMyGameMode::AMyGameMode()
{
    // カスタムのGameStateとPlayerStateクラスを指定
    GameStateClass = AMyGameState::StaticClass();
    PlayerStateClass = AMyPlayerState::StaticClass();
}

void AMyGameMode::OnPlayerScored(APlayerController* Scorer, int32 Points)
{
    if (AMyPlayerState* PS = Scorer->GetPlayerState<AMyPlayerState>())
    {
        PS->AddScore(Points);
    }
}

コンストラクタで GameStateClassPlayerStateClass を指定すると、UEが自動的にカスタムクラスのインスタンスを生成します。

カスタムGameState(レプリケーション変数付き)

// MyGameState.h
#pragma once
#include "GameFramework/GameStateBase.h"
#include "MyGameState.generated.h"

UCLASS()
class AMyGameState : public AGameStateBase
{
    GENERATED_BODY()

public:
    // Replicatedを付けた変数は全クライアントに自動同期される
    UPROPERTY(Replicated, BlueprintReadOnly, Category = "Score")
    int32 TeamAScore = 0;

    UPROPERTY(Replicated, BlueprintReadOnly, Category = "Score")
    int32 TeamBScore = 0;

    virtual void GetLifetimeReplicatedProps(
        TArray<FLifetimeProperty>& OutLifetimeProps) const override;
};
// MyGameState.cpp
#include "MyGameState.h"
#include "Net/UnrealNetwork.h"

void AMyGameState::GetLifetimeReplicatedProps(
    TArray<FLifetimeProperty>& OutLifetimeProps) const
{
    Super::GetLifetimeReplicatedProps(OutLifetimeProps);
    // DOREPLIFETIMEマクロで複製する変数を登録
    DOREPLIFETIME(AMyGameState, TeamAScore);
    DOREPLIFETIME(AMyGameState, TeamBScore);
}

UPROPERTY(Replicated) でマークした変数は、GetLifetimeReplicatedPropsDOREPLIFETIME マクロを使って登録する必要があります。Net/UnrealNetwork.h のインクルードも忘れずに。

ヒント: DOREPLIFETIME_CONDITION を使うと、条件付きレプリケーション(オーナーのみ、初回のみなど)を設定できます。帯域幅の節約に有効です。

カスタムPlayerState

// MyPlayerState.h
#pragma once
#include "GameFramework/PlayerState.h"
#include "MyPlayerState.generated.h"

UCLASS()
class AMyPlayerState : public APlayerState
{
    GENERATED_BODY()

public:
    UFUNCTION(BlueprintCallable, Category = "Score")
    void AddScore(int32 Points);

    UPROPERTY(Replicated, BlueprintReadOnly, Category = "Score")
    int32 KillCount = 0;

    virtual void GetLifetimeReplicatedProps(
        TArray<FLifetimeProperty>& OutLifetimeProps) const override;
};
// MyPlayerState.cpp
#include "MyPlayerState.h"
#include "Net/UnrealNetwork.h"

void AMyPlayerState::AddScore(int32 Points)
{
    // APlayerStateの組み込みメソッドでスコアを更新
    SetScore(GetScore() + Points);
    KillCount++;
}

void AMyPlayerState::GetLifetimeReplicatedProps(
    TArray<FLifetimeProperty>& OutLifetimeProps) const
{
    Super::GetLifetimeReplicatedProps(OutLifetimeProps);
    DOREPLIFETIME(AMyPlayerState, KillCount);
}

APlayerState には SetScore() / GetScore() が組み込まれています。カスタムの変数(ここでは KillCount)を追加する場合は、同じく Replicated + DOREPLIFETIME のパターンで登録します。

RepNotify: 変数の変更をクライアント側で検知してUIを更新したい場合は、UPROPERTY(ReplicatedUsing=OnRep_KillCount)UFUNCTION() void OnRep_KillCount() を組み合わせます。サーバーで値が変更されると、クライアント側で OnRep_ コールバックが自動的に呼ばれるため、スコア表示の更新処理をここに書くのが定石です。

シングルプレイヤーの場合: マルチプレイヤーを想定しないゲームでは、GameState/PlayerStateを省略してGameModeに多くのロジックを寄せる簡略パターンも有効です。ただし、将来マルチプレイヤーに拡張する可能性がある場合は、最初から3クラスに分離しておくと移行コストが大幅に下がります。

まとめ

  • GameMode = ルールブック。サーバーのみに存在し、クライアントには複製されない
  • GameState = スコアボード。ゲーム全体の状態を全クライアントに同期
  • PlayerState = 個人の成績表。プレイヤーごとの情報を全クライアントに同期
  • GameModeのコンストラクタで GameStateClass / PlayerStateClass を指定してカスタムクラスを使う
  • UPROPERTY(Replicated) + DOREPLIFETIME マクロでレプリケーション変数を登録

さらに学ぶために