Unity拓展关于阵列物品生成以及物品替换

用于快速在某个物体下生成阵列,以及替换子物体。

csharp 复制代码
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;

public class CombinedTool : EditorWindow
{
    private enum Tab
    {
        Creator,    // 阵列生成器
        Replacer    // 替换子物体
    }

    [SerializeField] private Tab currentTab = Tab.Creator;

    // ---------- 阵列生成器参数 ----------
    [SerializeField] private GameObject creatorPrefab;
    [SerializeField] private int creatorRows = 5;
    [SerializeField] private int creatorColumns = 5;
    [SerializeField] private float creatorSpacing = 2f;
    [SerializeField] private Vector3 creatorOffset = Vector3.zero;
    [SerializeField] private Transform creatorParent;

    // ---------- 替换子物体参数 ----------
    [SerializeField] private GameObject replacerParentObject;
    [SerializeField] private GameObject replacerPrefab;

    // 窗口菜单项
    [MenuItem("Tools/物品阵列生成器")]
    public static void ShowCreator()
    {
        var window = GetWindow<CombinedTool>("组合工具");
        window.currentTab = Tab.Creator;
        window.Show();
    }

    [MenuItem("Tools/替换子物体为预制体")]
    public static void ShowReplacer()
    {
        var window = GetWindow<CombinedTool>("组合工具");
        window.currentTab = Tab.Replacer;
        window.Show();
    }

    private void OnEnable()
    {
        // 加载上次使用的标签页
        currentTab = (Tab)EditorPrefs.GetInt("CombinedTool_CurrentTab", (int)Tab.Creator);
    }

    private void OnDisable()
    {
        // 保存当前标签页
        EditorPrefs.SetInt("CombinedTool_CurrentTab", (int)currentTab);
    }

    private void OnGUI()
    {
        DrawTabs();

        EditorGUILayout.Space(10);

        switch (currentTab)
        {
            case Tab.Creator:
                DrawCreatorGUI();
                break;
            case Tab.Replacer:
                DrawReplacerGUI();
                break;
        }
    }

    private void DrawTabs()
    {
        EditorGUILayout.BeginHorizontal();
        {
            if (GUILayout.Toggle(currentTab == Tab.Creator, "阵列生成器", EditorStyles.toolbarButton))
                currentTab = Tab.Creator;
            if (GUILayout.Toggle(currentTab == Tab.Replacer, "替换子物体", EditorStyles.toolbarButton))
                currentTab = Tab.Replacer;
        }
        EditorGUILayout.EndHorizontal();
    }

    // ---------- 阵列生成器界面 ----------
    private void DrawCreatorGUI()
    {
        GUILayout.Label("阵列参数", EditorStyles.boldLabel);

        creatorPrefab = (GameObject)EditorGUILayout.ObjectField("物品预制体", creatorPrefab, typeof(GameObject), false);
        creatorRows = EditorGUILayout.IntField("行数", creatorRows);
        creatorColumns = EditorGUILayout.IntField("列数", creatorColumns);
        creatorSpacing = EditorGUILayout.FloatField("间距", creatorSpacing);
        creatorOffset = EditorGUILayout.Vector3Field("整体偏移", creatorOffset);
        creatorParent = (Transform)EditorGUILayout.ObjectField("父对象 (可选)", creatorParent, typeof(Transform), true);

        EditorGUILayout.Space();

        GUI.enabled = creatorPrefab != null;
        if (GUILayout.Button("生成阵列"))
        {
            GenerateGrid();
        }

        if (GUILayout.Button("清除所有生成的物品"))
        {
            ClearGenerated();
        }
        GUI.enabled = true;

        if (creatorParent != null)
        {
            EditorGUILayout.HelpBox($"当前父对象下现有 {creatorParent.childCount} 个子物体", MessageType.Info);
        }
    }

    // ---------- 替换子物体界面 ----------
    private void DrawReplacerGUI()
    {
        GUILayout.Label("设置", EditorStyles.boldLabel);
        replacerParentObject = (GameObject)EditorGUILayout.ObjectField("父物体", replacerParentObject, typeof(GameObject), true);
        replacerPrefab = (GameObject)EditorGUILayout.ObjectField("目标预制体", replacerPrefab, typeof(GameObject), false);

        GUI.enabled = replacerParentObject != null && replacerPrefab != null;
        if (GUILayout.Button("执行替换"))
        {
            Replace();
        }
        GUI.enabled = true;

        if (replacerParentObject != null)
        {
            EditorGUILayout.Space();
            EditorGUILayout.LabelField($"父物体下将替换 {replacerParentObject.transform.childCount} 个子物体。", EditorStyles.helpBox);
        }
    }

