スポンサーリンク
UnityUnityメモ

Unityでゲーム進行フラグを安全に管理する方法|bool地獄を回避する設計パターン3選

Unity
  1. はじめに
  2. 1. Unityにおける「ゲーム進行フラグ」とは何か
    1. ゲーム進行フラグの特徴
  3. 2. よくある失敗例:bool地獄が生まれる瞬間
    1. ① フラグが増えるたびにboolを追加していく
    2. ② GameManagerが巨大化する
    3. ③ Dictionary<string, bool> による文字列管理
    4. ④ フラグの意味がコードから読み取れなくなる
  4. 3. 解決策① ScriptableObjectを使ったフラグ管理設計
    1. なぜScriptableObjectがフラグ管理に向いているのか
    2. ① 単一フラグを表すScriptableObjectを作る
    3. ② フラグをリストで管理する
    4. ③ 各スクリプトから直接フラグを参照する
  5. 4. フラグ管理とセーブ処理をどうつなぐか
    1. ScriptableObjectは自動で保存されない
    2. セーブ処理まで含めて「フラグ管理」
    3. Easy Save 3 を使うと何が楽になるのか
  6. 5. 解決策② Enum × Dictionaryによる安全なフラグ管理
    1. なぜ string キーは危険なのか
    2. Enumを使うと何が変わる?
    3. Enum × Dictionary の基本的な考え方
    4. この設計が向いているケース
  7. 6. 解決策③ 定数クラス+エディタ拡張による自動管理
    1. なぜ定数クラスで管理するのか
    2. 自動生成で人為的ミスを減らす
    3. FlagManagerで一元管理する
    4. この設計が向いているプロジェクト
  8. 7. 補足:ビットフラグという選択肢(使いどころ注意)
    1. ビットフラグの基本的な仕組み
    2. ビットフラグのメリット
    3. ただし、注意点も多い
  9. 8. フラグ管理設計で気をつけたい運用ルール
    1. ① 進行フラグと一時的な状態を混ぜない
    2. ② フラグの命名は「イベント基準」で行う
    3. ③ フラグは「基本的に戻らない」前提で設計する
    4. ④ フラグの追加・削除は慎重に行う
    5. ⑤ デバッグしやすい仕組みを用意する
  10. まとめ
    1. あわせて読みたい
    2. 参考文献・参考リンク
  11. よくある質問(FAQ)
    1. 関連記事:

はじめに

Unityでゲームを作っていると、必ず出てくるのが「ゲーム進行フラグ」の管理です。 「宝箱を開けたか」「ボスを倒したか」「チュートリアルは終わったか」など、最初は小さなboolから始まりますよね。

でも……開発が進むにつれて、boolがどんどん増えていって、
「これ何のフラグだっけ?」
「どこでtrueになってるの?」
「消したら壊れそうで触れない……」

こんな状態になった経験、ありませんか?😅

いわゆる「bool地獄」です。 これは初心者だから起きる問題ではなく、Unityでゲームを作っていれば誰でも一度は通る設計の壁なんですよ。

さらに厄介なのが、ゲーム進行フラグは シーンを跨いで参照され、セーブデータとして保存され、将来の仕様変更にも耐える必要がある という点です。 つまり「とりあえずboolでOK」という考え方が、後から大きな負債になりやすいんですね。

この記事では、そんなbool地獄から抜け出すために、 Unityでゲーム進行フラグを安全・拡張しやすく管理する設計パターンを、規模別に紹介していきます。

ScriptableObjectを使った管理方法、Enumによる型安全な設計、 さらに一歩進んだエディタ拡張を使った運用まで、
「今のプロジェクトにどれを選べばいいか」が分かる構成にしています。

「今は小規模だけど、後で困りたくない」
「GameManagerがもう限界」
そんな方は、ぜひこのまま読み進めてみてください✨


1. Unityにおける「ゲーム進行フラグ」とは何か

