敵を倒したらスコアを増やし、チームが10点を取ったら勝利にしたい。このとき、「10点で勝ちというルール」「チームの今の得点」「自分が倒した数」は、どこへ置けばよいでしょうか。
UEには、ゲームの土台となる役割を分担した Game Framework があります。なかでも名前が似ていて迷いやすいのが、GameMode・GameState・PlayerStateです。まず ルールを決める役、全体の記録、個人の記録 に分けると、使いどころが見えてきます。
この記事ではスコアを例に3つの違いを整理し、Blueprintで自分のクラスを設定して、保存した値を読み出すところまで試します。オンライン対戦で情報が共有される仕組みも、この役割分担につなげて説明します。
この記事でわかること
- GameMode・GameState・PlayerStateへ何を置くか
- GameStateとPlayerStateが持つ情報の違い
- サーバー・クライアント・複製の意味
- Blueprintで3クラスを設定し、GameStateの値を読む方法
まずは「ルール・全体・個人」で分ける
チーム戦を例にすると、次のように分けられます。クラスは、役割を持つオブジェクトを作るための型です。Blueprint Classも、自分でその型を作る方法の1つです。その型から作られ、プレイ中に実際に動いているものを、この記事では「実体」と呼びます。
| クラス | 担当 | チーム戦での例 |
|---|---|---|
| GameMode | ルールに従って処理を決める | 敵を倒したら加点し、先に10点取ったチームを勝ちにする |
| GameState | ゲーム全体の現在の状態 | 赤チーム3点、青チーム2点、試合は進行中 |
| PlayerState | プレイヤーごとの状態 | Aさんは2キル、Bさんは1キル |
「どうなったら勝ちか」と「今は何点か」は別の情報 です。前者をGameMode、後者をGameStateへ分けます。さらに「そのうち誰が何点取ったか」はPlayerStateへ置きます。

シングルプレイヤーでも、この考え方は使えます。ただし、小さな試作の数字をすべて3クラスへ分ける必要はありません。記事の前半で役割をつかみ、必要な部分から使っていきましょう。
GameMode:ルールを決める審判
GameMode は、ゲームの進め方を決める役です。ルールを書いた本に加えて、そのルールどおりに判断する審判も兼ねています。
他のエンジンでいうと: Unity / Godot に、これと同じ位置づけの標準クラスはありません。場面ごとに自作の管理スクリプトを置くことが多い部分が、UEでは Levelに紐づくクラスとして最初から用意されています 。
- プレイヤーが参加したとき、どのキャラクターをどこへ出すか
- 敵を倒したとき、誰に何点を加えるか
- 勝利条件を満たしたとき、試合をどう終えるか
これらが自分のゲームに合わせて、すべて自動実装されるわけではありません。たとえば「10点で勝ち」なら、得点を更新した後に10以上か比べ、試合を終了する処理をGameModeへ作ります。
使うGameModeは、プロジェクトの「Maps & Modes」で既定値を選べます。特定のレベル だけ変えるなら、そのレベルの「World Settings → GameMode Override」を使います。レベル側の指定が、プロジェクトの既定値より優先 されます(→ 初期設定の記事)。

GameState:全体の状態を持つ掲示板
GameState は、全員が知りたいゲームの状態をまとめます。チームの得点や試合の進行状況など、特定の1人に属さない情報が向いています。

「掲示板」「スコアボード」という例えは、持っている情報を表しています。GameStateを作るだけで、画面上に得点表が表示されるわけではありません。 数字を画面へ出すUIは、GameStateの値を読んで別途作ります。
自作のチーム得点なら、たとえば RedTeamScore と BlueTeamScore という変数を追加します。変数は値を覚えておく場所です。名前を付けただけで加点処理ができるわけではなく、GameModeなどから値を更新します。
PlayerState:1人ずつの成績表
PlayerState は、各プレイヤーの名前や個人成績を持ちます。自分だけの秘密の情報という意味ではなく、「誰の情報か」が1人に結び付いている のがポイントです。ほかの人の名前や得点を一覧に出すときにも使えます。
GameStateの PlayerArray には、参加者のPlayerStateへの参照が入っています。Arrayは複数の値を並べて持つ入れ物、参照は「その相手を指定するためのもの」です。GameStateから一覧をたどると、それぞれの成績表へアクセスできます。

