Unity

【脱出ゲーム制作6】データベースを活用したアイテム管理システム

オリジナルのボクセルを使って脱出ゲーム制作に挑戦!

アイテムを取得する機能を実装する

前回はLayer機能を使い、ズームとアニメーションを正しい状態で実行するようにできました。

今回はオブジェクトをクリックしたらアイテムを取得し、画面下のUIに画像を表示させるようにします。
複数のデータを管理するためにデータベース機能を活用します。

脱出ゲームには欠かせないアイテム表示!

クリックしたらオブジェクトが消えるようにする

アイテムをクリックしたらアイテムの情報を取得し、オブジェクトが消えるようにします。

アイテムを作成する

アイテム役のオブジェクトとスクリプトを作成します

オブジェクトの作成

まずは新しくオブジェクトのCubeを作成

PickUpObjスクリプトの作成

PickUpObj.cs
using UnityEngine;

public class PickUpObj : MonoBehaviour
{
    public void OnClick()
    {
        gameObject.SetActive(false);
    }
}

クリックしたらオブジェクトが消えるメソッド

スクリプトをオブジェクトに反映させる

CubeにEventTriggerとスクリプトを追加しOnClickを設定

Layerに新しい項目「ItemObj」を追加

今回はテストなのでMainCameraのEventMaskのItemObjにチェックを入れる

本来はズームしてからアイテムを取る流れになると思うので、MainCameraの時にアイテムは取れないほうがいいかも

実行

クリックしたら消えた

Itemクラスの作成

何のアイテムを取得したのかわかるようにItemクラスを作成する

Itemスクリプトの作成

「Item.cs」を作成

Unity6の場合
Create→Scripting→EmptyC#Script

(過去のバージョンの場合はMonoBehaviourの継承を削除)

クラスを作成するのでクラスの継承は無しなんだ

Item.cs
using UnityEngine;
using System;

public class Item
{
    public enum Type
    {
        Cube,
        Key,
    }

    public Type type;
}

CubeとKeyの2種類が用意された

オブジェクトのタイプを設定

PickUpObj.csにどのアイテムなのか判別するための変数を作成する

PickUpObj.cs
using UnityEngine;

public class PickUpObj : MonoBehaviour
{
    // 追加
    public Item.Type type;

    public void OnClick()
    {
        gameObject.SetActive(false);
    }
}

CubeのTypeを選択する

publicにしたことでインスペクターから選択できるようになった

アイテムボックスの作成

取得したアイテムを表示するためのアイテムボックスを作成します

UIを作成する

画面下にアイテムの画像を表示するスペースを用意します

Canvas,Panelの作成

UI→Canvasで新しくCanvasを作成する
さらにPanelを作成

最前面に表示させたいので、Canvasの「SortOrder」を他のCanvasより多い数値(今回は1)にする

アイテムを表示させるスロットの作成

Panelの下にCreateEmptyで「Slot」を作成

Slotの下にUI→imageでimageを作成

Horizontal Layout Group

Slotを複製し、Panelに「Horizontal Layout Group」を追加

Child Alignmentを「Middle Center」
Spacingの数値を変え画像の間隔をあける
下のチェックは全部外す

子オブジェクトのSlotが綺麗に並んだ

ItemBoxスクリプトの作成

ItemBox.cs
using UnityEngine;

public class ItemBox : MonoBehaviour
{
    public static ItemBox instance;
    private void Awake()
    {
        instance = this;
    }
    public void SetItem(Item.Type item)
    {
        Debug.Log(item);
    }
}

ほかのスクリプトからアクセスできるようにシングルトン化させ、SetItemメソッドで何のアイテムを取得したかのログを表示させます

PickUpObj.cs
using UnityEngine;
using static UnityEditor.Progress;

public class PickUpObj : MonoBehaviour
{
    //
    public Item.Type type;

    public void OnClick()
    {
        // 追加
        ItemBox.instance.SetItem(type);
        
        gameObject.SetActive(false);
    }
}

クリックしたらSetItemにtypeを渡し実行します

実行

実行しクリックしてみる

ログにCubeと出た

データベースの作成

スクリプタブルオブジェクトの作成

ItemDatabaseEntityというスクリプタブルオブジェクトを作成

ItemDatabaseEntity.cs
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(fileName = "ItemDatabaseEntity", menuName = "Scriptable Objects/ItemDatabaseEntity")]
public class ItemDatabaseEntity : ScriptableObject
{
    public List<Item> items = new List<Item>();
}

アセットからデータベースの作成

アセットメニューからItemDatabaseEntityを選択

内容の設定

データベースを開き内容を設定する

今のところTypeしかないけどパラメーターや画像など複数のデータを設定することができる

HierarchyにItemDatabaseを作成

ItemDatabaseスクリプトを作成

ItemDatabase.cs
using System.Threading;
using UnityEngine;

public class ItemDatabase : MonoBehaviour
{
    //PickUpObjからアクセスするためにシングルトン化
    public static ItemDatabase instance;
    private void Awake()
    {
        instance = this;
    }
    
    [SerializeField] ItemDatabaseEntity ItemDatabaseEntity;

    public Item Spawn(Item.Type type)
    {
        for (int i=0; i < ItemDatabaseEntity.items.Count; i++)
        {
            Item itemData = ItemDatabaseEntity.items[i];
            if (itemData.type == type)
            {
                return new Item(itemData);
            }
        }
        return null;
    }
}

Spawnメソッドは、typeが一致するItemを探し、新しいItemインスタンスを作成して返す

他のスクリプトも変更

PickUpObj.cs
using UnityEngine;

public class PickUpObj : MonoBehaviour
{
    public Item.Type type;

