【Unity】重ならないようにランダムにオブジェクトを配置する

投稿者: | 2021-06-21

スクリプトでアイテムが重ならないようにランダムに配置してみました。

ランダムの位置を設定

Random.insideUnitCircleメソッドは、半径1の円の内部のランダムな位置を返します。その位置に値をかけると半径を伸縮できます。

戻り値の型はVector2なので、これをVector3の変数に入れてY軸とZ軸を入れ替えることで、床に沿った位置にしています。

// ランダムの位置
Vector3 pos = Random.insideUnitCircle * 5;
pos.z = pos.y;
pos.y = 0.5f;

アイテムをインスタンス化

この位置を使ってアイテムをインスタンス化すると、互いに重なることがあります。

これを回避するために、Physics.CheckBoxメソッドを使って、アイテムを配置したいところにボックスを作り、それが他のアイテムと重ならないときだけアイテムをインスタンス化しました。

重なりを確認

Physics.CheckBoxメソッドの第1引数にはボックスの中心、第2引数にはボックスサイズの半分を渡します。

このボックスが他のコライダーと重ならないときにtrueを返します。

// ボックスサイズの半分
Vector3 halfExtents = new Vector3(0.5f, 0.5f, 0.5f);

// ---
// 10回試す
for (int n = 0; n < 10; n++)
{
    // ランダムの位置
    Vector3 pos = Random.insideUnitCircle * 5;
    pos.z = pos.y;
    pos.y = 0.5f;

    // ボックスとアイテムが重ならないとき
    if (!Physics.CheckBox(pos, halfExtents, Quaternion.identity, 1 << 12))
    {
        // アイテムをインスタンス化
        items.Add(Instantiate(item, pos, Quaternion.identity));
        break;
    }
}

他のアイテムと重なるときは位置を変えて再度ボックスを作っています。これを10回ずつ試すことにしました。

ボックスが床を無視してほしいので、アイテムのプレハブにレイヤーを設定して、第4引数にレイヤーマスクを入れています。

作られるアイテムの数に偏りができましたが、アイテム同士が重ならなくなりました。

using System.Collections.Generic;
using UnityEngine;

public class PlaceItemsTest : MonoBehaviour
{
    // アイテムのプレハブ
    [SerializeField] GameObject item;

    // アイテムを作る数 
    [SerializeField] int itemCount = 30;
    
    // アイテムのインスタンス
    List<GameObject> items = new List<GameObject>();

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        // 左クリック  
        if (Input.GetMouseButtonDown(0))
        {
            // アイテムを全削除
            foreach(var i in items)
            {
                Destroy(i);
            }
            items.Clear();

            // ボックスサイズの半分
            Vector3 halfExtents = new Vector3(0.5f, 0.5f, 0.5f);

            // アイテムを作る
            for (int i = 0; i < itemCount; i++)
            {               
                // 10回試す
                for (int n = 0; n < 10; n++)
                {
                    // ランダムの位置
                    Vector3 pos = Random.insideUnitCircle * 5;
                    pos.z = pos.y;
                    pos.y = 0.5f;

                    // ボックスとアイテムが重ならないとき
                    if (!Physics.CheckBox(pos, halfExtents, Quaternion.identity, 1 << 12))
                    {
                        // アイテムをインスタンス化
                        items.Add(Instantiate(item, pos, Quaternion.identity));
                        break;
                    }
                }

            }
        }
    }
}

コメントを残す

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