Unity 3D GridLayoutGroup3D 让子物体对齐,调整子物体间距
效果
介绍
GridLayoutGroup3D
脚本是一个用于在 Unity 3D 编辑器中创建 3D 网格布局的实用工具。主要用于在 Unity 编辑器中提供一种可视化的方式来设置和调整子物体的位置,同时支持删除脚本时将物体恢复原位。
源码:
javascript
#if UNITY_EDITOR
using UnityEditor.SceneManagement;
#endif
using System.Collections.Generic;
using UnityEngine;
public class GridLayoutGroup3D : MonoBehaviour
{
#if UNITY_EDITOR
public Vector3 matrixInterval = new Vector3(0.25f, 0.25f, 0.25f);
public Vector3Int matrixSize = new Vector3Int(3, 3, 3);
public Color matrixColor = new Color(255 / 225f, 225 / 225f, 0, 100 / 255f);
public float radius = 0.1f;
private int ChildIndex = 0;
public List<Location> locations = new List<Location>();
//子物体间隔
public Vector3 MatrixInterval
{
get => matrixInterval; set
{
matrixInterval = value;
UnityEditor.EditorApplication.QueuePlayerLoopUpdate();
}
}
//矩阵大小
public Vector3Int MatrixSize
{
get => matrixSize; set
{
matrixSize = value;
UnityEditor.EditorApplication.QueuePlayerLoopUpdate();
}
}
public float Radius
{
get => radius; set
{
radius = value;
UnityEditor.EditorApplication.QueuePlayerLoopUpdate();
}
}
public Color MatrixColor
{
get => matrixColor; set
{
matrixColor = value;
UnityEditor.EditorApplication.QueuePlayerLoopUpdate();
}
}
private void OnDrawGizmosSelected()
{
DrawAndSetLocation();
}
public void DrawAndSetLocation()
{
ChildIndex = 0;
int MatrixSizeX = Mathf.Abs(MatrixSize.x);
int MatrixSizeY = Mathf.Abs(MatrixSize.y);
int MatrixSizeZ = Mathf.Abs(MatrixSize.z);
//刷新子物体位置
for (int j = 0; j < MatrixSizeY; j++)
{
for (int z = 0; z < MatrixSizeZ; z++)
{
for (int i = 0; i < MatrixSizeX; i++)
{
Vector3 CurLoc = new Vector3(
MatrixSize.x > 0 ? i : -i,
MatrixSize.y > 0 ? j : -j,
MatrixSize.z > 0 ? -z : z
);
Vector3 Location = new Vector3(
CurLoc.x * MatrixInterval.x,
CurLoc.y * MatrixInterval.y,
CurLoc.z * MatrixInterval.z
);
if (ChildIndex < transform.childCount)
{
if (transform.GetChild(ChildIndex).localPosition != Location)
{
transform.GetChild(ChildIndex).localPosition = Location;
EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
}
}
Gizmos.color = MatrixColor;
Gizmos.matrix = transform.localToWorldMatrix;
Gizmos.DrawCube(Location, Radius * Vector3.one);
ChildIndex++;
}
}
}
}
public void CopyInfo()
{
if (locations.Count == 0)
{
foreach (var item in GetComponentsInChildren<Transform>())
{
locations.Add(new Location(item.name, item.position, item.rotation));
}
}
}
public string PasteInfo()
{
Transform[] transforms = GetComponentsInChildren<Transform>();
if (transforms.Length != locations.Count)
{
return "子物体数量不对等!停止赋值!";
}
for (int i = 0; i < transforms.Length; i++)
{
if (transforms[i].name == locations[i].name)
{
transforms[i].position = locations[i].position;
transforms[i].rotation = locations[i].rotation;
}
else
{
return "子物体" + locations[i].name + "有变动!停止赋值!";
}
}
return "赋值成功!";
}
public class Location
{
public string name;
public Vector3 position;
public Quaternion rotation;
public Location(string name, Vector3 position, Quaternion rotation)
{
this.name = name;
this.position = position;
this.rotation = rotation;
}
}
#endif
}
编辑器扩展(美化面板)
记得放到Editor文件夹下
javascript
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(GridLayoutGroup3D)), CanEditMultipleObjects]
public class Editor_GridLayoutGroup3D : Editor
{
private GridLayoutGroup3D gridLayoutGroup3D;
// Start is called before the first frame update
private void OnEnable()
{
gridLayoutGroup3D = target as GridLayoutGroup3D;
gridLayoutGroup3D.CopyInfo();
}
public override void OnInspectorGUI()
{
gridLayoutGroup3D.MatrixInterval = EditorGUILayout.Vector3Field("子物体间隔", gridLayoutGroup3D.MatrixInterval);
GUILayout.Space(10);
gridLayoutGroup3D.MatrixSize = EditorGUILayout.Vector3IntField("矩阵大小", gridLayoutGroup3D.MatrixSize);
GUILayout.Space(10);
GUILayout.BeginHorizontal();
GUILayout.Label("矩阵显示调节:");
gridLayoutGroup3D.MatrixColor = EditorGUILayout.ColorField(gridLayoutGroup3D.MatrixColor);
GUILayout.Space(10);
float MaxValue = Mathf.Max(new float[3] { gridLayoutGroup3D.MatrixInterval.x, gridLayoutGroup3D.MatrixInterval.y, gridLayoutGroup3D.MatrixInterval.z });
gridLayoutGroup3D.Radius = EditorGUILayout.Slider(gridLayoutGroup3D.Radius,0.01f, MaxValue/2);
GUILayout.EndHorizontal();
if (gridLayoutGroup3D.MatrixSize.y==0|| gridLayoutGroup3D.MatrixSize.x==0|| gridLayoutGroup3D.MatrixSize.z==0)
{
EditorGUILayout.HelpBox("矩阵大小,最小值为1", MessageType.Info);
}
GUILayout.Space(10);
if (GUILayout.Button("移除并恢复"))
{
if (EditorUtility.DisplayDialog("警告!", "这将使当前的子物体恢复到开始状态。", "是的", "手滑了~"))
{
Debug.Log(gridLayoutGroup3D.PasteInfo());
DestroyImmediate(gridLayoutGroup3D);
}
}
}
}
代码解释
1. 基本信息
javascript
// 使用 UNITY_EDITOR 指令确保这些代码仅在编辑器模式下运行
#if UNITY_EDITOR
using UnityEditor.SceneManagement;
#endif
using System.Collections.Generic;
using UnityEngine;
public class GridLayoutGroup3D : MonoBehaviour
{
// ...(省略其他代码)
}
- 在这里,我们通过 #if UNITY_EDITOR 确保包含的代码仅在 Unity 编辑器中运行。
2. 字段和属性
javascript
public Vector3 matrixInterval = new Vector3(0.25f, 0.25f, 0.25f);
public Vector3Int matrixSize = new Vector3Int(3, 3, 3);
public Color matrixColor = new Color(255 / 225f, 225 / 225f, 0, 100 / 255f);
public bool isDrawGizmos = true;
public float radius = 0.1f;
private int ChildIndex = 0;
public List<Location> locations = new List<Location>();
这些字段和属性用于在 Unity 编辑器中设置参数。
- matrixInterval 是一个三维向量,表示子物体之间的间隔。
- matrixSize 是一个三维整数向量,表示矩阵的大小。
- matrixColor 是一个颜色,表示绘制 Gizmos 的颜色。
- radius 表示 Gizmos 的大小。
- locations 是一个存储子物体位置信息的列表。
3. 方法
javascript
private void OnDrawGizmosSelected()
{
DrawAndSetLocation();
}
public void DrawAndSetLocation()
{
ChildIndex = 0;
int MatrixSizeX = Mathf.Abs(MatrixSize.x);
int MatrixSizeY = Mathf.Abs(MatrixSize.y);
int MatrixSizeZ = Mathf.Abs(MatrixSize.z);
//刷新子物体位置
for (int j = 0; j < MatrixSizeY; j++)
{
for (int z = 0; z < MatrixSizeZ; z++)
{
for (int i = 0; i < MatrixSizeX; i++)
{
Vector3 CurLoc = new Vector3(
MatrixSize.x > 0 ? i : -i,
MatrixSize.y > 0 ? j : -j,
MatrixSize.z > 0 ? -z : z
);
Vector3 Location = new Vector3(
CurLoc.x * MatrixInterval.x,
CurLoc.y * MatrixInterval.y,
CurLoc.z * MatrixInterval.z
);
if (ChildIndex < transform.childCount)
{
if (transform.GetChild(ChildIndex).localPosition != Location)
{
transform.GetChild(ChildIndex).localPosition = Location;
EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
}
}
Gizmos.color = MatrixColor;
Gizmos.matrix = transform.localToWorldMatrix;
Gizmos.DrawCube(Location, Radius * Vector3.one);
ChildIndex++;
}
}
}
}
- OnDrawGizmosSelected 是 Unity 编辑器中的一个回调方法,用于在选择物体时绘制 Gizmos。
- DrawAndSetLocation 方法用于刷新子物体的位置并在 Unity 编辑器中绘制 Gizmos。它通过循环遍历矩阵的每个位置,将子物体放置在相应的位置。
4. 位置信息容器
javascript
public class Location
{
public string name;
public Vector3 position;
public Quaternion rotation;
public Location(string name, Vector3 position, Quaternion rotation)
{
this.name = name;
this.position = position;
this.rotation = rotation;
}
}