図のNameは名前、Scoreは得点、Pingは通信の応答時間の目安です。PlayerStateには名前や得点を扱う機能があり、自分のゲーム用にチーム番号や集めたコイン数などを足せます。たとえば協力ゲームなら、GameStateに「全員で集めた数」、各PlayerStateに「その人が集めた数」を持たせられます。
操作する体とは分けておく
Pawn は操作される体、PlayerController はプレイヤーの操作を受け持つ役です。歩く人物向けのCharacterもPawnの仲間です。

同じ試合中に倒されたキャラクターを作り直す場合、Pawnだけを交換してPlayerStateを残せば、個人成績を引き継げます。HPなど今の体に属する値と、倒されても残したい得点を分ける理由がここにあります(→ チェックポイントとリスポーン)。
オンラインでは、誰が結果を決める?
オンライン対戦では、各プレイヤーが別々のゲーム画面を動かしています。全員が自分の画面だけで勝敗を決めると、結果が食い違ってしまいます。
そこで、サーバー がゲームの正式な状態を管理し、クライアント がそこへ接続してプレイします。ホストのPCがサーバーを兼ねることも、専用のサーバーを使うこともあります。

| クラス | サーバー | 接続先のクライアント |
|---|---|---|
| GameMode | ル ールを処理する実体がある | その実体はない |
| GameState | 全体の状態を持つ | 複製された状態を持つ |
| PlayerState | 各プレイヤーの状態を持つ | 各プレイヤーの複製された状態を持つ |
サーバー側の値をクライアント側へ伝えて、状態をそろえる仕組みを レプリケーション(複製) と呼びます。GameStateとPlayerStateはこの仕組みに対応しています。ただし、自分で追加した変数まで全部自動で共有されるわけではありません。共有したい変数へ、Blueprintでは「Replicated」や「RepNotify」を設定します。
GameModeの実体はサーバーだけにあるため、接続先のクライアントから Get Game Mode を呼んでも取得できません。みんなに見せる値をGameModeだけへ置くと、表示側が読めなくなる理由です。
シングルプレイヤーでは、1つのゲームがルールの処理と画面表示を両方行います。外部のサーバーへ接続する必要はなく、GameModeもそのゲーム内に存在します。
スコアがほかの人の画面へ届くまで
「敵を倒して得点する」という場面を、役割のつながりで見てみましょう。