まずは整理からいきましょう。
Unityでいうゲーム進行フラグとは、 「プレイヤーの行動やイベントの結果を記録しておくための状態情報」のことです。

例えば、こんなものが代表的ですね。

  • 宝箱をすでに開けたか
  • 特定のボスを倒したか
  • チュートリアルが完了しているか
  • このイベントは一度再生済みか
  • 次のエリアに進める条件を満たしているか

これらは一見すると「ただのON / OFF」に見えます。 だから最初は、bool変数で管理したくなるんですよね。

でも、ゲーム進行フラグには普通のboolとは決定的に違う特徴があります。

ゲーム進行フラグの特徴

  • シーンを跨いで参照される
  • 一度立ったら基本的に戻らない
  • セーブ/ロードの対象になる
  • 複数のスクリプトから参照される
  • 将来の仕様変更の影響を受けやすい

つまり、ゲーム進行フラグは 「一時的な状態」ではなく「ゲーム全体の履歴・文脈」なんです。

にもかかわらず、これを
GameManagerにboolを並べる
if文で条件分岐を重ねる
という形で扱い始めると、だんだんと設計が歪んでいきます。

小規模なうちは問題が見えにくいですが、 イベントが増え、シーンが増え、フラグが増えた瞬間に、 管理コストだけが爆発的に増えるんですね。

次の章では、実際にどんな形で「bool地獄」が生まれてしまうのか、 よくある失敗パターンを具体的に見ていきます。




2. よくある失敗例:bool地獄が生まれる瞬間

では、なぜゲーム進行フラグはこんなにも管理が崩れやすいのでしょうか。
ここでは、Unity開発で本当によく見る失敗パターンを整理してみます。

① フラグが増えるたびにboolを追加していく

最初はこんな感じから始まります。

  • isBossDefeated
  • isTutorialFinished
  • isChestOpened

この時点では問題ありません。 でも、イベントが増えるたびにboolを追加し続けると、 「どこまでが進行フラグなのか」分からなくなっていきます。

気づいたら、
「一時的な状態」
「UI表示用の状態」
「セーブ対象の進行状態」
が全部同じクラスに混ざっている……という状態になりがちです。

② GameManagerが巨大化する

次によくあるのが、すべてのフラグをGameManagerに集約する設計です。

「どこからでも参照できるから便利」
その気持ち、すごく分かります。

でも、フラグが増えるにつれて、 GameManagerが“何でも屋”になっていきます。

  • 進行フラグ
  • シーン制御
  • UI管理
  • チュートリアル制御

その結果、 if文だらけの巨大クラスが完成します。
修正しようとしても、どこに影響が出るか分からず、 触るのが怖くなる状態ですね……。

③ Dictionary<string, bool> による文字列管理

boolが増えすぎた結果、 Dictionary<string, bool>でまとめて管理し始めるケースも多いです。

一見するとスッキリしますが、ここにも罠があります。

  • キーのスペルミスで実行時エラー
  • 大文字・小文字の違いに気づきにくい
  • IDEの補完が効かない
  • リファクタリングに弱い

つまり、 「動くけど安全じゃない設計」になりやすいんですね。

④ フラグの意味がコードから読み取れなくなる

bool地獄の一番つらいところは、 「このフラグが何を意味しているのか分からなくなる」ことです。

true / false だけでは、
「いつ立つのか」
「誰が変更するのか」
「戻ることはあるのか」
がコードから見えません。

その結果、仕様変更やバグ修正のたびに コード全体を読み返す必要が出てくるんですね。

ここまで来ると、問題は「書き方」ではなく、 設計そのものにあります。

次の章からは、こうしたbool地獄を避けるために、 実際に使えるフラグ管理の設計パターンを一つずつ紹介していきます。




3. 解決策① ScriptableObjectを使ったフラグ管理設計

bool地獄を避けるための、まず最初の現実的な解決策が ScriptableObjectを使ってフラグを管理する方法です。

