digitalcontent.jp マンガ×図解解説メディア

【Unity×AI】カフェ経営ゲーム開発:第1回 GameManager&セーブ実装

編集

まなびコミック編集部 監修


Unity×AI開発講座【第1回】

『Gemini & ChatGPT × Unity』で本格カフェ経営シミュレーションを作る!

ゲームの心臓部となる「時間管理」「資金システム」「データセーブ/ロード」をAIプロンプトで一括構築しよう!


第1回の実装を開始

「経営シミュレーションゲームを作りたいけれど、時間経過や売上計算、セーブ処理をどう連携させれば良いか分からない…」

カフェ経営ゲームの土台となるのは、**「1日の時間サイクル」「資金・売上・店舗評価の管理」そして「進行データの保存(セーブ&ロード)」**です。これらはゲーム全体のデータを統括するマネージャー(GameManager)が担当します。

第1回となる本記事では、データ一元管理(シングルトンパターン)、リアルタイムUIバインド、JSON形式のデータ保存ロジックまでを、Gemini / ChatGPTにプロンプトを送ってC#コードを自動生成しながら完全実装していきます!

1. はじめに(第1回で実装する基本システム)

カフェ経営シミュレーションゲームでは、「今何時か」「いくら持っているか」「お店の評価はいくつか」というデータが常に変動します。これらを各オブジェクトがバラバラに管理すると、プログラムが乱雑になりバグの原因になります。

バラバラ管理(バグが発生しやすい構造)

客AIが直接UIのテキストを書き換えたり、レジスクリプトが別個に所持金を計算。データの矛盾や参照エラーが頻発する。

中央集権管理(GameManager構造)

すべての重要データをGameManagerに集約。データ更新時にイベントを発火させ、UIや客AIに自動通知する美しく安全な構造。

第1回では、**「時間・資金・評価の一元管理スクリプト」**と**「画面UIの自動更新」**、そしてゲームを落としてもデータを保持できる**「JSONセーブ/ロード機能」**を完成させます。

2. Step 1: ゲーム内時間・経済・評価システムの設計

まずはゲーム内で扱うパラメーターの仕様を整理しましょう。カフェ経営に必要なコアデータは以下の3点です。

カフェ経営データの構成要素とパラメータ構造

システム名 データ型 / 設定値 ゲーム内での役割・仕様
1. 時間サイクル(Time) float / DateTime(8:00〜22:00) 現実の1秒=ゲーム内の1分としてカウント。22:00に閉店し、翌日に移行。
2. 資金システム(Money) ★最重要 int(初期値:¥500,000) ドリンク売上で増加、家具購入・人件費払いで減少。¥0未満で赤字警告。
3. 店舗評価(Rating) float(★1.0 〜 ★5.0) 客の待ち時間や満足度に応じて動的変動。評価が高いほど来店率が上昇。

初心者補足:シングルトンパターン(Singleton)とは?

ゲーム内に「1つだけ存在する統括オブジェクト」をどのスクリプトからでも簡単に呼び出せるようにするC#のデザインパターンです。`GameManager.Instance.AddMoney(500);` のようにどこからでも1行でアクセスできるようになります。

\ AIにプロンプトを送ってコードを生成させよう! /


Step 2: GameManagerスクリプト生成へ

3. Step 2: AIプロンプトによる「GameManager」と「DataManager」スクリプトの生成

設計したデータ構造に基づき、Gemini / ChatGPTに指示を出して `GameManager.cs` を生成させます。

コピペで使える!AI生成用プロンプト