- サーバー側で、誰が敵を倒したかを確かめま す。
- GameModeがルールに従って、そのプレイヤーへ加点すると決めます。
- 該当するPlayerStateの個人成績を更新します。チームの得点もあるならGameStateへ反映します。
- 複製対象に設定した値の変更が、サーバーからクライアントへ届きます。
- 各画面のUIが、その値を読んで表示を更新します。
ここで分けたいのは、結果を決める処理、値を共有する仕組み、画面へ出す処理 の3つです。複製されても、UIを更新する処理がなければ表示は変わりません。また、通信が入るので全画面が厳密に同じ瞬間に変わるとは限りません。
クライアントからサーバーへ操作を伝えるには、RPCという「別のゲーム側へ処理を頼む仕組み」などを使います。通信の実装は マルチプレイヤーとレプリケーションの基礎 で扱います。ここではまず、情報の置き場を見分けられれば十分です。
実践:Blueprintで3クラスを設定する
まず1人用の試験で、自分のGameMode・GameState・PlayerStateが使われていることを確認 します。その後、GameStateに置いた数字をキー入力で読みます。得点ゲームを作る前の、土台の準備です。
Third Personテンプレートの試験用レベルを保存し、Play設定を「Number of Players: 1」「Net Mode: Play Standalone」にします。ここではネットワーク同期を試す必要はありません。
① 3つのBlueprintを用意する
コンテンツブラウザに練習用フォルダを作り、次のクラスを用意します。
| 作るもの | 作り方 |
|---|---|
BP_PracticeGameMode | テンプレートで使っているGameMode(通常はBP_ThirdPersonGameMode)を複製して名前を変更 |
BP_PracticeGameState | 「Blueprint Class → All Classes」からGameStateBaseを親にして作成 |
BP_PracticePlayerState | 同じくPlayerStateを親にして作成 |
GameModeは、既存のThird Person用クラスを複製します。そうすると、プレイヤーの体に使う「Default Pawn Class」など、テンプレートの操作に必要な設定を引き継げます。ここではGameModeBaseを親とするテンプレートを前提にしています。親がGameModeの場合は、GameState側もGameStateを親にそろえます。
各BlueprintのEvent Graphで、Event BeginPlay → Print String をつなぎます。BeginPlayは、その実体がプレイを始めるときに呼ばれるイベントです。複製したGameModeに既存のBeginPlay処理があれば、接続を保って末尾へ追加します。
| Blueprint | Print StringのIn String |
|---|---|
| BP_PracticeGameMode | [Framework] GameMode ready |
| BP_PracticeGameState | [Framework] GameState ready |
| BP_PracticePlayerState | [Framework] PlayerState ready |
Print Stringは「Print to Screen」「Print to Log」をオン、Durationを 5.0 にします。Compile・Saveしておきます(→ Print StringとOutput Log)。
② GameModeに、使うクラスを登録する

BP_PracticeGameModeを開き、「Class Defaults」で次の2項目を変更します。
| 項目 | 指定するクラス |
|---|---|
| Game State Class | BP_PracticeGameState |
| Player State Class | BP_PracticePlayerState |
Compile・Saveしたら、試験用レベルの「World Settings → GameMode Override」に BP_PracticeGameMode を指定し、レベルを保存します。
3つをレベルへドラッグして配置する必要はありません。 使うクラスを設定すると、UEがプレイ開始やプレイヤーの参加に合わせて実体を作ります。GameMode内の指定は「どの型を使うか」、レベル側の指定は「このレベルでどのGameModeを使うか」を決めています。
③ Playして、3つのメッセージを確認する
Playすると、3種類の ready が表示されます。表示順は成功条件にせず、GameMode・GameState・PlayerStateがそれぞれ出たことを確かめてください。見逃した場合はOutput Logで [Framework] を検索します。
GameModeだけ出て他が出ない場合は、Class Defaultsの2項目を見直します。何も出ない場合は、レベルのGameMode Override、BeginPlayからの接続、Print Stringの表示設定を確認します。
④ GameStateの数字を読み出す
Playを止め、BP_PracticeGameStateにInteger型の変数 TeamScore を作ります。Integerは 0、1、2 のような整数の型です。Compileして初期値を 0 にし、保存します。今回は1人用なので、Replicationは変更しません。
レベルエディタの「Blueprints → Open Level Blueprint」で、この試験用レベルのグラフを開きます。ここへ次の接続を作ります。

