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

作成: 2026-02-07最終更新: 2026-07-19

UEのGame Frameworkの中核、GameMode・GameState・PlayerStateの役割分担を図解。マルチプレイヤーでの複製の仕組み、スコア獲得の情報フロー、チームデスマッチの土台を組むC++実践つき。

GameMode、GameState、PlayerState。名前が似ているうえに役割の説明もどこか抽象的で、UEを学び始めると必ず一度は混乱する三兄弟です。

見分けるコツは、機能ではなく 「どこに存在して、誰に共有されるか」 で覚えることです。この軸さえ持てば、シングルプレイヤーでもマルチプレイヤーでも迷いません。この記事では、3クラスの役割分担と、スコア獲得時の情報の流れ、そしてC++でチームデスマッチの土台を組む方法を解説します。

ルールブックを読む審判の人形と、後ろのスコアボード、足元の成績カード。Game Frameworkのイメージ

この記事でわかること

  • 3クラスの違いは 「存在場所」と「複製されるか」 で覚える
  • GameMode =ルールブック、 GameState =スコアボード、 PlayerState =個人の成績表
  • スコア獲得が全員の画面に届くまでの 情報フロー
  • 実践:C++で チームデスマッチの土台 を組む(ReplicatedDOREPLIFETIME

Sponsored

3クラスの比較:どこにいて、誰に共有されるか

まず全体像です。この表が本記事の地図になります。

クラス存在場所役割複製
GameModeサーバーのみゲームのルール定義・実行されない
GameStateサーバー+全クライアントゲーム全体の状態管理される
PlayerStateサーバー+全クライアント各プレイヤーの状態管理される
サーバーには3クラスが揃い、クライアントにはGameStateとPlayerStateだけが同期される図。ルールはサーバーに、状態はみんなに

最重要ポイントは GameModeがサーバーにしか存在しない ことです。勝利条件やスポーン位置といったルールをクライアント側で改変させないための設計で、クライアントが知るべき情報は、GameStateとPlayerStateを経由して同期(複製)されます。シングルプレイヤーでは自分のPCがサーバーを兼ねるため、この構造を意識せずに動きます。ただし、内部の仕組み自体は同じです。

GameMode:ルールブック

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

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

GameModeはプロジェクト設定のマップ&モード、レベルごとのWorld Settings、またはURL引数で指定でき、レベルごとに違うルールで動かせます。

AGameModeBase vs AGameMode

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

クラス特徴推奨用途
AGameModeBaseシンプルで軽量シングルプレイヤー、非マッチベースのゲーム
AGameModeマッチステートマシン搭載チームデスマッチ等のマッチベースのマルチプレイヤー
AGameModeのマッチステートマシン。EnteringMapからLeavingMapまでの進行と異常時のAborted

AGameMode には、いわば「試合の進行表」にあたるステートマシンが組み込まれています。管理するのは6つのマッチステート(EnteringMapWaitingToStartInProgressWaitingPostMatchLeavingMap、異常時 Aborted)です。「全員が揃うまで待機」「試合後のリザルト画面」のような進行制御を自前で書かずに済みます。

GameState:スコアボード

みんなが見る掲示板としてのGameStateと、1人1枚の成績表としてのPlayerStateを対比した図

GameStateは ゲーム全体の現在の状態 を管理し、全クライアントへ同期するクラスです。GameModeが「ルールブック」なら、GameStateは会場に掲げられた「スコアボード」や「タイマー」です。

  • ゲーム経過時間: GetServerWorldTimeSeconds()(サーバー同期された正確な時間)
  • 接続プレイヤーの一覧: PlayerArray プロパティ
  • チームスコアなど、 特定のプレイヤーに紐付かない ゲーム全体の情報

「これは誰か1人のものか、全体のものか?」で仕分けます。全体のものだけがGameStateの担当です。個人のスコアや名前は、次のPlayerStateへ。

Sponsored

PlayerState:個人の成績表

PlayerStateは 個々のプレイヤーの状態 を管理するクラスです。全クライアントに複製されるので、「他のプレイヤーのスコアをランキングUIに表示する」といった用途にそのまま使えます。

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

GameStateの PlayerArray にはPlayerStateのインスタンスが入っています。つまり「スコアボード(GameState)には、全員分の成績表(PlayerState)が綴じ込まれている」という関係です。

GameStateのPlayerArrayにName・Score�・Pingを持つPlayerStateカードが3枚綴じ込まれ、それぞれが別々のプレイヤーに対応していることを示す構造図

情報フローの実例:スコアが全員に届くまで

3クラスの連携を、「プレイヤーが敵を倒してスコアを得る」シナリオで追いかけましょう。

Pawnが敵を倒し、GameModeがルールで判定し、PlayerStateがスコアを更新し、複製で全員のUIに反映される流れ
  1. プレイヤーのPawnが敵を倒す
  2. その報告がサーバーに届き、サーバー上の GameMode がルールに基づいて「スコアを加算する」と判定
  3. GameModeが該当プレイヤーの PlayerState を取得してスコアを更新
  4. スコア変数は Replicated 設定なので、サーバーでの変更が 自動的に全クライアントへ伝播
  5. 各クライアントのUIがPlayerStateの変化を受けて表示を更新

ルールの執行はサーバーのGameModeが行い、結果の共有はGameState / PlayerStateの複製が担う 。これがUEマルチプレイヤー設計の基本形です。

Sponsored

実践:チームデスマッチの土台をC++で組む

概念が繋がったところで、手を動かします。題材はチームデスマッチですが、この土台の形はレースゲームの周回ランキングでも、協力ゲームの共有進捗でも変わりません。「チーム全体の数字はGameState、個人の数字はPlayerState」という仕分けを、C++の骨格として組みます。

チームデスマッチの1場面。スコアボードは2-1、倒した瞬間にキルした側へ+1が付く。倒した瞬間、全員の画面で数字が動く

カスタムGameMode:3クラスを束ねる

ここでは最小の土台として AGameModeBase を継承します。マッチの開始待ち・リザルトといった進行管理(マッチステート)が要る本格運用なら、親を AGameMode に差し替えます。

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

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

public:
    AMyGameMode();

    // 「倒した」報告を受けてルールを執行する入口(Blueprintから呼べるようにしておく)
    UFUNCTION(BlueprintCallable, Category = "Score")
    void OnPlayerScored(APlayerController* Scorer, int32 Points);
};
// MyGameMode.cpp
#include "MyGameMode.h"
#include "MyGameState.h"
#include "MyPlayerState.h"

