Unityでカーリングゲームを作る #4 オブジェクトを集めて距離によってソートする

投稿者: | 2019-12-25

円(ハウス)の中にあるオブジェクトの情報を、円の中心からの距離が近い順に表示します。

空のゲームオブジェクトを新しく作って、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です。


他のストーンも正しく整列されているようです。

コメントを残す

メールアドレスが公開されることはありません。