- 右クリックでキーボードの
Kイベントを置きます。 Get Game Stateを置き、そのReturn ValueからCast To BP_PracticeGameStateを作ります。Object入力へReturn Valueがつながっていることを確認します。- KのPressedを、Castの白い実行入力へつなぎます。
- Castの「As BP Practice Game State」からドラッグし、
Get TeamScoreを置きます。Castのこの出力をGet TeamScoreのTargetへつなぐことで、どの相手の値を読むかを指定します。 - Cast成功側の白い出力からPrint Stringを呼びます。Get TeamScoreの値をIn Stringへつなぐと、整数を文字へ変えるノードが間に入ります。
Get Game Stateは、今のレベルで使っている実体を取得します。 Castは、その相手がBP_PracticeGameStateかを確認し、このクラスに追加したTeamScoreを読めるようにする操作です。Castで新しいGameStateを作っているわけではありません。
Print Stringは先ほどと同じく、画面・ログ表示をオン、Durationを5にします。何も出ないときは、Cast Failedから 別のPrint Stringを呼び、GameState class mismatch と出すと、取得した相手の型が違うかを確かめられます。
Compile・SaveしてPlayし、ゲーム画面をクリックしてから K を押します。0 が出たら、作成したGameStateの値を読めています。次にPlayを止め、TeamScoreの初期値を 10 に変え、再度PlayしてKを押してください。今度は 10 になります。
ここまでで、「クラスを作る」「使うクラスを登録する」「生成された実体の値を読む」がつながりました。実際に得点を増やすゲームでは、初期値を手で変える部分を、GameModeなどの加点処理へ置き換えていきます。
| 困ったこと | 確認する場所 |
|---|---|
| Castが失敗する | 使用中のGameModeと、そのGame State Class |
| Get TeamScoreが見つからない | Castの成功結果から検索しているか、変数をCompileしたか |
| Kを押しても出ない | Play中の画面に入力が届いているか、白い実行線、Print Stringの設定 |
| 変更前の数字が出る | Playを止めて初期値を変え、Compile・Saveしてから再開したか |
| プレイヤーが動かなくなった | 複製前のGameModeとDefault Pawn Class・Player Controller Classが一致するか |
おまけ:Baseの有無と、データが残る期間
GameModeBaseとGameModeの違い
UEには、基本機能を持つGameModeBaseと、試合進行の仕組みを加えたGameModeがあります。C++の資料では、先頭にAを付けて AGameModeBase、AGameMode と表記されます。
| 組み合わせ | 選ぶ目安 |
|---|---|
| GameModeBase + GameStateBase | 必要な進行を自分で組む、シンプルな出発点 |
| GameMode + GameState | 待機・試合中・試合終了など、標準のマッチ進行を使う |

この進行状態を切り替える仕組みを、ステートマシン と呼びます。図のWaitingToStartは開始待ち、InProgressは試合中、WaitingPostMatchは終了後です。GameModeを使う場合も、勝利条件や結果画面そのものは自分で用意します。マルチプレイヤーだから必ずGameMode、1人用だから必ずGameModeBase、という分け方ではありません。
レベルを変えても残したいデータ
Pawnを作り直すことと、レベル全体を読み替えることは別です。通常のOpen Levelでレベルを切り替えると、これらの実体は作り直されるため、同じ変数がそのまま残る前提にはしません。
ゲームを起動している間、レベルをまたいで 持ちたい情報には GameInstance が使えます。ゲーム終了後も残したいなら SaveGame などへ保存します(→ レベル遷移とデータの置き場、Subsystemの使い分け)。ネットワークのSeamless Travelには一部を引き継ぐ仕組みがありますが、別途対応が必要です。
複製とUI更新は別に考える
RepNotify は、複製された値を受け取ったときの処理を用意できる設定です。表示更新のきっかけに使えます。ただし、UIを作る前に値が届くこともあるので、UIを開いた直後に現在値を読み、その後の変更にも対応する形にします。
通信を試す段階では、Play設定の違いにも注意します。「Play as Listen Server」はプレイヤーもいるサーバーを作り、「Play as Client」は画面のない専用サーバーへ接続するクライアントを作ります。後者に、Kを押して操作できるサーバー画面はありません。詳しい確認手順は、マルチプレイヤーの記事で進めましょう。
まとめ
GameModeはルールを処理する役、GameStateは全体の状態、PlayerStateは個人ごとの記録です。まず「どう進めるか」「今どうなっているか」「誰の記録か」に分けて考えると、置き場所を決めやすくなります。
3クラスを用意したら、GameModeの設定とレベルのGameMode Overrideを確認します。最初は値を1つ読み出せれば十分です。そこから加点や勝利判定を足し、オンラインで共有したくなったら複製の設定へ進みましょう。