ScriptableObjectは、 「シーンに依存しないデータをアセットとして保持できる」 という特徴を持っています。 これが、ゲーム進行フラグと非常に相性がいいんですね。

なぜScriptableObjectがフラグ管理に向いているのか

  • シーンを跨いでも値を保持できる
  • インスペクターで現在の状態を確認できる
  • 複数のスクリプトから安全に参照できる
  • 「データ」と「処理」を分離できる

つまり、フラグを
「ただの変数」ではなく「ゲームの状態データ」 として扱えるようになります。

① 単一フラグを表すScriptableObjectを作る

まずは、1つのフラグを1つのアセットとして表現します。

プロジェクトウィンドウを右クリックして 「Create」→「C# Script」を選び、 新しいスクリプトを作成します。 名前は FlagData としておきましょう。

このScriptableObjectには、 boolの値と、そのフラグが何を意味するか だけを持たせます。

こうすることで、 「このフラグは何のためのものか」 がアセット単位で明確になります。

② フラグをリストで管理する

次に、複数のフラグをまとめて管理するための FlagList(フラグ一覧用のScriptableObject)を用意します。

ここでは、List<FlagData>として フラグを登録していきます。

この構成にすると、
「どんな進行フラグが存在するのか」
「今どこまで進んでいるのか」
インスペクター上で一目で分かるようになります。

③ 各スクリプトから直接フラグを参照する

実際のゲームロジックでは、 GameManagerを経由せずに、 必要なFlagDataアセットを直接参照します。

例えば、
「この宝箱は、すでに開けられているか?」
といった判定も、 FlagDataのboolを見るだけで済みます。

これにより、

  • 不要な依存関係が減る
  • クラス同士の責務が明確になる
  • テストや修正がしやすくなる

ScriptableObjectによるフラグ管理は、 小〜中規模のプロジェクトで特に効果を発揮します。
「まずはbool地獄から抜け出したい」 という人にとって、最初に選ぶべき設計です。

ただし、この方法にも 必ず直面する壁があります。

それが、 「このフラグ、どうやってセーブするの?」 という問題です。

次の章では、ScriptableObjectで管理したフラグを 安全にセーブ/ロードする考え方について触れていきます。




4. フラグ管理とセーブ処理をどうつなぐか

ScriptableObjectでフラグを管理し始めると、 多くの人が次にぶつかるのがこの疑問です。

「このフラグ、ゲームを終了したら消えない?」
「セーブ/ロードはどう実装するのが正解なの?」

ゲーム進行フラグは、 シーンを跨ぐだけでなく、プレイセッションを跨いで保持されるべき情報です。 つまり、セーブ処理は避けて通れません。

ScriptableObjectは自動で保存されない

ここで一つ、重要な注意点があります。

ScriptableObjectはアセットとして存在するデータですが、 実行中に変更した値は、自動で保存されるわけではありません。

そのため、

  • フラグを立てたのに再起動すると戻る
  • エディタでは動くのにビルド後に消える

といったトラブルが起こりやすくなります。

ここを自力で実装しようとすると、
JSON変換
ID管理
データ破損対策
バージョン差分対応
など、想像以上に考えることが増えていきます。

セーブ処理まで含めて「フラグ管理」

ゲーム進行フラグは、 立てることより「安全に保持し続けること」のほうが重要です。

特に、

  • フラグ数が増えてきた
  • 後から仕様変更する可能性がある
  • セーブデータ破損は絶対に避けたい

という状況なら、 セーブ処理を自前で抱え込まないという選択肢も、かなり現実的です。

Easy Save 3 を使うと何が楽になるのか

そこで役立つのが、 Unity Asset Storeでも定番のセーブ管理アセット Easy Save 3です。

Easy Save 3 は、

  • bool・List・Dictionary・ScriptableObjectに対応
  • コード量を抑えてセーブ/ロードを実装できる
  • データ破損対策や上書き管理が考慮されている

