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} 个子物体。");
    }
}
相关推荐
林川~0110 小时前
Unity新输入系统使用笔记
unity·input system
小贺儿开发17 小时前
Unity 结合百度AI开放平台 手写文字识别
人工智能·科技·学习·unity·云服务·文字识别·手写文字
goujunwe21 小时前
GEO 评估方法:如何自测你的内容,会不会被 AI 引用
搜索引擎·网络安全·游戏引擎·全文检索·中文分词
林川~011 天前
Unity 万能物理检测工具:射线检测 / 范围检测 / 层级过滤 / 编辑器可视化(可直接拿去用)
游戏·unity·c#·射线检测·通用工具
yi碗汤园2 天前
MQTT消息队列遥测传输协议
网络·网络协议·unity
林川~012 天前
Unity 常用 API 与核心类全解:从生命周期到协程,附性能避坑清单(Unity 6 适配)
unity·游戏引擎·常用api
sensen_kiss2 天前
CPT306 Coursework 1 个人游戏作业——坦克大战
unity·游戏程序·游戏策划
蒸鱼Yuzheng2 天前
Unity 与 Unreal 测试工具怎么回答:Profiler、日志、自动化与证据链
测试工具·unity·性能分析·游戏测试·unrealengine
野区捕龙为宠2 天前
Unity WebGL平台 通过 URL 加载图片(UGUI RawImage / 模型材质)
unity·团结引擎
阿松爱学习3 天前
【Unity开发】FileStream 详解及用法指南
unity·c#·unity开发