拾ったアイテムをインベントリに置いて管理してみました。
まずこちらのサイトを参考にアイテムデータベースを作りました。
アイテム管理クラスでは、スタートでシーン上のインスタンスを静的フィールドに入れて、静的メソッドでそれを返すようにしました。また、アイテムがデータベースに存在するか調べるメソッドも作りました。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ItemManager : MonoBehaviour
{
static ItemManager instance;
public static ItemManager GetInstance()
{
return instance;
}
// ...
// Use this for initialization
void Start()
{
instance = this;
// ...
}
// ...
public bool HasItem(string searchName)
{
return itemDataBase.GetItemLists().Exists(item => item.GetItemName() == searchName);
}
}
次に取得したアイテムの画像を表示するスクロールビューを作りました。Contentの子として取得したアイテムの画像が設定されるImageオブジェクトを作ります。
ContentにGrid Layout Groupコンポーネントを追加すると綺麗に配置されます。Spacingを少し上げてImage同士に隙間を作りました。
このImageオブジェクトをプレハブ化して、シーン上のものは削除します。空のゲームオブジェクトを作って取得したアイテムを管理するスクリプトを新規作成し、シーン上のContentとProjectウィンドウのImageのプレハブをアタッチします。
アイテムを取得したいときは、まず取得可能クラスのオブジェクトをそのアイテムのクラスに持たせて、取得可能クラスのメソッドを呼んでいます。
// アイテムのクラス
using UnityEngine;
public class ObtainableItem : MonoBehaviour, ITouchable
{
[SerializeField] Obtainable obt; // 取得可能クラスのオブジェクト
// アイテムに触った時
public void Touch(GameObject sender, Vector3 point)
{
obt.Obtain(gameObject); // 取得メソッドを呼ぶ
}
public void Release(GameObject sender)
{
}
}
// 取得可能クラス
using UnityEngine;
using System;
[Serializable]
public class Obtainable
{
[SerializeField] string itemName;
GameObject gameObject;
internal void Obtain(GameObject item)
{
gameObject = item;
Inventory.GetInstance().Obtain(this);
}
internal string GetItemName()
{
return itemName;
}
internal GameObject GetGameObject()
{
return gameObject;
}
}
取得可能クラスには[Serializable]属性が付いているので、アイテムクラスを付けたプレハブのインスペクタで取得可能クラスで宣言されているフィールドの値を変えられます。
アイテムクラスが複数あっても、この取得可能クラスを書き換えれば取得方法を変えられます。
取得メソッドでは、取得可能クラスのフィールドにアイテムのゲームオブジェクトを入れて、取得したアイテムを管理するクラスのメソッドを呼びます。このときに自分自身を引数に渡しています。
取得したアイテムを管理するクラスではインスタンスを取得する静的メソッドと、アイテムを取得するメソッドが定義されています。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Inventory : MonoBehaviour
{
[SerializeField] Transform content;
[SerializeField] GameObject imagePrefab;
List<Goods> inventory = new List<Goods>();
static Inventory instance;
public static Inventory GetInstance()
{
return instance;
}
// アイテムを取得するメソッド
public void Obtain(Obtainable item)
{
// アイテムの存在を確認
if (ItemManager.GetInstance().HasItem(item.GetItemName()))
{
GameObject goodsObj = Instantiate(imagePrefab, content); // Imageインスタンスを作る
Goods goods = goodsObj.GetComponent<Goods>(); // スクリプトを取得
goods.SetUp(item); // 画像などを設定
item.GetGameObject().SetActive(false); // アイテムを非アクティブにする
inventory.Add(goods); // リストに入れる
TextManager.GetInstance().SetText(item.GetItemName() + "を取得した", 2);
}
else
{
TextManager.GetInstance().SetText("アイテム名が無効", 2);
}
}
private void Awake()
{
instance = this;
}
}
この取得メソッドに渡されたオブジェクトにはアイテム名とアイテムのゲームオブジェクトのGetメソッドがあるので、アイテム管理のメソッドにアイテム名を渡して、アイテムがデータベースに存在するかを調べられます。
あれば、Contentの子として、Imageオブジェクトを生成します。そして、このImageオブジェクトにつけるスクリプトを取得して、それをリストに入れて置きました。
Imageオブジェクトのスクリプトのクラスでは、アイテム名でデータベースを検索し、アイテムの画像を取得してImageコンポーネントに設定します。そして、アイテム名とアイテムのゲームオブジェクトを記憶しておきました。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Goods :MonoBehaviour
{
GameObject itemObject;
string itemName;
Image image;
private void Awake()
{
image = GetComponent<Image>();
}
public void SetUp(Obtainable item)
{
image = GetComponent<Image>(); // Imageコンポーネント
this.itemName = item.GetItemName(); // アイテム名を取得
// 画像を取得してImageコンポーネントに入れる
image.sprite = ItemManager.GetInstance().GetItem(this.itemName).GetIcon();
this.itemObject = item.GetGameObject(); // オブジェクトを取得
}
}
これで拾ったアイテムをインベントリに置いて管理できました。掴めるアイテムも掴めないアイテムも同じ様に取得できます。
アイテムのアイコンの画像はどうやって取得しましたか?
スクリーンショット画像をスクリプタブルオブジェクトのインスタンスにアタッチして、
そのインスタンスのメソッドを呼んでいます。
「ITouchable」の部分でエラーが起きてしまいます。
正しく打ってるはずなのですがなぜなのか教えて下さい
コンパイルエラーはcs0246です
こちらのようにインターフェースを定義していただくと良いかと思います。
定義したら今度はcs0535が出てきました
どこが定義されてないかわかりません
インターフェースを継承したクラスで、インターフェースで宣言したメソッドを実装すると大丈夫かと思います。
わかりにくくて申し訳ございません。m(__)m
goodsクラスの24行目に関してなんですが、ItemManagerにGetItemという関数は定義されていないと思うのですが、GetItemの内容を教えていただけますでしょうか。よろしくお願いします。
冒頭の参考サイトのItemManagerクラスのものと同じです。
始めのアイテム管理クラスは参考にされたサイトのItemManagerを使用していない別のものですか?
同じものに、冒頭のコードを追加しています。
アイテムを取得する際、Rayを使用してますか?
それとも別の記事のスクリプトを使用されていますか?
Physics.Raycastを使っています。
同じアイテムを拾った際に×個数と表示させようとしているのですが、Inventoryクラスのinventoryリストを使ってリスト内に入っているgoodsがあれば、×個数と表示させるためにif分を使ったり、containsメソッドをつかったりしているのですがうまくいきません。大雑把でもいいのでこんな風にやれば、できるかもしれないなどのヒントがあればぜひ教えてほしいです( ノД`)。
LinqのWhereメソッドを使って条件に合った要素をカウントできます。
参考:https://csharp.sql55.com/cookbook/csharp-how-to-count-number-of-objects-in-list-satisfying-a-condition.php