부트캠프

GameObject 배치하기

noyyo 2023. 8. 9. 23:03

게임을 만들다보면 GameObject를 인스턴스화 해서 PlaceHolder등에 자식으로 여러개 생성할 때가 있다.

이럴 때 UI의 경우는 GridLayoutGroup이라는 좋은 컴포넌트가 있지만 월드 스페이스에서는 축이 3개라는 문제점으로 인해 비슷한 기능을 하는 컴포넌트가 존재하지 않는다.

그래서 GameObject를 만들어서 배치하는 스크립트를 하나 작성해보았다.

using JetBrains.Annotations;
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.UI;
using static UnityEditor.PlayerSettings;

public class Collocator : MonoBehaviour
{
    [Tooltip("생성된 프리팹의 부모를 설정합니다. 없으면 씬에 직접 생성됩니다.")]
    public GameObject parent;
    [Tooltip("배치할 프리팹 객체입니다.\n게임 실행시 해당 객체가 자동으로 생성됩니다.")]
    public GameObject Prefab;
    [System.Serializable]
    public struct PlacingBox
    {
        public Vector3 UpperLeft;
        public Vector3 UpperRight;
        public Vector3 LowerLeft;
        public Vector3 LowerRight;
    }
    [System.Serializable]
    public struct CellSize
    {
        public float x;
        public float y;
    }
    [System.Serializable]
    public struct Padding
    {
        public float up;
        public float down;
        public float left;
        public float right;
    }
    [System.Serializable]

    public struct Spacing
    {
        public float x;
        public float y;
    }
    public enum StartCorner
    {
        UpperLeft = 0,
        UpperRight,
        LowerLeft,
        LowerRight,
    }
    public enum StartAxis
    {
        Horizontal = 0,
        Vertical,
    }

    public PlacingBox placingBox;
    public CellSize cellSize;
    public Padding padding;
    public Spacing spacing;
    public StartCorner startCorner;
    public StartAxis startAxis;
    public int amountToMake = 1;

    private int columnCount;
    private int rowCount;
    private int axisXDirection;
    private int axisYDirection;

    private Vector3 upperLeft;
    private Vector3 upperRight;
    private Vector3 lowerLeft;
    private Vector3 lowerRight;

    private Vector3 startPosition;

    void Awake()
    {
        Init();
    }
    public void Init()
    {
        upperLeft = new Vector3(placingBox.UpperLeft.x + padding.left, placingBox.UpperLeft.y - padding.up, 0);
        upperRight = new Vector3(placingBox.UpperRight.x - padding.right, placingBox.UpperRight.y - padding.down, 0);
        lowerLeft = new Vector3(placingBox.LowerLeft.x + padding.left, placingBox.LowerLeft.y + padding.up, 0);
        lowerRight = new Vector3(placingBox.LowerRight.x - padding.right, placingBox.LowerRight.y + padding.down, 0);
        columnCount = (int)((upperRight.x - upperLeft.x) / (cellSize.x + spacing.x));
        if ((upperRight.x - upperLeft.x) % (cellSize.x + spacing.x) >= cellSize.x)
            columnCount++;
        rowCount = (int)((upperLeft.y - lowerLeft.y) / (cellSize.y + spacing.y));
        if (((upperLeft.y - lowerLeft.y) % (cellSize.y + spacing.y)) >= cellSize.y)
            rowCount++;

        if (columnCount <= 0 || rowCount <= 0)
        {
            Debug.Log("위치할 박스 영역을 올바르게 지정해주세요");
            return;
        }

        switch (startCorner)
        {
            case StartCorner.UpperLeft:
                {
                    startPosition = upperLeft;
                    axisXDirection = 1;
                    axisYDirection = -1;
                    break;
                }
            case StartCorner.UpperRight:
                {
                    startPosition = upperRight;
                    axisXDirection = -1;
                    axisYDirection = -1;
                    break;
                }
            case StartCorner.LowerLeft:
                {
                    startPosition = lowerLeft;
                    axisXDirection = 1;
                    axisYDirection = 1;
                    break;
                }
            case StartCorner.LowerRight:
                {
                    startPosition = lowerRight;
                    axisXDirection = -1;
                    axisYDirection = 1;
                    break;
                }
        }
    }
    public List<GameObject> InstantiatePrefab()
    {
        List<GameObject> prefabList = new List<GameObject>();
        for (int i = 0; i < amountToMake; ++i)
        {
            GameObject prefab = Instantiate(Prefab);

            int rowNum = i / columnCount;
            int columnNum = i % columnCount;
            if (rowNum >= rowCount || columnNum >= columnCount)
            {
                Debug.Log("생성할 수 있는 영역을 넘어섰습니다.");
                Debug.Log("행 번호 : " + rowNum);
                Debug.Log("열 번호 : " + columnNum);
                return null;
            }
            if (startAxis == StartAxis.Vertical)
            {
                int temp = rowNum;
                columnNum = rowNum;
                rowNum = temp;
            }

            float x = startPosition.x + (axisXDirection * (spacing.x + cellSize.x) * columnNum) + (axisXDirection * (cellSize.x / 2f));
            float y = startPosition.y + (axisYDirection * (spacing.y + cellSize.y) * rowNum) + (axisYDirection * (cellSize.y / 2f));
            prefab.transform.position = new Vector3(x, y, 0);

            if (parent != null)
            {
                prefab.transform.parent = parent.transform;
            }

            prefabList.Add(prefab);
        }
        return prefabList;
    }
}

아무 객체나 (보통 GameManager 등의 객체에 많이 추가하게 될 것 같다)컴포넌트를 추가하고 인스펙터 창에서 생성할 프리팹과 영역을 지정해주면 padding과 spacing을 해서 인스턴스화 시켜주는 스크립트다.

2D에서 사용할 것만 고려해서 3D에서 사용하긴 어려울 것 같다. 그래도 매번 트랜스폼 포지션을 계산하느라 머리 싸매지 않아도 되니 훨씬 나은 것 같다.