MVVMパターンを使ったRPGのアーキテクチャーを考えてみる



MVVM(Model-View-ViewModel)パターンはオブジェクト指向プログラミングの開発手法の一つ。プログラムで使う機能を役割ごとにモデル、ビュー、ビューモデルの3種類に分けて作るとわかりやすくなっていいよ、という考え方。RPGの裏側で動いているたくさんのプログラムファイルはいろんなデータが複雑に絡み合ってとかく煩雑になりやすいのでメンテナンス性を高めるためもぜひ取り入れたい。

それぞれの機能は以下の通り

1. Model(モデル)

これはデータの箱。ゲームの中で必要な情報が入ってる。例えば、「プレイヤーが持っているお金」や「お店で売っているアイテム」といったデータがここに入ってる。

:

  • プレイヤーのお金: 500円
  • 売っているアイテム: ポーション 50円

2. View(ビュー)

これは画面に表示される部分。実際にプレイヤーが見たり、クリックしたりするボタンやテキストのことだ。

:

  • 「500円持っています」という表示
  • 「ポーション 50円」というアイテムリスト
  • 「購入ボタン」

3. ViewModel(ビューモデル)

これはいわば仲介役。Model(データの箱)とView(表示される画面)の間で情報をやり取りする。ViewModelが「データの箱」から情報を取り出して、画面に正しく表示するように指示する。また、プレイヤーが何かをクリックしたときに必要な処理をして、その情報をModelに伝える。

:

  • プレイヤーが「購入ボタン」を押すと、ViewModelが「このアイテムを買えるか?」を確認して、買えるならプレイヤーのお金を減らし、買えなければ「お金が足りません」と教えてくれる。

ショップ機能でMVVMモデル

RPGでよくある買い物機能を例に簡単なMVVMモデルを考えてみる。

全体の流れ

  1. ModelにShopクラスとしてプレイヤーの所持金、ショップの商品のデータが入っている。
  2. ViewModelが「プレイヤーのお金」と「売っているアイテム」のデータをView(画面)に渡して、「500円持っています」「ポーション50円です」と表示する。Viewはプレイヤーからの入力を検知して具体的な処理を行ってもらうようにViewModelに要請する。
  3. プレイヤーが「ポーションを買う」ボタンを押すと、ViewModelは「買えるか?」を確認して、買えたらお金を減らし、またViewに「今の所持金」を表示する。

ポイントはビューはモデルのロジックをいじらないし知る必要もないということか。良い意味でブラックボックス化してそれぞれの役割に専念すれば良いという感じ。

1. Model(データとロジック)

Modelは、アイテムのデータや在庫、プレイヤーの所持金などを管理します。

Modelの例

public class Item
{
    public string Name { get; set; }
    public int Price { get; set; }
}

public class Player
{
    public int Gold { get; set; }
}

public class Shop
{
    public List<Item> Inventory { get; private set; }

    public Shop()
    {
        // Initial Settings
        Inventory = new List<Item>
        {
            new Item { Name = "Potion", Price = 50 },
            new Item { Name = "Sword", Price = 200 },
            new Item { Name = "Shield", Price = 150 }
        };
    }
}
  • Item: 各アイテムの名前と価格を保持。
  • Player: プレイヤーの所持金(ゴールド)を保持。
  • Shop: ショップの在庫を管理。

2. View(ユーザーに表示される部分)

Viewは、ユーザーに表示されるUI部分を管理する。UnityではCanvas上にアイテムのリストや所持金、購入ボタンなどを表示する。

Viewの例

public class ShopView : MonoBehaviour
{
    public Text playerGoldText;  // プレイヤーの所持金を表示
    public Text itemListText;    // ショップの在庫リストを表示
    public Button buyButton;     // 購入ボタン

    // 購入ボタンを押すと発火するイベント
    public event Action<Item> OnItemBought;

    public void DisplayPlayerGold(int gold)
    {
        playerGoldText.text = $"Gold: {gold}";
    }

    public void DisplayItemList(List<Item> items)
    {
        itemListText.text = string.Join("\n", items.Select(i => $"{i.Name}: {i.Price} Gold"));
    }

    // 購入ボタンが押された時の処理
    public void OnBuyButtonClicked(Item item)
    {
        OnItemBought?.Invoke(item); // 購入イベントをViewModelに伝える
    }
}
  • playerGoldText: プレイヤーの所持金を表示。
  • itemListText: ショップの在庫アイテムをリスト形式で表示。
  • buyButton: アイテム購入のためのボタン。

3. ViewModel(ModelとViewを橋渡しする部分)

ViewModelは、ショップの在庫を表示したり、プレイヤーがアイテムを購入できるかどうかを管理。

ViewModelの例

public class ShopViewModel
{
    private Shop shop;
    private Player player;

    public ShopViewModel(Shop shop, Player player)
    {
        this.shop = shop;
        this.player = player;
    }

    public List<Item> GetInventory()
    {
        return shop.Inventory;
    }

    public int GetPlayerGold()
    {
        return player.Gold;
    }

    public bool CanBuyItem(Item item)
    {
        return player.Gold >= item.Price;
    }

    public void BuyItem(Item item)
    {
        if (CanBuyItem(item))
        {
            player.Gold -= item.Price; // プレイヤーのゴールドを減らす
            shop.Inventory.Remove(item); // ショップの在庫からアイテムを削除
        }
    }
}

ShopViewModel: ショップの在庫とプレイヤーの所持金を管理し、アイテムが購入可能かどうかを判定。

4. 全体の流れ

  1. Model: ショップの在庫やプレイヤーの所持金を保持。
  2. View: ユーザーにアイテムリストや所持金を表示し、購入ボタンを提供。
  3. ViewModel: 在庫やプレイヤーのゴールドをViewに渡し、購入処理のロジックを管理。

例: ショップでの買い物の実装

public class GameController : MonoBehaviour
{
    public ShopView shopView;
    private ShopViewModel shopViewModel;

    void Start()
    {
        // モデルとしてのプレイヤーとショップデータを作成
        Player player = new Player { Gold = 500 };
        Shop shop = new Shop();

        // ViewModelを作成し、モデルを渡す
        shopViewModel = new ShopViewModel(shop, player);

        // Viewに初期データを表示
        UpdateShopView();
        
        // 購入ボタンが押された時のイベントリスナーを追加
        shopView.OnItemBought += HandleItemBought;
    }

    // ショップ画面を更新
    void UpdateShopView()
    {
        shopView.DisplayPlayerGold(shopViewModel.GetPlayerGold());
        shopView.DisplayItemList(shopViewModel.GetInventory());
    }

    // アイテム購入時の処理
    void HandleItemBought(Item item)
    {
        if (shopViewModel.CanBuyItem(item))
        {
            shopViewModel.BuyItem(item);
            UpdateShopView(); // 購入後にショップ画面を更新
        }
        else
        {
            Debug.Log("Not enough gold!");
        }
    }
}

この実装のポイント

  • Model: ショップの在庫とプレイヤーの所持金を保持。
  • View: ショップのUIを管理し、プレイヤーの入力(購入ボタンのクリック)を受け取る。
  • ViewModel: 購入可能かどうかを判定し、プレイヤーのゴールドや在庫の管理を行う。

この実装により、プレイヤーがショップでアイテムを購入するたびに、ショップの在庫とプレイヤーのゴールドが正しく更新され、UIにも反映されるようになる…はず。

Join the ConversationLeave a reply

Your email address will not be published. Required fields are marked *

Comment*

Name*

Website