AMyGameMode::AMyGameMode()
{
    // 使うGameState/PlayerStateのクラスをここで指定すると、UEが自動生成してくれる
    GameStateClass = AMyGameState::StaticClass();
    PlayerStateClass = AMyPlayerState::StaticClass();
}

void AMyGameMode::OnPlayerScored(APlayerController* Scorer, int32 Points)
{
    // 該当プレイヤーの成績表を取り出して加点(ルールの執行はサーバーのここで)
    if (AMyPlayerState* PS = Scorer->GetPlayerState<AMyPlayerState>())
    {
        PS->AddScore(Points);
    }
}

カスタム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"   // DOREPLIFETIMEに必要

void AMyGameState::GetLifetimeReplicatedProps(
    TArray<FLifetimeProperty>& OutLifetimeProps) const
{
    Super::GetLifetimeReplicatedProps(OutLifetimeProps);
    // 複製したい変数はここで登録する(忘れると同期されない)
    DOREPLIFETIME(AMyGameState, TeamAScore);
    DOREPLIFETIME(AMyGameState, TeamBScore);
}

カスタム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)
{
    SetScore(GetScore() + Points);   // 組み込みのスコアを更新
    KillCount++;                     // カスタムの成績も加算
}

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

組み上がったら、動作を見てみましょう。エディタのPlayボタン横のオプションで Number of Players を 2 にし、Net Modeを Play As Client にしてPlayすると、サーバーとクライアントの2窓でテストできます。

