アイテムを拾う
3D シーンの配置できるコインと鍵を拾って、Timmy が持っているアイテムを追跡しましょう。
コイン
シーンにコインを一つ作ろう(後ほどプレハブにして、何個も配置できるようにする)。まず、「Coin」の名前で空のオブジェクトを作成する。拾う処理を確認しやしくするため、Timmyの近くに配置してください。
次、「Coin」の子オブジェクトとして、3Dモデルを追加しましょう。Meshes の中にある「coin-gold」を「Coin」の中に挿入し、位置は 0, 0, 0 にしてください。
最後、3Dモデルとの接触を確認できるため、SphereCollider 型の トリガーを追加してください
※注目:3Dモデル coin-gold の位置は「親に対して 0, 0, 0」であることを確認してください。3D空間の中で動かすのは、親の Coin である!
アニメーション
コインが浮くアニメーションを作成しましょう。coin-gold を選択し、アニメーションパネルを表示してください。ここで2つの値をアニメーションで更新しましょう:位置と回転
| フレーム | 0 | 60 | 120 |
| 位置 | 0, 1, 0 | 0, 0.5, 0 | 0, 1, 0 |
| 回転 | 0, 0, 0 | 0, 180, 0 | 0, 360, 0 |
タグ
何を拾ったのかを確認したいので、SphereCollider が付いている coin-gold には「Coin」というタグを付けてください
プレハブ化
最後、親の「Coin」を新しく作った「Prefabs」フォルダーの中に入れ、プレハブ化にしてください
インベントリ
つぎ、主人公のインベントリ(アイテム管理クラス)を作成しましょう。このクラスは OnTriggerEnter を活用し、タグを確認する。「Coin」だったら、持っているコインの数を増やす。Debug.Log で確認しましょう:
スクリプト
using UnityEngine;
// アイテムを管理するインベントリ
public class Inventory : MonoBehaviour
{
// コインの数
private int coinCount;
// トリガーが発動した
private void OnTriggerEnter(Collider other)
{
// コインなの?
if (other.CompareTag("Coin"))
{
coinCount++;
Debug.Log($"コインの数:{coinCount}");
}
}
}
このスクリプトをPlayerにアタッチし、実行してみてください。
アイテム削除
一応、これで正しく数えられることが確認できるが、コインが消えないので、何度も拾うことができてしまう。拾った後に、アイテムをシーンから廃止しましょう。ここの注目ポイントは、削除するのはコインの「親」であること(コライダーは子オブジェクトにあるから)
// トリガーが発動した
private void OnTriggerEnter(Collider other)
{
// コインなの?
if (other.CompareTag("Coin"))
{
// コライダー「の」トランスフォーム「の」親「の」ゲームオブジェクト
Destroy(other.transform.parent.gameObject);
coinCount++;
Debug.Log($"コインの数:{coinCount}");
}
}
いくつかのコインを配置し、確認してみてください。
鍵
コインと同じ手順を従い、鍵のプレハブを作成してください。ただし:
- 親オブジェクトの名前:Key
- タグ:Key
にしてください。そして、Inventory クラスは:
using UnityEngine;
// アイテムを管理するインベントリ
public class Inventory : MonoBehaviour
{
// コインの数
private int coinCount;
// 鍵の数
private int keyCount;
// トリガーが発動した
private void OnTriggerEnter(Collider other)
{
// コインなの?
if (other.CompareTag("Coin"))
{
// コライダー「の」トランスフォーム「の」親「の」ゲームオブジェクト
Destroy(other.transform.parent.gameObject);
coinCount++;
Debug.Log($"コインの数:{coinCount}");
}
// 鍵なの
else if (other.CompareTag("Key"))
{
Destroy(other.transform.parent.gameObject);
keyCount++;
Debug.Log($"鍵の数:{keyCount}");
}
}
}
インベントリの改善:統一処理を一体化
この処理で、すぐ眼立つのは、似たような処理2回現れる
- コインの場合:アイテム削除→アイテムを数える→デバッグ用の表示
- 鍵の場合:アイテム削除→アイテムを数える→デバッグ用の表示
この作り方は大きな問題2つがある:
- 新しいアイテムを増やすのは大変:何度も同じ処理を追加しないといけない
- 処理を直すのも手間がかかる:何か直したい場合、1か所ではなく、アイテム1個ずつ直さないといけない。
できる限りに、統一している処理を一体にすると、ゲームの整備が楽になる!このため、新しい C# のクラス「Dictionary」を紹介
なんでも指数として使える"配列"「Dictionary」
今まで、2つのコレクションを学んできた:
- 配列:決まった数の要素を入れることができる
- リスト:サイズを決めず、自由に伸びる配列
いずれにしても、それぞれの要素を「指数」でアクセスする形になる。先頭の要素を「0番目」の要素であり、そこから連続に次々の要素をアクセスができる。
// 配列、またはリストは「指数」でアクセス
int [] numbers = new int[5];
numbers[0] = 10;
numbers[1] = 20;
numbers[2] = 30;
// など…
連続にデータを処理する場合は、配列、またはリストは最適であるが、今回のインベントリのように、連続で整理するよりも、何かの「キーワード」でアクセスしたい。具体的に、それぞれのアイテムの数を、そのアイテムの名前でアクセスしたい。
// これをやりたい…
items["Coin"] = 10; // コイン10個ある
items["Key"] = 0; // 鍵がない
これがあれば、アイテムごとの変数(coinCount, keyCount)を別々で作成する必要がない。この特別なコレクションに新しい "アイテム"を追加すれば、処理が統一できるようになる。
このコレクションは「ディクショナリー(Dictionary)」といい、リストと同様に、変数を作るのは1つだけで、中には要素を自由に追加ができるが、違いとしては、0から連続にアクセスするではなく、何かの「キー」で要素を探す。
ここで「キー」にできるのは、ほとんどなんでもOK!
- 整数(int)
- 文字列(string)
- 他のオブジェクト(GameObject)
- なんでも良い(比較で等しいかどかを確認できれば)
書式
ディクショナリーの宣言は、以下のようになる:
Dictionary<キーの型、要素の型> ディクショナリー名;
そして、新しいディクショナリーを作るには:
ディクショナリー名 = new Dictionary<キーの型、要素の型>();
例えば、文字列→整数のディクショナリーは
// 学生の点数
// キー:文字列
// 要素:整数
Dictionary<string, int> studentScore = new Dictionary<string, int>();
// 代入
studentScore["太郎"] = 75;
studentScore["大原"] = 91;
// 読み込む
int score = studentScore["太郎"];
Debug.Log($"太郎さんの点数は{score}");
または、列挙→文字列でもOKですし:
// 武器の列挙
public enum Weapon
{
Sword,
Pistol,
Axe,
Rock
}
// 各武器の日本語表示
// キー:武器(列挙)
// 要素:文字列
Dictionary<Weapon, string> weaponName = Dictionary<Weapon, string>();
// 代入
weaponName[Weapon.Sword] = "剣";
weaponName[Weapon.Axe] = "斧";
// 読み込む
int name = weaponName[Weapon.Pistol];
Inventory クラスの改善
では、Dictionary を使用し、処理を一体化しましょう:
using System.Collections.Generic;
using UnityEngine;
// アイテムを管理するインベントリ
public class Inventory : MonoBehaviour
{
// アイテムの数(タグ → 数)
private Dictionary<string, int> itemCount;
private void Start()
{
// ディクショナリーを作成
itemCount = new Dictionary<string, int>();
// 有効なアイテムとその初期の数
itemCount["Coin"] = 0;
itemCount["Key"] = 0;
}
// トリガーが発動した
private void OnTriggerEnter(Collider other)
{
// 相手のタグを求める
string itemTag = other.tag;
// そのタグあるかないかを確認
// "ContainsKey" は、ディクショナリーの中に
// 定めた「キー」があると、「true」を返す
bool isItem = itemCount.ContainsKey(itemTag);
// 有効なアイテじゃなければ、何もしない
if (!isItem)
return; // 終了
// コライダー「の」トランスフォーム「の」親「の」ゲームオブジェクト
Destroy(other.transform.parent.gameObject);
itemCount[itemTag]++;
Debug.Log($"{itemTag}の数:{itemCount[itemTag]}");
}
}