Unity3D编辑器初始化场景物体

工欲善其事必先利其器 ,熟练的使用工具,会使得一些重复的事情变得很容易。

unity编辑器给我们提供了一些交互接口,可以用于助力游戏开发的效率。

需求:需要在创建一个64*64阵列的球体,并且球体两两之间的间隔相同,如果手动右键创建再拖动,这是一件非常费时费力的事情,所以我们通过Unity提供的编辑器功能实现,实现的代码如下,代码也用AI添加了注释,比较详细:

csharp 复制代码
using System.Collections; // 引入集合迭代相关命名空间,用于支持协程等迭代操作
using System.Collections.Generic; // 引入泛型集合命名空间,提供List<T>等泛型容器
using UnityEditor; // 引入Unity编辑器命名空间,提供EditorWindow、MenuItem等编辑器特有功能
using UnityEngine; // 引入Unity引擎核心命名空间,提供GameObject、Transform、Vector3等基础类

// Start is called before the first frame update
// 这是MonoBehaviour的默认注释模板,但此类继承自EditorWindow,该注释不适用,建议删除
public class copyModelProperty : EditorWindow // 定义公共类,继承自EditorWindow,使其成为编辑器窗口
{
    public GameObject targetObject; // 公共字段,用于存储用户在编辑器窗口中选择的目标游戏对象(作为父物体容器)

    // MenuItem特性将该方法添加到Unity顶部菜单栏,菜单路径为"Tools/initSphere"
    [MenuItem("Tools/initSphere")]
    public static void ShowWindow() // 静态方法,用于显示或创建该编辑器窗口的实例
    {
        // 获取或创建copyModelProperty类型的窗口,并设置窗口标题为"Init Sphere Position"
        GetWindow<copyModelProperty>("Init Sphere Position");
    }

    // OnGUI方法在编辑器窗口每次重绘或刷新时调用,用于绘制窗口中的所有UI控件
    private void OnGUI()
    {
        // 在窗口顶部绘制一个粗体标签,显示"Init Sphere Array"作为标题
        GUILayout.Label("Init Sphere Array", EditorStyles.boldLabel);

        // 创建一个对象选择字段,允许用户在编辑器中通过拖拽或选择按钮来指定GameObject
        // 参数依次为:标签文本、当前值、允许的对象类型、是否允许选择场景中的对象(true表示允许)
        targetObject = (GameObject)EditorGUILayout.ObjectField("Target Model", targetObject, typeof(GameObject), true);
       
        // 创建一个按钮,显示文本"run"
        if (GUILayout.Button("run"))
        {
            // 当按钮被点击时,检查目标对象是否已被赋值
            if (targetObject != null)
            {
                run(); // 如果已赋值,调用核心执行方法
            }
            else
            {
                // 如果未赋值,在控制台输出错误日志,提示用户必须指定目标对象
                Debug.LogError("Target Model objects must be assigned.");
            }
        }
    }

    // 私有方法,包含该工具的核心功能逻辑
    private void run()
    {
        // 获取targetObject及其所有子物体的Transform组件(包括targetObject自身的Transform)
        var childs = targetObject.GetComponentsInChildren<Transform>();
        // 创建一个GameObject列表,用于存储需要删除的子物体
        List<GameObject> toDestroy = new List<GameObject>();
        // 遍历所有子物体的Transform组件
        foreach (Transform child in childs)
        {
            // 判断当前Transform是否不是targetObject自身的Transform(即只处理子物体,排除父物体自身)
            if(child != targetObject.transform)
            {
                toDestroy.Add(child.gameObject); // 将符合条件的子物体添加到待删除列表
            }
        }
        // 遍历待删除列表中的所有GameObject
        foreach (GameObject child in toDestroy)
        {
            DestroyImmediate(child); // 立即销毁该物体(编辑器模式下必须使用DestroyImmediate而非Destroy)
        }

        // 双层循环,生成64x64=4096个球体,在XZ平面上排列成网格
        for (int i = 0; i < 64; ++i) // 外层循环,控制X轴方向的位置(行索引,范围0-63)
        {
            for (int j = 0; j < 64; ++j) // 内层循环,控制Z轴方向的位置(列索引,范围0-63)
            {
                var sphere = initSphere(); // 调用方法实例化一个新的球体
                if (sphere == null) continue; // 如果实例化失败(预制体未找到),跳过本次循环,继续生成下一个
                string name = "sphere + " + i + "_" + j; // 生成带有行列索引的唯一名称
                sphere.name = name; // 设置球体的名称,便于在Hierarchy面板中识别
                Vector3 pos = new Vector3(i * 3, 1, j * 3 ); // 计算位置:X和Z轴间隔3单位,Y轴固定为1
                sphere.transform.position = pos; // 设置球体的世界坐标位置
                sphere.transform.SetParent(targetObject.transform); // 将球体设置为targetObject的子物体,保持层级结构
            }
        }
        Debug.Log("done!"); // 在控制台输出完成信息,提示所有球体已生成完毕
    }

    // 私有方法,用于初始化并实例化一个球体
    GameObject initSphere()
    {
        // 从Resources文件夹中加载球体预制体,路径为"prefab/sphere"
        GameObject prefab = Resources.Load<GameObject>("prefab/sphere");
        // 检查预制体是否加载成功
        if (prefab == null)
        {
            // 如果加载失败,在控制台输出错误日志,提示用户检查路径
            Debug.LogError("未找到预制体,请检查路径:prefab/sphere");
            return null; // 返回null,调用方会进行判空处理
        }
        // 实例化加载的预制体,在场景中创建实际的游戏对象并返回
        return Instantiate(prefab);
    }
}

创建的实机示意图如下所示:

我们需要在项目位置创建一个叫做Editor的文件夹,并且将上述脚本放在这个文件夹下面,就可以在编辑器的Tool目录下,找到initSphere菜单了

还有一些比较常见的使用场景,比如我遇到过一种情况,美术给过来一个场景,里面有很多Object的材质球需要替换,此时如果人工一个一个的去找,这是一件事倍功半的事情,此时使用编辑器功能就非常轻松了。

相关推荐
weixin_469273811 天前
.md文件是什么?.md如何打开?
前端·编辑器
W_chuanqi1 天前
VSCode + Claude Code + DeepSeek:打造 AI 编程神器
ide·vscode·编辑器·ai编程
凌云拓界2 天前
NodeVerdict | .ndv 二进制格式:为 WASM 解码器设计的紧凑布局
安全·架构·开源·node.js·编辑器·软件工程·wasm
New_Horizons6662 天前
远程linux终端中支持vscode的code命令
ide·vscode·编辑器
ElasticPDF-新国产PDF编辑器2 天前
【最新·免费PDF编辑器·不限终端·最高性价比SDK】高亮、评论、图章、手绘与签名,一套完成 PDF 批注
pdf·编辑器
AttackingLin3 天前
VScode Claude Code 插件配置第三方api
ide·vscode·编辑器
码农爱学习3 天前
VSCode的Markdown插件哪个好用
ide·vscode·编辑器
FeelTouch Labs4 天前
Node.js 中的 spawn 和 exec:子进程执行的不同方式对比
node.js·编辑器·vue·vim
啦啦啦!5 天前
虚拟机如何接入Codex并配置中转站(vscode插件版)
linux·vscode·ubuntu·编辑器