いぬおさんのおもしろ数学実験室

おいしい紅茶でも飲みながら数学、物理、工学、プログラミング、そして読書を楽しみましょう

ひとつのオブジェクトに複数の弾が当たったときに出た不具合

 ひとつのオブジェクトに向かって、下の絵のように弾を発射して当てます。1発ずつ発射するだけでなく、「ブースター」みたいな感じでガンガン撃つこともあるでしょう。

このとき、的は1つなのに複数の弾から「当たったよ」と通知がくることがあるのです。この場合、アイテムなどを取るなら相手はひとつなのに2つ、あるいは3つ取ったことになってしまいます。同じような現象はすでに確認できていて、解決もしていました。次の記事です。

www.omoshiro-suugaku.com

しかし今回は前とは違っていくつもの弾が一気に当たるということもあるんでしょう、さらに高確率で(2回に1回くらい)「2個以上のアイテムが取れた!」ということになってしまうのです。なお当たったゲームオブジェクトは即、削除しているんですが、前の記事にも書いた通りで実際に削除されるのフレームの最後だそう。つまり、本当の意味で「即、削除」されているわけではないのです。だからこんなことが起こるのですが、次で解決です。

 弾が当たったアイテムのゲームオブジェクトを記録し、別の弾が当たったときにゲームオブジェクトをさっきの記録と比較して、同じだったら当たった処理はしない(無視する)、とやればOKです。前の記事では、あまり一気に当たることはなさそうだったので記録するゲームオブジェクトは1つだけにしましたが、今回は2個以上が当たることもありそうです。実際、「3つ当たった」とされてしまうこともあります。そこで5,6個記録することにしましょう。配列を定義して自分でコードを書いてもいいんですが、目的にぴったりのデータ構造がC#にあります。キュー(Queue)と言います。まず簡単にキューについて説明しておきます。

 

static public Queue<int> queue = new Queue<int>();
とキューを定義しておくとしましょう。
    queue.Enqueue(1);
    queue.Enqueue(2);
    queue.Enqueue(3);
とやると1,2,3がキューに入ります。ここで
    queue.Dequeue();
を実行するとキューには2,3だけが残ります。このようにキューでは先に入れたデータが先に出てくるのです(先に入れたデータが後から出てくるデータ構造はスタックと言うのでした)。


 実際に相手にするのはゲームオブジェクトです。メインのスクリプト(デフォルトでは「NewBehaviourScript.cs」)で次のようにキューを定義しておきます。

static public Queue<GameObject> queue = new Queue<GameObject>();

そしてStart()内で以下のように初期化。

for(int i = 0; i < 6; i++){
    queue.Enqueue(null);
}

これでキューには6個のnullが入りました。各弾にアタッチした当たり判定のスクリプトで、OnTriggerEnter()の先頭に以下のようにコードを入れます。

private void OnTriggerEnter(Collider coll) {
    if (queue.Contains(coll.gameObject)) { return; }
    queue.Enqueue(coll.gameObject);
    queue.Dequeue();

queue.Contains(coll.gameObject)はqueueにゲームオブジェクトcoll.gameObjectが含まれていればtrueを返します。キューには常に6個のデータが入っているようにしています。このコードは要するに当たったゲームオブジェクトを追加し、古いものは1つずつ捨てています。すでにキューに入っているゲームオブジェクトは追加せず、無視します。

 これで不具合はピタッと収まりました。なお、弾はたくさんあるのですから、当たったゲームオブジェクトはどこかで一括して管理する必要があります。だからキューは各弾にアタッチしているスクリプトで定義してもうまくいきません。メインのスクリプトで定義するのです。また、各弾のスクリプトには

using static NewBehaviourScript;

を入れ、メインのスクリプトにアクセスできるようにしておく必要があります。念のため注意しておきます。