といった特徴があります。

特に、 「ScriptableObjectで管理したフラグを、そのまま保存したい」 というケースでは相性が良く、 フラグ管理とセーブ処理をきれいにつなげることができます。

セーブ実装に悩む時間を減らして、 ゲームの中身に集中したい人には、 十分に検討する価値のあるアセットです。

Easy Save 3
Asset Storeでチェックする

次の章では、ScriptableObjectとは別のアプローチとして、 Enumを使った型安全なフラグ管理設計を紹介していきます。




5. 解決策② Enum × Dictionaryによる安全なフラグ管理

次に紹介するのは、 Enum(列挙型)とDictionaryを組み合わせたフラグ管理です。

これは、
「ScriptableObjectを量産するほどではない」
「でも、stringキーのDictionaryは不安」
という中規模プロジェクトで特に使いやすい設計です。

なぜ string キーは危険なのか

bool地獄の章でも触れましたが、 Dictionary<string, bool>には大きな弱点があります。

  • スペルミスしてもコンパイルエラーにならない
  • IDEの補完が効かない
  • 名前変更時に追従されない

これらはすべて、 実行して初めて気づくバグにつながります。 進行フラグでこれが起きると、かなり致命的です。

Enumを使うと何が変わる?

そこで登場するのが Enum です。

フラグ名をEnumとして定義しておけば、 フラグの存在そのものを「型」で保証できます。

例えば、

  • BossDefeated
  • TutorialFinished
  • OpenedFirstChest

といった形で定義することで、 存在しないフラグを参照すること自体が不可能になります。

Enum × Dictionary の基本的な考え方

実装の考え方はシンプルです。

  • Enum:フラグの種類を定義する
  • Dictionary<Enum, bool>:フラグの状態を保持する

これだけで、 型安全・補完あり・リファクタ耐性あり という、かなり安心できる構成になります。

この設計が向いているケース

  • フラグ数が数十個程度
  • 進行フラグの種類が比較的固定
  • コードベースで管理したい
  • チーム開発で命名ルールを統一できる

逆に、 頻繁にフラグが追加・削除されるプロジェクトや、 非エンジニアが触る必要がある場合は、 ScriptableObjectのほうが向いています。

Enum × Dictionary は、 「安全だけどシンプル」な選択肢です。 bool地獄を抜け出す第一歩としても、とても扱いやすいですよ。

次の章では、さらに一歩進んで、 定数定義とエディタ拡張を組み合わせた大規模向け設計を紹介します。




6. 解決策③ 定数クラス+エディタ拡張による自動管理

ここまで紹介してきた方法でも対応しきれないのが、 フラグ数が多く、長期間運用されるプロジェクトです。

そんなケースで有効なのが、 定数クラスとエディタ拡張を組み合わせたフラグ管理設計です。

なぜ定数クラスで管理するのか

フラグを定数として定義することで、 フラグ名をコード上で一元管理できます。

例えば、

  • フラグ名の重複を防げる
  • 命名ルールを強制できる
  • 参照箇所を一括で追える

といったメリットがあります。

特に、 セーブデータと紐づくフラグでは、 名前の揺れや削除が致命傷になりやすいため、 定義の集中管理が重要になります。

自動生成で人為的ミスを減らす

ただし、定数クラスだけでは インスペクターでの視認性が弱くなりがちです。

そこで、 エディタ拡張を使って定数クラスからフラグ一覧を自動生成 する仕組みを作ります。

具体的には、

  • 定数クラスに const string でフラグ名を定義
  • リフレクションで定数を取得
  • ScriptableObject に自動登録

という流れです。

これにより、 コードとデータのズレをほぼゼロにできます。

FlagManagerで一元管理する

実行時には、 FlagManagerのような管理クラスを用意し、 自動生成されたフラグ一覧を元に状態を管理します。

ここで重要なのは、 フラグの生成・初期化・保存処理を1か所に集約 することです。

