Unityでのオブジェクト指向学習サンプル(レジ)

2023年7月24日

スーパーのレジシステムをUnityでシミュレートして、オブジェクト指向のメリットをみていきましょう

オブジェクト指向の初学では、オブジェクト指向の書き方も大切ですが、まず、何がメリットなのか、なぜ使うのか、自分が理解しやすい例えで学習しているかに着目して進めるといいでしょう

無闇に時間をかけるより、イメージを掴むように心がけましょう

実行結果

最初から画面上にある、りんごとお肉はオブジェクト(インスタンス)です
実行後に作成されるお客とバナナとレジもオブジェクトですが、インスタンス作成命令によってインスタンス化されています

実行中に商品を追加したい時

CreateItem(商品名, 値段, 画像, 場所)というフォーマットを考えてみました
このパターンでコード行を追加すると商品が場面に増えていきます

// 商品の登録
CreateItem("バナナ", 30, "fruit_banana", new Vector2(0, -3));

レジの作成はどうなってるのか?

Unityでは場面状のオブジェクトの作成はエンジン側に任せることになっています(Instantiateメソッドを呼び出す)
・・・regPrefabとは、どのようなオブジェクトを作るかを示しています(今のところは、クラスのようなものと考えてください)

// Reg reg1 = new Reg();
Reg reg1 = Instantiate(regPrefab);

イメージ

クラス図

上記のイメージを図式で表したものになります
イラストはありませんが、各クラス名、属性名(フィールド名)、振る舞い名(メソッド名)が記されています

四角形は3段に分かれている上段がクラス名(©️はクラスを表します)、中段が属性下段が振る舞いを表しています

メソッドの()内は引数になります

コード

C#の初学であれば、次のポイントに注意して確認してください

クラスは中を2つの部分に分けて分析してみます

属性(データ、フィールドともいいます)と振る舞い(動作の部分でメソッドや関数とも言います)の2つです

メソッドは、ブロック内を全て理解することを目的にせず、メソッド名から何の動作をさせようとしているのかを分析対象とします

Item.cs

商品クラスになります

属性

商品名と価格の2つで考えます

labelは表示用ですが、今回は考えなくていいでしょう

振る舞い

商品名と価格を表示するのみになります

using TMPro;
using UnityEngine;

public class Item : MonoBehaviour
{
    // 表示するためのフィールド変数(代入すると表示される)
    public TMP_Text label;

    // (フィールド)
    // 商品名
    public string name;
    // 価格
    public int price;

    // ------------------------------------

    // (メソッド、関数、プロシジャー、ファンクション)
    // 商品名と価格を表示する
    public void Show()
    {
        label.text = $"{name}の価格は{price}円です";

        // Console.WriteLineの代わり
        Debug.Log($"{name}の価格は{price}円です");
    }
}

Customer.cs

お客クラスになります

属性

お客様名複数の商品の2つで考えます

振る舞い

インスタンス作成時名前を登録する購入するの2つになります

using System.Collections.Generic;
using UnityEngine;

public class Customer : MonoBehaviour
{
    public string name;

    // カートの商品
    public List<Item> items;

    // コンストラクタ(コンストラクタの代わり)
    public void Init(string name)
    {
        this.name = name;
    }

    public void Buy(Collider2D other)
    {
        if (!other.CompareTag("Item"))
        {
            return;
        }

        Item item = other.GetComponent<Item>();
        items.Add(item);
        item.Show();
    }
}

Reg.cs

レジクラスになります

属性

ありません

レシートが記されていますが、表示用のため今回は考えなくていいでしょう

振る舞い

レシートを表示するのみになります

using TMPro;
using UnityEngine;

public class Reg : MonoBehaviour
{
    public TMP_Text receipt;

    // レシートを表示する
    public void PrintReceipt(Collider2D other)
    {
        Customer customer = other.GetComponent<Customer>();

        string messageText = "";

        messageText += $"{customer.name}さんのレシート\n";
        messageText += "----------------------------\n";

        // レシートの表示
        for (int i = 0; i < customer.items.Count; i++)
        {
            // 商品名を表示する
            messageText += $"商品{i + 1}: {customer.items[i].name} {customer.items[i].price}円\n";
        }

        // 合計金額を入れる変数を用意する
        int totalPrice = 0;

        // 商品の数だけ繰り返す
        for (int i = 0; i < customer.items.Count; i++)
        {
            // 合計金額に商品の価格を足していく
            totalPrice += customer.items[i].price;
        }

        messageText += "----------------------------\n";

        // 合計を表示する
        messageText += $"合計金額は{totalPrice}円です\n";

        receipt.text = messageText;

        // Console.WriteLineの代わり
        Debug.Log(messageText);
    }
}

RegSystem.cs

レジのシステム全体をコントロールするクラスになります

属性

Unity特有のPrefab(プレファブ)を使っているため、理解が及ばないかもしれません
C#でのクラス(いくつかのクラスを合わせたもの)と考えるのみでいいでしょう

お客様型の変数の宣言商品の変数の宣言レジの変数の宣言があります

振る舞い

スタートメソッド(Startメソッド)からコードが始まります

ここでは、お客様の山田さんの作成レジの作成商品の登録を行なっています

また、複数の商品が簡単に登録できるように、商品の作成はメソッドとして分割しています
まとめることで重複したコードを作成することを防げます(処理の共通化、保守性の向上)

using UnityEngine;

public class RegSystem : MonoBehaviour
{
    // お客の雛形(設計図)
    public Customer customerPrefab;

    // 商品の雛形
    public Item itemPrefab;

    // レジの雛形
    public Reg regPrefab;

    //public void Run()
    private void Start()
    {
        // Customer customer1 = new Customer("山田");
        Customer customer1 = Instantiate(customerPrefab);

        customer1.Init("山田");

        //Reg reg1 = new Reg();
        Reg reg1 = Instantiate(regPrefab);

        //reg1.PrintReceipt(customer1);
        // レシートの印刷は、レジに到着した時に変更

        // 商品の登録
        CreateItem("バナナ", 30, "fruit_banana", new Vector2(0, -3));
        //CreateItem("秋刀魚", 120, "fish_sakana_sanma", new Vector2(5, -3));


    }

    private void CreateItem(string name, int price, string image, Vector2 pos)
    {
        Item item = Instantiate(Resources.Load<Item>("Item"), pos, Quaternion.identity);
        item.name = name;
        item.price = price;
        item.GetComponent<SpriteRenderer>().sprite = Resources.Load<Sprite>(image);
    }
}

その他、採用している機能(参考)

難易度が上がるため、詳細は今後の学習に委ねるとして、次のような機能も使っています

イベント

お客を移動させて、商品やレジと接触させています
接触する(当たり判定とも言います)時に実行されるメソッドを登録できる機能を使っています

お客が商品に接触した時

お客クラスの商品購入メソッドが実行されるように登録されています

お客がレジに接触した時

レジクラスのレシート表示処理メソッドが実行されるように登録されています

コード

using UnityEngine;
using UnityEngine.Events;

public class OnTrigger : MonoBehaviour
{
    public UnityEvent<Collider2D> unityEvent;

    private void OnTriggerEnter2D(Collider2D collision)
    {
        unityEvent.Invoke(collision);
    }
}