Unity 2022.3 (C#) で動作する「GameManager.cs」を作成してください。

【要件】
1. シングルトンパターン(Singleton)で実装してください。
2. 以下の変数(データ)を管理してください:
   - currentMoney (int) : 所持金(初期値 500000)
   - currentRating (float) : 店舗評価 1.0〜5.0(初期値 3.0)
   - currentHour (int) : 現在の時刻(8〜22時)
   - currentMinute (int) : 現在の分(0〜59分)
   - dayCount (int) : 経過日数(初期値 1)
3. 時間経過ロジック:
   - Update関数で時間を進め、現実の1秒=ゲーム内の1分として処理してください。
   - 22時になったら時間を停止し、「閉店イベント」を発火させてください。
4. C#のSystem.Action(イベント)を定義し、所持金・時間・評価が変化した際に外部(UIスクリプト等)へ通知できるようにしてください。
   - System.Action<int> OnMoneyChanged;
   - System.Action<int, int> OnTimeChanged;
   - System.Action<float> OnRatingChanged;

生成された C# スクリプト:GameManager.cs

using System;
using UnityEngine;

public class GameManager : MonoBehaviour
{
    public static GameManager Instance { get; private set; }

    // 管理データ
    public int currentMoney = 500000;
    public float currentRating = 3.0f;
    public int currentHour = 8;
    public int currentMinute = 0;
    public int dayCount = 1;

    // 時間進行用タイマー
    private float timer = 0f;
    public bool isStoreOpen = true;

    // UI更新通知用イベント
    public event Action<int> OnMoneyChanged;
    public event Action<int, int> OnTimeChanged;
    public event Action<float> OnRatingChanged;

    private void Awake()
    {
        if (Instance == null)
        {
            Instance = this;
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            Destroy(gameObject);
        }
    }

    private void Update()
    {
        if (!isStoreOpen) return;

        timer += Time.deltaTime;
        if (timer >= 1.0f) // 現実の1秒=ゲーム内1分
        {
            timer = 0f;
            AdvanceMinute();
        }
    }

    private void AdvanceMinute()
    {
        currentMinute++;
        if (currentMinute >= 60)
        {
            currentMinute = 0;
            currentHour++;
            if (currentHour >= 22) // 22時で閉店
            {
                isStoreOpen = false;
                Debug.Log("本日閉店!");
            }
        }
        OnTimeChanged?.Invoke(currentHour, currentMinute);
    }

    public void AddMoney(int amount)
    {
        currentMoney += amount;
        OnMoneyChanged?.Invoke(currentMoney);
    }

    public void SetRating(float newRating)
    {
        currentRating = Mathf.Clamp(newRating, 1.0f, 5.0f);
        OnRatingChanged?.Invoke(currentRating);
    }
}

4. Step 3: uGUI(Canvas)とデータのリアルタイムバインド(UI表示)

`GameManager.cs` が出力するイベントを受け取り、画面上部(ヘッダーUI)のテキストを自動更新する `UIManager.cs` を実装します。

uGUIとスクリプトを結びつける配置手順

1

CanvasとText (TMP) の作成

【Hierarchy】右クリック ➔【UI】➔【Canvas】を作成。その配下に【Text – TextMeshPro】を3つ作成し、名前を「MoneyText」「TimeText」「RatingText」にします。

2

UIManagerスクリプトのアタッチ

空のGameObject「UIManager」を作成し、以下の `UIManager.cs` をアタッチします。

UIManager.cs スクリプト

using UnityEngine;
using TMPro;

public class UIManager : MonoBehaviour
{
    [SerializeField] private TMP_Text moneyText;
    [SerializeField] private TMP_Text timeText;
    [SerializeField] private TMP_Text ratingText;

    private void Start()
    {
        // GameManagerのイベントにUI更新メソッドを登録(バインド)
        GameManager.Instance.OnMoneyChanged += UpdateMoneyUI;
        GameManager.Instance.OnTimeChanged += UpdateTimeUI;
        GameManager.Instance.OnRatingChanged += UpdateRatingUI;

        // 初期表示の設定
        UpdateMoneyUI(GameManager.Instance.currentMoney);
        UpdateTimeUI(GameManager.Instance.currentHour, GameManager.Instance.currentMinute);
        UpdateRatingUI(GameManager.Instance.currentRating);
    }

    private void UpdateMoneyUI(int money)
    {
        moneyText.text = $"所持金: ¥{money:N0}";
    }

    private void UpdateTimeUI(int hour, int minute)
    {
        timeText.text = $"{hour:D2}:{minute:D2}";
    }

    private void UpdateRatingUI(float rating)
    {
        ratingText.text = $"評価: ★{rating:F1}";
    }
}

\ プレイヤーの成長をしっかり保存しよう! /


Step 4: JSONセーブ&ロード機能へ

5. Step 4: JSON形式によるセーブ&ロード機能の実装

ゲームを中断しても次回プレイ時に続きから再開できるように、セーブデータを**JSON(テキストデータ形式)**としてPC/スマホ内に保存する仕組みを作ります。

JsonUtility によるシリアライズ

Unity標準機能の `JsonUtility.ToJson()` を使えば、C#のデータ構造を1行でJSONテキストデータに変換できます。

Application.persistentDataPath

Windows/Mac/iOS/Androidなど、どのプラットフォームでも確実にファイルを読み書きできる安全な保存パスを提供します。

SaveDataManager.cs スクリプト

using System.IO;
using UnityEngine;

[System.Serializable]
public class SaveData
{
    public int money;
    public float rating;
    public int dayCount;
}

public class SaveDataManager : MonoBehaviour
{
    private string saveFilePath;

    private void Awake()
    {
        saveFilePath = Path.Combine(Application.persistentDataPath, "cafesave.json");
    }

    public void SaveGame()
    {
        SaveData data = new SaveData
        {
            money = GameManager.Instance.currentMoney,
            rating = GameManager.Instance.currentRating,
            dayCount = GameManager.Instance.dayCount
        };

        string json = JsonUtility.ToJson(data, true);
        File.WriteAllText(saveFilePath, json);
        Debug.Log($"セーブ完了: {saveFilePath}");
    }

    public void LoadGame()
    {
        if (File.Exists(saveFilePath))
        {
            string json = File.ReadAllText(saveFilePath);
            SaveData data = JsonUtility.FromJson<SaveData>(json);

            GameManager.Instance.AddMoney(data.money - GameManager.Instance.currentMoney);
            GameManager.Instance.SetRating(data.rating);
            GameManager.Instance.dayCount = data.dayCount;
            Debug.Log("ロード成功!");
        }
        else
        {
            Debug.LogWarning("セーブファイルが見つかりません。");
        }
    }
}

6. まとめ & よくある質問 (FAQ)

第1回では、カフェ経営ゲームの心臓部である「GameManager」「時間・資金システム」「UI同期」「JSONセーブ機能」を構築しました。これで経営シミュレーションの骨格が完璧に固まりました!

次回:第2回「NavMesh × Animatorによる『動く客AI』」へ!

客が自動で来店し、注文・着席・飲食・退店する自律AIシステムをFSM(有限ステートマシン)で実装します!


第2回記事を読む(準備中)

よくある質問 (FAQ)

Q. シングルトンで `NullReferenceException` が出る場合はどうすれば良いですか?

GameManagerオブジェクトがHierarchy上に配置されているか確認してください。また、`UIManager` の `Start()` 関数より先に `GameManager` の `Awake()` が実行されている必要があります。

Q. セーブファイル(cafesave.json)はパソコンのどこに保存されますか?

Windowsの場合は `C:\Users\ユーザー名\AppData\LocalLow\会社名\プロジェクト名\` 内に保存されます。`Debug.Log(saveFilePath);` で保存先のパスをコンソールログで確認できます。

サクッと読めて超わかりやすい!

【無料】マンガで全部わかる!

図解&ストーリー形式だから活字が苦手でもスルスル理解!全話を一覧からチェックしよう

マンガ全話一覧を見る(無料)
マンガ全話一覧へ 次話を読む