円(ハウス)の中にあるオブジェクトの情報を、円の中心からの距離が近い順に表示します。
空のゲームオブジェクトを新しく作って、Sphere Colliderを付けます。
Is Triggerにチェックを入れます。
非アクティブにして、新しいスクリプトを付けます。
とりあえず、テストのために適当なキーを押したときに、他のスクリプトからこのゲームオブジェクトをアクティブにします。
if (Input.GetKeyDown(KeyCode.Space))
{
house.SetActive(!house.activeSelf);
}
このゲームオブジェクトを使って、スコアを出すためにハウスの中にあるストーンを調べられるようにします。
ストーンのインスタンスを特定するために、ストーンを出現させたときに固有の番号を与えるようにします。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class StoneScript : MonoBehaviour
{
public int stoneState = 0;
public bool player = true;
Rigidbody rb;
public static int num = 0;
public int stoneNumber;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody>();
stoneNumber = num; // 固有の番号を与える
num++;
}
// Update is called once per frame
void Update()
{
if (stoneState == 1 && rb.velocity != Vector3.zero)
{
stoneState = 2; // 投げられたあと動いている間は状態2
}
if (stoneState == 2 && rb.velocity == Vector3.zero)
{
stoneState = 3; // 動いた後、静止したら状態3
}
}
}
インスタンスが作られたら静的なint型の値をインスタンスのプロパティに代入してから1足します。次に作られるインスタンスには1足された値が使われます。これで、ストーンには0からの連番が割り振られます。
コライダーの中にあるのがストーンかどうかを判別するために、ストーンのプレハブにタグを設定しています。
インスペクタで、Add Tag…から「Stone」タグを新規作成して、Tagで選択します。
CompareTagメソッドの引数をこのタグ名にすると、このタグとオブジェクトに設定されたタグが一致する時、返る値がtrueになります。
Sphereコライダーの中にあるストーンの番号を表示させてみます。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HouseScript : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
private void OnTriggerStay(Collider other)
{
if (other.CompareTag("Stone"))
{
Debug.Log(other.gameObject.GetComponent<StoneScript>().stoneNumber); // コンソールに番号を表示
}
}
}
OnTriggerStayの処理は、コライダーの中にオブジェクトがあるときに実行され、そのオブジェクトのコライダーがCollider型の変数に代入されて全体の処理がループするようですが、オブジェクトが複数あるときは、オブジェクトをローテーションさせるようです。
上のスクリーンショットでは、ハウスの中にストーンが5個あり、0~4の番号が割り振られたオブジェクトのコライダーが順番に代入されながら処理がループしています。
ゲームオブジェクトが一巡したら処理が中断されるようにしたいので、番号をリストに入れていって、次に来た番号がリストの中に既にあれば処理を変えます。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
public class HouseScript : MonoBehaviour
{
bool secondLap = false;
List<int> stoneNumbers;
Dictionary<GameObject, float> stonesInHouse;
public Transform center;
// Start is called before the first frame update
void Start()
{
stoneNumbers = new List<int>();
stonesInHouse = new Dictionary<GameObject, float>();
}
// Update is called once per frame
void Update()
{
}
private void OnTriggerStay(Collider other)
{
// ストーンの番号が一巡していないとき
if (!secondLap && other.CompareTag("Stone"))
{
// ストーンの番号がリストにあるとき
if (stoneNumbers.Count != 0 && stoneNumbers.Contains(other.gameObject.GetComponent<StoneScript>().stoneNumber))
{
secondLap = true;
Score();
}
else {// ストーンの番号がリストに無いとき
Debug.Log(other.gameObject.GetComponent<StoneScript>().stoneNumber);
stoneNumbers.Add(other.gameObject.GetComponent<StoneScript>().stoneNumber); // 番号をリストに追加する
stonesInHouse.Add(other.gameObject, Vector3.Distance(other.gameObject.transform.position, center.position)); // オブジェクトと中心までの距離をディクショナリーに追加する
}
}
}
void Score()
{
var stoneDic = stonesInHouse.OrderBy((x) => x.Value); // ディクショナリーの値をソートする
foreach (KeyValuePair<GameObject, float> stone in stoneDic)
{
Debug.Log(stone.Key.transform.position.ToString() + " " + stone.Value.ToString() + " " + stone.Key.GetComponent<StoneScript>().stoneNumber);
}
}
}
リストのContainsメソッドでリストの中を検索できます。
if (stoneNumbers.Count != 0 && stoneNumbers.Contains(other.gameObject.GetComponent<StoneScript>().stoneNumber))
{
secondLap = true;
Score();
}
ストーンの番号がリストにあるときに、変数secondLapがtrueになります。ストーンに対する処理はsecondLapがfalseのときだけ行われるようにしているので、これ以降は処理がスルーされます。
これで各ストーンに対して一度ずつ処理が実行されるようになりました。
番号のリストとは別に、ディクショナリーにストーンのオブジェクトと、ハウスの中心からの距離のfloat値を追加しています。
ハウスの中心には新しく空のゲームオブジェクトを置きました。
2つのオブジェクトの距離はVector3.Distance(a, b)で取得できます。
ストーンが一巡したら、ディクショナリーを中心からの距離の小さい順に並び替えて、ストーンの位置、中心からの距離、ストーンの番号を表示しました。
ハウスの中心に近い順に、1、0、2 とストーンが整列されています。
ストーンを選択してインスペクタで、ストーンの番号を確認してみます。
中心に最も近いストーンの番号は1です。
他のストーンも正しく整列されているようです。