    // ---------- 阵列生成器核心逻辑 ----------
    private void GenerateGrid()
    {
        if (creatorPrefab == null)
        {
            Debug.LogError("请先指定物品预制体!");
            return;
        }

        Transform parent = creatorParent;
        if (parent == null)
        {
            GameObject newParent = new GameObject("GeneratedGrid");
            parent = newParent.transform;
            Undo.RegisterCreatedObjectUndo(newParent, "Create Grid Parent");
        }

        Undo.RegisterCompleteObjectUndo(parent, "Generate Grid");

        float startX = -(creatorColumns - 1) * creatorSpacing * 0.5f;
        float startZ = -(creatorRows - 1) * creatorSpacing * 0.5f;

        List<GameObject> createdObjects = new List<GameObject>();

        for (int i = 0; i < creatorRows; i++)
        {
            for (int j = 0; j < creatorColumns; j++)
            {
                float x = startX + j * creatorSpacing;
                float z = startZ + i * creatorSpacing;
                Vector3 localPos = new Vector3(x, 0, z) + creatorOffset;

                GameObject newObj;
                if (PrefabUtility.IsPartOfAnyPrefab(creatorPrefab))
                {
                    newObj = (GameObject)PrefabUtility.InstantiatePrefab(creatorPrefab);
                }
                else
                {
                    newObj = Instantiate(creatorPrefab);
                }

                Undo.RegisterCreatedObjectUndo(newObj, "Create Grid Item");

                Transform newTransform = newObj.transform;
                Undo.SetTransformParent(newTransform, parent, "Reparent Grid Item");
                newTransform.localPosition = localPos;
                newTransform.localRotation = Quaternion.identity;
                newTransform.localScale = Vector3.one;

                newObj.name = $"{creatorPrefab.name}_{i}_{j}";

                createdObjects.Add(newObj);
            }
        }

        Debug.Log($"生成完成:共 {creatorRows * creatorColumns} 个物品,父对象为 {parent.name}");
    }

    private void ClearGenerated()
    {
        if (creatorParent == null)
        {
            Debug.LogWarning("未指定父对象,无法清除。");
            return;
        }

        Undo.RegisterCompleteObjectUndo(creatorParent, "Clear Generated Items");

        List<GameObject> childrenToDelete = new List<GameObject>();
        foreach (Transform child in creatorParent)
        {
            childrenToDelete.Add(child.gameObject);
        }

        foreach (GameObject child in childrenToDelete)
        {
            Undo.DestroyObjectImmediate(child);
        }

        Debug.Log($"已清除 {childrenToDelete.Count} 个生成的物品");
    }

    // ---------- 替换子物体核心逻辑 ----------
    private void Replace()
    {
        if (replacerParentObject == null || replacerPrefab == null) return;

        Undo.RegisterFullObjectHierarchyUndo(replacerParentObject, "Replace Children with Prefab");

        Transform parent = replacerParentObject.transform;
        List<Transform> children = new List<Transform>();

        foreach (Transform child in parent)
        {
            children.Add(child);
        }

        foreach (Transform child in children)
        {
            Vector3 localPos = child.localPosition;
            Quaternion localRot = child.localRotation;
            Vector3 localScale = child.localScale;
            string oldName = child.name;

            Undo.DestroyObjectImmediate(child.gameObject);

            GameObject newObj = (GameObject)PrefabUtility.InstantiatePrefab(replacerPrefab);
            Undo.RegisterCreatedObjectUndo(newObj, "Create replacement object");

            Transform newTransform = newObj.transform;
            Undo.SetTransformParent(newTransform, parent, "Reparent replacement");
            newTransform.localPosition = localPos;
            newTransform.localRotation = localRot;
            newTransform.localScale = localScale;
            newObj.name = oldName;
        }

        Debug.Log($"替换完成,共处理 {children.Count} 个子物体。");
    }
}
相关推荐
电子云与长程纠缠2 小时前
Godot学习01 - HelloWorld
学习·游戏引擎·godot
mxwin2 小时前
Unity Shader · UV 技术 用 UV 坐标打造水波涟漪效果
unity·游戏引擎·shader·uv
野奔在山外的猫12 小时前
【解决】IndexOutOfRangeException: renderPassIndex
unity
weixin_4093831218 小时前
godot中文不显示,仅显示编码,是因为没设置字体,设置字体就好了
游戏引擎·godot
C蔡博士21 小时前
Unity像素游戏精灵导入最佳实践
游戏·unity·游戏引擎·2d·pixel arts
GLDbalala1 天前
Unity基于自定义管线实现贴花效果
unity·游戏引擎
上证50指数etf1 天前
unity面试总结(项目篇)
unity·游戏引擎
sensen_kiss1 天前
CPT306 Principles of Computer Games Design 电脑游戏设计原理 Pt.2 游戏引擎
学习·游戏引擎
zyh______2 天前
unity值属性修改步骤
unity·游戏引擎