こうすることで、

  • どこでフラグが作られているか明確
  • 初期化漏れを防げる
  • テストがしやすい

という設計になります。

この設計が向いているプロジェクト

  • RPG・ADVなどフラグ数が非常に多い
  • 長期アップデートを前提としている
  • 複数人で開発している
  • セーブデータの互換性を強く意識する必要がある

正直に言うと、 すべてのプロジェクトで必要な設計ではありません。

でも、 「後から絶対に困りたくない」 という規模・状況なら、 最初から検討する価値は十分にあります。

次の章では、少し毛色の違う選択肢として、 ビット演算によるフラグ管理についても触れておきます。




7. 補足:ビットフラグという選択肢(使いどころ注意)

ここまで紹介してきた設計とは少し方向性が違いますが、 フラグ管理の方法として ビットフラグ(ビット演算)という選択肢もあります。

これは、 1つの数値の中に複数のON / OFF状態を詰め込む という考え方です。

ビットフラグの基本的な仕組み

例えば、int や uint 型の変数を1つ用意して、 各ビットをフラグとして使います。

  • 1ビット目:チュートリアル完了
  • 2ビット目:ボス撃破
  • 3ビット目:宝箱開封

操作はビット演算で行います。

  • フラグを立てる: flags |= (1 << n)
  • フラグを確認: (flags & (1 << n)) != 0
  • フラグを下ろす: flags &= ~(1 << n)

ビットフラグのメリット

  • 非常に省メモリ
  • 保存・通信に向いている
  • 処理が高速

特に、 フラグ数が少なく、用途が明確な場合には、 シンプルで強力な手段になります。

ただし、注意点も多い

一方で、ビットフラグには明確なデメリットもあります。

  • 可読性が非常に低い
  • デバッグがしづらい
  • 途中でビット順を変えられない
  • 仕様変更に弱い

特にゲーム進行フラグでは、 「後から意味が分からなくなる」 という問題が起きやすいです。

そのため、 大規模な進行管理にビットフラグを使うのは非推奨です。 小規模・限定用途に留めておくのが安全ですね。

次の章では、ここまでの内容を踏まえて、 フラグ管理設計で気をつけたい運用ルールをまとめます。




8. フラグ管理設計で気をつけたい運用ルール

ここまで、いくつかのフラグ管理パターンを紹介してきましたが、 実は設計そのもの以上に重要なのが「運用ルール」です。

どんなに良い仕組みを作っても、 ルールが曖昧だと、時間とともに必ず崩れていきます。

① 進行フラグと一時的な状態を混ぜない

まず大事なのは、 セーブ対象の進行フラグ一時的な状態(現在戦闘中、UI表示中など) を明確に分けることです。

この2つが同じクラスに混ざり始めると、 「これは保存されるべき?されない?」という判断が 毎回必要になり、事故の元になります。

② フラグの命名は「イベント基準」で行う

フラグ名は、 状態ではなく「何が起きたか」を基準にすると、 後から読み返したときに理解しやすくなります。

例えば、
isBossAlive → BossDefeated
canOpenGate → GateUnlocked
のように、 完了・発生ベースで考えるのがおすすめです。

③ フラグは「基本的に戻らない」前提で設計する

ゲーム進行フラグは、 一度立ったら基本的に戻らないケースがほとんどです。

もし戻る可能性があるなら、 それは進行フラグではなく ゲーム状態(ステート)として扱うべきかもしれません。

④ フラグの追加・削除は慎重に行う

特にセーブデータと紐づいているフラグは、 削除・名前変更がそのまま互換性問題になります。

使わなくなったフラグも、 すぐに消さず、 「非推奨」扱いで残すという選択が 安全な場合も多いです。

⑤ デバッグしやすい仕組みを用意する

フラグ管理は、 デバッグのしやすさがそのまま開発効率に直結します。

  • 現在立っているフラグ一覧を確認できる
  • インスペクターやデバッグUIで変更できる
  • ログで変更履歴を追える

