うにてぃブログ

UnityやUnreal Engineの記事を書いていきます

【Unity】子供のオブジェクトを整列させるコンポーネント

整列させたいけど、スクリプト書くのが面倒なときにご利用ください

using UnityEngine;
using UnityEngine.UI;
 
#if UNITY_EDITOR
using UnityEditor;
#endif
 
/// <summary>
/// GameObjectを整列させるためのツール
/// </summary>
public class ChildObjectAlignment : MonoBehaviour
{
    private enum Mode
    {
        XY,
        XZ,
    }
 
    [SerializeField]
    private bool _applyOnAwake;
     
    [SerializeField]
    private Vector2 _spacing;
 
    /// <summary>
    /// 開始位置
    /// </summary>
    [SerializeField]
    private GridLayoutGroup.Corner _startCorner;
    [SerializeField]
    private GridLayoutGroup.Axis _startAxis;
    [SerializeField]
    private Mode _target;
     
    /// <summary>
    /// 固定するか
    /// </summary>
    [SerializeField]
    private bool _isFixedCount;
    [SerializeField]
    private int _fixedCount;
     
    private void Awake()
    {
        if (_applyOnAwake)
            DoAlignment();
    }
 
    /// <summary>
    /// 整列開始
    /// </summary>
    public void DoAlignment()
    {
        var childCount = transform.childCount;
        if (childCount <= 0)
            return;
 
        // 縦横に並べる数を確保
        var count = new Vector2Int(1, 1);
        var index = _startAxis == GridLayoutGroup.Axis.Horizontal ? 0 : 1;
        var index2 = (index + 1) % 2;
        count[index] = childCount;
 
        // 個数上限
        if (_isFixedCount)
        {
            count[index] = _fixedCount;
            count[index2] = Mathf.CeilToInt(childCount / (float) _fixedCount);
        }
 
        var size = new Vector2(
            (count.x - 1) * _spacing.x,
            (count.y - 1) * _spacing.y
        );
 
        var startPosition = new Vector2(size.x / 2f, size.y / 2f);
        if (_startCorner == GridLayoutGroup.Corner.UpperLeft || _startCorner == GridLayoutGroup.Corner.LowerLeft)
            startPosition.x *= -1;
        if (_startCorner == GridLayoutGroup.Corner.LowerLeft || _startCorner == GridLayoutGroup.Corner.LowerRight)
            startPosition.y *= -1;
         
        // 座標毎のDiff
        var diff = new Vector2(_spacing.x, _spacing.y);
        if (_startCorner == GridLayoutGroup.Corner.UpperRight || _startCorner == GridLayoutGroup.Corner.LowerRight)
            diff.x *= -1;
        if (_startCorner == GridLayoutGroup.Corner.UpperRight || _startCorner == GridLayoutGroup.Corner.UpperLeft)
            diff.y *= -1;
 
        for (var i = 0; i < childCount; i++)
        {
            var child = transform.GetChild(i);
            var position = new Vector2Int(0, 0)
            {
                [index] = i % count[index],
                [index2] = Mathf.FloorToInt(i / (float)count[index])
            };
            var localPosition = startPosition + position * diff;
            switch (_target)
            {
                case Mode.XY:
                    child.localPosition = localPosition;
                    break;
                case Mode.XZ:
                    child.localPosition = new Vector3(localPosition.x, 0f, localPosition.y);
                    break;
            }
        }
    }
}

#if UNITY_EDITOR
 
[CustomEditor(typeof(ChildObjectAlignment))]
public class ChildObjectAlignmentEditor : Editor
{
    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();
         
        if (GUILayout.Button("Apply"))
        {
            var t =  target as ChildObjectAlignment;
            t.DoAlignment();
        }
    }
}
 
#endif