3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【Unity(C#)】ボタン1つで同一オブジェクトを大量に等間隔で配置する

Last updated at Posted at 2019-07-03

隙間なく配置

私はこれまでUnityで隙間なく配置していく際にはShift + Vで頂点を合わせてました。
しかし、何度も繰り返してたのでなんとか自動化できないかと思い試みました。

結果的にはこのような形です。
chain.gif

XYZそれぞれの方向に連ねて配置ができます。

一定の間隔ごとに配置

ぴったりくっつける必要はないが、何個も配置する必要のあるオブジェクトもあるかと思います。
そんなときのために等間隔で配置できる形も用意しました。
chain_2.gif

Bounds

Boundsを利用することで、Renderer及びColliderの3次元空間での範囲を取得することができます。
平たく言うとオブジェクトの大きさがわかるってことです。

先ほどの例で示した、
隙間なく配置する場合においてはRenderer
一定の間隔ごとに配置する場合においてはColliderを利用してオブジェクトの大きさを取得しています

特に後者のColliderを利用した場合なんかはおもしろくて、
椅子のColliderは画像のようになっています。
chair.PNG
コライダーを利用しているという意味が伝わったかと思います。

それぞれのコードを見ていきます。

Renderer

RendererのBoundsは下記のように取得します。

    Renderer objRenderer = chainObj.GetComponent<Renderer>();
    Bounds objBounds = objRenderer.bounds;

取得したBoundsを使う際は、objBounds.size.xのようにXYZ方向の大きさを個別に利用できます。
size直径が得られると考えてしまっていいと思います。

厳密な考え方は違いますのでしっかりと叩き込みたい方は公式リファレンスをご覧ください。

Collider

ColliderのBoundsは下記のように取得します。
取得の仕方も使い方もRendererと全く一緒です。

    Collider objCollider = chainObj.GetComponent<Collider>();
    Bounds objBounds = objCollider.bounds;

Mesh

実はMeshから取得する方法もあるのですが、
World座標でBoundsを返すので注意が必要です。

公式リファレンスより引用

This is the axis-aligned bounding box of the mesh in its local space (that is, not affected by the transform). Note that the Renderer.bounds property is similar but returns the bounds in world space.

コード

実際にコードに落とし込んだものがこちらです。

# if UNITY_EDITOR
using UnityEditor;
# endif  
using UnityEngine;
using System.Text.RegularExpressions;

public class CreateChainObj : MonoBehaviour
{
    [SerializeField]
    GameObject chainObj;

    [SerializeField]
    int chainNum = 0;

    [SerializeField]
    bool useX, useY, useZ;

    //指定された方向に連ねて配置する(Renderer)
    void DoneChain_UseMesh()
    {
        Renderer objRenderer = chainObj.GetComponent<Renderer>();
        Bounds objBounds = objRenderer.bounds;
        Vector3 objPos = chainObj.transform.position;

        for (int i = 0; i < chainNum; i++)
        {
            if (useX) objPos.x += objBounds.size.x;

            if (useY) objPos.y += objBounds.size.y;

            if (useZ) objPos.z += objBounds.size.z;

            GameObject tmpObj = Instantiate(chainObj, objPos, chainObj.transform.rotation);
            tmpObj.transform.parent = chainObj.transform.parent;
            string num_InObjName_String = Regex.Replace(chainObj.name, @"[^0-9]", "");

            if (num_InObjName_String != "")
            {
                int num_InObjName_Integer = int.Parse(num_InObjName_String);
                tmpObj.name = Regex.Replace(chainObj.name, @"[0-9]", "") + (i + 1 + num_InObjName_Integer);
            }
            else
            {
                tmpObj.name = chainObj.name + (i + 1);
            }
        }
    }

    //指定された方向に連ねて配置する(Collider)
    void DoneChain_UseBoxCollider()
    {
        Collider objCollider = chainObj.GetComponent<Collider>();
        Bounds objBounds = objCollider.bounds;
        Vector3 objPos = chainObj.transform.position;

        for (int i = 0; i < chainNum; i++)
        {
            if (useX) objPos.x += objBounds.size.x;

            if (useY) objPos.y += objBounds.size.y;

            if (useZ) objPos.z += objBounds.size.z;

            GameObject tmpObj = Instantiate(chainObj, objPos, chainObj.transform.rotation);
            tmpObj.transform.parent = chainObj.transform.parent;
            string num_InObjName_String = Regex.Replace(chainObj.name, @"[^0-9]", "");

            if (num_InObjName_String != "")
            {
                int num_InObjName_Integer = int.Parse(num_InObjName_String);
                tmpObj.name = Regex.Replace(chainObj.name, @"[0-9]", "") + (i + 1 + num_InObjName_Integer);
            }
            else
            {
                tmpObj.name = chainObj.name + (i + 1);
            }
        }
    }

# if UNITY_EDITOR
    [CustomEditor(typeof(CreateChainObj))]
    public class ExampleInspector : Editor
    {
        CreateChainObj rootClass;
        string explanation = 
            @"
                ・オブジェクトの名前の途中に数字を含めないでください。
                ・連ねる方向の指定にはワールド座標を利用します。
                ・Hierarchy上のオブジェクトをアタッチしてください。
                ・連結位置が気に入らない場合はColliderで調節することを推奨します。
                ・連ねたいオブジェクトには親オブジェクトを持たせてください。
                ";

        private void OnEnable()
        {
            rootClass = target as CreateChainObj;
        }

        public override void OnInspectorGUI()
        {
            

            EditorGUILayout.Space();

            EditorGUILayout.BeginVertical(GUI.skin.box);
            {
                EditorGUILayout.HelpBox(explanation, MessageType.Info);
            }

            EditorGUILayout.EndVertical();

            base.OnInspectorGUI();

            if (GUILayout.Button("連ねて配置(Rendererから位置を計測)"))
            {
                // 押下時に実行したい処理
                rootClass.DoneChain_UseMesh();
            }

            if (GUILayout.Button("連ねて配置(BoxColliderから位置を計測)"))
            {
                // 押下時に実行したい処理
                rootClass.DoneChain_UseBoxCollider();
            }

            serializedObject.Update();

            serializedObject.ApplyModifiedProperties();
        }
    }
# endif  
}

}

Regex.Replace

正直、作ってから別にいらないなこれってなったんですが、
せっかく覚えたので書いときます。

    string num_InObjName_String = Regex.Replace(chainObj.name, @"[^0-9]", "");
            
    if (num_InObjName_String != "")
    {
        int num_InObjName_Integer = int.Parse(num_InObjName_String);
        tmpObj.name = Regex.Replace(chainObj.name, @"[0-9]", "") + (i + 1 + num_InObjName_Integer);
    }
    else
    {
        tmpObj.name = chainObj.name + (i + 1);
    }

なにをやっているかというと、生成されたオブジェクトに連番をつけてリネームしています。
**Regex.Replace(指定した文字列, 置換したい文字, 置換後の文字);**という使い方です。

"指定した文字列内の置換機能"を利用して、一度0~9以外の文字を空文字にしてnum_InObjName_Stringにつっこんでます。
string num_InObjName_String = Regex.Replace(chainObj.name, @"[^0-9]", "");

あとは、空文字かどうか判定して連番を追加するだけです。

もっと他に用途がありそうな機能なので見つかったら、また まとめようと思います。


HelpBox

完全なる自己満足ですが、HelpBoxもつけてます。
ChainObj.PNG


小ネタ

string hoge = @"文字列";で改行できるらしい
たぶんみんな知っている...


リンク

・利用したアセット:昭和の椅子

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?