こうした仕組みがあるだけで、 テストや不具合対応がかなり楽になります。




まとめ

ゲーム進行フラグの管理は、 つい「とりあえずboolで済ませる」選択をしがちですが、 その積み重ねが後々のbool地獄を生み出します。

これはスキル不足ではなく、 設計を考えるタイミングがなかっただけ というケースがほとんどです。

本記事では、規模や目的に応じて選べる フラグ管理の設計パターンを紹介してきました。

  • 小〜中規模向け:ScriptableObjectによるフラグ管理
  • 中規模・コード重視:Enum × Dictionary
  • 大規模・長期運用:定数クラス+エディタ拡張
  • 限定用途:ビットフラグ

大切なのは、 「どれが正解か」ではなく「今のプロジェクトに合っているか」 を基準に選ぶことです。

また、フラグ管理は セーブ/ロードまで含めて一つの設計です。 管理方法だけでなく、データの保持・互換性まで意識すると、 後からのトラブルを大きく減らせます。

フラグは単なるON / OFFの集合ではありません。
ゲームの進行そのものを記録した「地図」です。

その地図が整理されていれば、 機能追加も修正も、ずっと楽になります。

今まさにbool地獄に片足を突っ込んでいるなら、 ぜひ今日から、フラグの扱い方を見直してみてください。 未来の自分が、きっと助かりますよ 😊


あわせて読みたい

ゲーム進行フラグの設計は、 データ管理・セーブ設計・コード設計と強く結びついています。 ここでは、今回の内容と特に相性の良い関連記事をピックアップしました。


参考文献・参考リンク

上記リンクは、ゲーム開発におけるフラグ管理・状態管理・設計思想を理解するうえで参考になる資料です。 実装方法だけでなく、「なぜその設計が必要なのか」を考える際の補助資料として活用してください。


よくある質問(FAQ)

Q
フラグはすべてScriptableObjectで管理したほうがいいですか?
A

いいえ、必ずしもそうではありません。 ScriptableObjectは非常に便利ですが、フラグ数や運用規模に合っているかが重要です。

小〜中規模で、インスペクターから状態を確認・調整したい場合は向いています。 一方で、フラグ数が多く、頻繁に追加・削除される場合は、 Enumや定数クラスによるコード管理のほうが扱いやすいこともあります。

「万能な方法」を探すより、 今のプロジェクトにとって管理しやすいかを基準に選ぶのがおすすめです。

Q
小規模なゲームでもフラグ設計を意識する必要はありますか?
A

はい、意識しておく価値は十分にあります。

最初は小規模でも、 「イベントを1つ追加しただけでif文が増える」 「後から仕様変更したくなった」 ということはよく起こります。

最初から完璧な設計にする必要はありませんが、 GameManagerにboolを並べない 進行フラグと一時状態を分ける といった最低限の意識だけでも、 後々の修正コストは大きく変わります。

Q
途中でフラグ構成を変えたくなった場合はどうすればいいですか?
A

実はこれが一番よくあるケースです。 そして、ここで設計の差がはっきり出ます。

フラグ名を直接コードに散らしている場合、 変更はかなり大変になります。 逆に、ScriptableObjectやEnum、定数クラスなどで 定義を一元管理していれば、影響範囲は最小限に抑えられます。

セーブデータと紐づいている場合は、 削除や名前変更をせず、 非推奨フラグとして残すという判断も重要です。 「もう使っていない=消していい」とは限らない点には注意しましょう。

※当サイトはアフィリエイト広告を利用しています。リンクを経由して商品を購入された場合、当サイトに報酬が発生することがあります。

※本記事に記載しているAmazon商品情報(価格、在庫状況、割引、配送条件など)は、執筆時点のAmazon.co.jp上の情報に基づいています。
最新の価格・在庫・配送条件などの詳細は、Amazonの商品ページをご確認ください。

スポンサーリンク