スコアを動かすトリガーを1つ用意します。テスト用に、PlayerControllerかLevel Blueprintへ次を組みます。

キー入力 K(Pressed)
  → Get Game Mode → Cast To AMyGameMode
      → On Player Scored(Scorer: Get Player Controller, Points: 100)

Get Game Mode が値を返すのはサーバー側だけなので、 この配線はサーバー窓で押したときだけ成功 します(クライアント窓ではCastが失敗し、何も起きません)。数字を目で追うため、PlayerStateのScoreとKillCountを画面に出す Print String を1つ添えておくとよいです。

期待どおりなら、 サーバー窓でKを押した瞬間、両方の窓で Score が 0→100、KillCount が 0→1 に変わります。同期のコードは1行も書いていないのに、クライアント窓の数字まで動く。これがレプリケーションです。もし同期されなければ、DOREPLIFETIME の登録漏れか、変更を クライアント側で 行っていないか(複製はサーバー→クライアントの一方通行です)を疑ってください。レプリケーションの仕組みそのものは マルチプレイヤーとレプリケーションの基礎 で掘り下げています。

ポイントは2つです。

  • 仕分けの判断基準は「誰の数字か」: チームスコアは全体のもの→GameState、キル数は個人のもの→PlayerState。この仕分けを最初に決めれば、残りは型どおりです
  • ReplicatedDOREPLIFETIME は必ずペア: UPROPERTY(Replicated) を付けたら GetLifetimeReplicatedProps での登録まで。片方だけでは同期されません(DOREPLIFETIME_CONDITION で「オーナーのみ」等の条件付き複製もでき、帯域の節約に有効です)

おまけ:先に知っておくと良いこと

  • PlayerControllerとPawnも家族の一員: 「操作の主(PlayerController)」が「体(Pawn)」を操縦し、「成績表(PlayerState)」を持ち歩く関係です。リスポーンでPawnが消えて作り直されても、PlayerStateが残るからスコアは消えません。この「Pawnを作り直す/置き直す」という復帰の実装そのものは チェックポイントとリスポーン で扱っています
PlayerControllerが操作の主、Pawnが操作される体、PlayerStateが持ち歩く成績表。体が消えても、成績表は残る
  • UIの更新はRepNotifyが定石: 変数の複製をクライアント側で検知するには UPROPERTY(ReplicatedUsing=OnRep_KillCount)UFUNCTION() void OnRep_KillCount() を組み合わせます。値が届いた瞬間に OnRep_ 関数が呼ばれるので、スコア表示の更新処理はここに書きます
  • シングルプレイヤーなら簡略化も可: マルチ化の予定が一切ないなら、GameModeにロジックを寄せる簡略パターンも成立します。ただし将来のマルチ化が少しでも視野にあるなら、最初から3クラスに分けておくと移行コストが激減します
  • シーンをまたぐデータは別の仲間へ: GameMode一族はレベルが変わると作り直されます。レベルをまたいで生き続けるデータの置き場は GameInstance です → Subsystemの使い分け

まとめ

  • GameMode =ルールブック。サーバーのみに存在し、複製されない
  • GameState =スコアボード。ゲーム全体の状態を全クライアントに同期
  • PlayerState =個人の成績表。プレイヤーごとの情報を全クライアントに同期
  • 仕分けの軸は 「どこにいて、誰に共有されるか」「誰の数字か」
  • 複製は UPROPERTY(Replicated)DOREPLIFETIME のペアで登録

3クラスの前提となるActor・GameModeの基礎はUE5の基本概念で、C++の書き方そのものはBlueprintからC++への第一歩で扱っています。

いまあなたが構想中のゲームで、「全体の数字」と「個人の数字」、それぞれ何がありますか? 紙に書き出して仕分けてみると、設計はもう半分終わっています。

さらに学ぶために