    public void OnClick()
    {
        //Spawnを実行するものを追加
        Item item = ItemDatabase.instance.Spawn(type);

        ItemBox.instance.SetItem(item); //引数を変更

        gameObject.SetActive(false);
    }
}

ItemBox.cs
using UnityEngine;

public class ItemBox : MonoBehaviour
{
    public static ItemBox instance;
    private void Awake()
    {
        instance = this;
    }
    public void SetItem(Item item)  //こちらの引数も変更
    {
        Debug.Log(item.type);  //ログも変更
    }
}

Item.cs
using UnityEngine;
using System;

[Serializable]
public class Item
{
    public enum Type
    {
        Cube,
        Key,
    }

    public Type type;
    
    // 渡されたItemの情報をコピーして新しいインスタンスを作成
    public Item(Item item)
    {
        this.type = item.type;
    }
}

Itemメソッドにより、元のデータ(ItemDatabaseEntity内のアイテム)を上書きせずに使用できる。

ItemDataBaseにItemDataBase.csをアタッチし、
さらにスクリプトにItemDataBaseEntityを設定

実行

データベースを参照しアイテムのタイプがログに表示された

クリックしたら画像を表示させる

アイテムを取得したら、関連した画像をUIに表示させるようにします。

Slot.csの作成

UIのSlot用にスクリプトを作成します

Slot.cs
using UnityEngine;
using UnityEngine.UI; // UIを取得するために追加

public class Slot : MonoBehaviour
{
    [SerializeField] Image image;

    public void Set(Item item)
    {
        image.sprite = item.sprite;
    }
}

Item.cs
using UnityEngine;
using System;

[Serializable]
public class Item
{
    public enum Type
    {
        Cube,
        Key,
    }

    public Type type;
    // 追加
    public Sprite sprite;

    public Item(Item item)
    {
        this.type = item.type;
        // 追加
        this.sprite = item.sprite;

    }
}
ItemBox.cs
using UnityEngine;

public class ItemBox : MonoBehaviour
{
    // 追加
    [SerializeField] Slot[] slots;

    public static ItemBox instance;
    private void Awake()
    {
        instance = this;
    }
    public void SetItem(Item item)
    {
        // 追加
        slots[0].Set(item);
    }
}

Slotにスクリプトを追加

SlotにSlot.csを追加し、imageを設定

Slotをプレファブ化

AssetsにPrefabsフォルダを作りHierarchyからドラッグしプレファブ化

再度Slotを複製

CanvasのItemBoxスクリプトのSlotの項目にSlotをドラッグして設定

アイテムの画像をスクリーンショットなどを使い用意する

画像の設定

Texture TypeをSpriteにし、Sprite ModeをSingleにし、Apply

ItemDatabaseEntityに画像情報を追加する

データベースに画像をドラッグして設定

右上の鍵マークを押すと、インスペクターを固定できて操作し易くなるよ

実行

クリックしたらUIに画像が表示された!

ここまでのメソッドの流れ

クリックされたら「type」を基にデータベースから対応する「Item」を取得し、新しい「Item」インスタンスを作成。
取得した「Item」を「SetItem()」で「Slot」に渡し、スロットの0番目にアイテム画像をセットする。その後、クリックされたオブジェクトを非表示にする。

左から順番に画像をセットするようにする

今のままでは一番左の0番目にしかアイテムがセットされないので正しく修正します。

Cubeと同じように球体のSphereを作成してテスト

Sphereの画像は2番目にセットされるようにしたい

Slotが空っぽかどうか判別する

Slot.csにアイテム情報を保持するようにし、空かどうか判定するメソッドを追加します

Slot.cs
using UnityEngine;
using UnityEngine.UI; // UIを取得するために追加

public class Slot : MonoBehaviour
{
    [SerializeField] Image image;
    
    // 保持するアイテム情報(ローカル変数)
    Item item = null;

    public void Set(Item item)
    {
        // 渡されたアイテム情報をローカル変数に保存
        this.item = item;
        
        image.sprite = item.sprite;
    }

    // スロットが空かどうか判定
    public bool IsEmpty()
    {
        if (item == null)
        {
            return true;
        }
        return false;
    }
}

ItemBox.cs
using UnityEngine;

public class ItemBox : MonoBehaviour
{
    [SerializeField] Slot[] slots;

    public static ItemBox instance;
    private void Awake()
    {
        instance = this;
    }
    public void SetItem(Item item)
    {
        // Slotを一つずつ確認し、空ならSetメソッドを実行
        for (int i=0; i<slots.Length; i++)
        {
            Slot slot = slots[i];
            if (slot.IsEmpty())
            {
                slot.Set(item);
                break;
            }
        }
    }
}

for文を活用し、スロットを左から順に確認し、空のスロットを見つけたらSetメソッドを実行し、ループを抜ける

実行

ちゃんと2番目にセットされた!

ここまでのメソッドの流れ

クリックされたら「type」を基にデータベースから対応する「Item」を取得し、新しい「Item」インスタンスを作成。
取得した「Item」を「SetItem()」で「Slot」に渡し、スロットの0番目(左端)から順番に確認し、スロットが空だった場合にアイテム画像をセットする。その後、クリックされたオブジェクトを非表示にする。

まとめ

データベースを活用すれば、アイテムやイベントの管理が簡単になり、脱出ゲームのバリエーションを増やすことができそうです。

例えば、アイテムに使用できる回数(耐久度)を設定したり特定のアイテムを所持していたらエンディングが変わるアイテムの取得でスコアが変わるなど色々できそうです。

とりあえず今回はここまで

参考にさせていただきました。

COMMENT

メールアドレスが公開されることはありません。 が付いている欄は必須項目です