编辑器场景视窗扩展

接上文编辑器菜单栏扩展测试

具体代码如下:

cs 复制代码
     //开窗口调用
    private void OnEnable()
    {
        Debug.Log("enable");
    }
    //关窗口调用
    private void OnDisable()
    {
        Debug.Log("disable");
    }
    //开窗口时每帧调用
    private void Update()
    {
        Debug.Log("update");
    }
    private void OnGUI()
    {
        if (GUILayout.Button("测试点击"))
        {
            Debug.Log("测试点击");
        }
    }
    //场景结构发生变化,执行回调函数
    private void OnHierarchyChange()
    {
        Debug.Log("hierarchy");
    }
    //项目结构发生变化,执行回调函数
    private void OnProjectChange()
    {
        Debug.Log("Project");
    }
    //被选中物体发生变化,执行回调函数
    private void OnSelectionChange()
    {
        //获取当前选中的物体的名称
        Debug.Log(Selection.activeGameObject.name);
    }
编辑器场景视窗扩展

实现在Plane上开始编辑后通过点击生成Cube并连线的功能:

相应的代码如下:

NodeManager脚本

cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[ExecuteInEditMode]//在编辑模式下可以执行一些生命周期函数
public class NodeManager : MonoBehaviour
{
    public List<GameObject> nodes;//存储了所有编辑器下点击生成的点,并使用预制体显示
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        for(int i = 0; i < nodes.Count - 1; i++)
        {
            Debug.DrawLine(nodes[i].transform.position,
                nodes[i+1].transform.position,
                Color.red,
                Time.deltaTime);
        }
    }
}

NodeManagerEditor脚本

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

public class NodeWindow : EditorWindow
{
    static NodeWindow window;
    static GameObject nodeManager;
    public static void OpenWindow(GameObject manager)
    {
        nodeManager= manager;
        //真正开启了一个窗口
        window = EditorWindow.GetWindow<NodeWindow>();
    }
    void Update()
    {
        //通过窗口的Update,每帧执行一次,当前选中的对象为Plane
        Selection.activeGameObject= nodeManager;
    }
    public static void CloseWindow()
    {
        window.Close();
    }
}
//外挂式关联NodeManager
[CustomEditor(typeof(NodeManager))]
public class NodeManagerEditor : Editor
{
    NodeManager manager;
    bool isEditor = false;//是否是编辑的状态
    //当选中带有NodeManager组件的对象时,获得组件
    void OnEnable()
    {
        manager = (NodeManager)target;
    }
    //绘制组件的生命周期函数
    public override void OnInspectorGUI()
    {
        //通过终极的数据获取方法,显示列表中的数据
        serializedObject.Update();
        SerializedProperty nodes = serializedObject.FindProperty("nodes");
        EditorGUILayout.PropertyField(nodes, new GUIContent("路径"), true);
        serializedObject.ApplyModifiedProperties();
        //开始编辑的开关
        if (!isEditor && GUILayout.Button("开始编辑"))
        {
            NodeWindow.OpenWindow(manager.gameObject);
            //调用打开界面的方法
            isEditor = true;//改变状态变成编辑模式
        }//结束编辑的开关
        else if (isEditor && GUILayout.Button("结束编辑"))
        {
            NodeWindow.CloseWindow();//调用关闭界面的方法
            isEditor = false;//改变状态变成非编辑模式
        }
        //删除按钮
        if (GUILayout.Button("删除最后一个节点"))
        {
            RemoveAtLast();
        }
        //删除所有按钮
        else if (GUILayout.Button("删除所有节点"))
        {
            RemoveAll();
        }

    }
    RaycastHit hit;
    //当选中关联的脚本挂载的物体
    //当鼠标在Scene视图下发生变化时,执行该方法,比如鼠标移动,比如鼠标的点击
    void OnSceneGUI()
    {
        if (!isEditor)
        {
            return;
        }
        //鼠标按下左键时发射一条射线
        //非运行时,使用Event类
        //Event.current.button 判断鼠标是哪个按键的(0:鼠标左键)
        //Event.current.type 判断鼠标的事件方式的
        if (Event.current.button == 0 && Event.current.type == EventType.MouseDown)
        {
            //Debug.Log("鼠标按下");
            //从鼠标的位置需要发射射线了
            //因为是从Scene视图下发射射线,跟场景中的摄像机并没有关系,所以不能使用相机发射射线的方法
            //从编辑器GUI中的一个点向世界定义一条射线,参数一般都是鼠标的坐标
            Ray ray = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);
            if(Physics.Raycast(ray, out hit,100))
            {
                //需要在检测的点实例化,路点
                InstancePathNode(hit.point + Vector3.up * 0.1f);
            }
        }
    }
    

    ///<summary>
    ///生成节点
    /// </summary>
    /// <param name="position"></param>
    void InstancePathNode(Vector3 position)
    {
        //点预制体
        GameObject prefab = Resources.Load<GameObject>("Cube");
        //点对象生成到Plane的子物体下
        GameObject cube = Instantiate<GameObject>(prefab, position, Quaternion.identity, manager.transform);
        manager.nodes.Add(cube); //把生成的路点添加到列表里
    }

    ///<summary>
    ///删除最后一个节点
    /// </summary>
    void RemoveAtLast()
    {

        //保证有节点才能删节点
        if(manager.nodes.Count > 0)
        {
            //从场景中删除游戏物体
            DestroyImmediate(manager.nodes[manager.nodes.Count-1]);
            //把该节点从列表中移除
            manager.nodes.RemoveAt(manager.nodes.Count-1);
        }
    }

    ///<summary>
    ///删除所有的节点
    /// </summary>
    void RemoveAll()
    {
        //遍历删除所有节点物体
        for(int i = 0; i < manager.nodes.Count; i++)
        {
            if (manager.nodes[i] != null)
            {
                DestroyImmediate(manager.nodes[i]);
            }
        }
        manager.nodes.Clear();//请空列表
    }
}

Plane检视器初始如图:

选中Plane游戏物体并点击开始编辑后(鼠标在Scene视图点击三次的效果)如图:

删除最后一个节点及删除所有节点的具体功能即字面意思。

该系列专栏为网课课程笔记,仅用于学习参考。

相关推荐
2501_9068014823 分钟前
BY组态-低代码web可视化组件
前端·物联网·低代码·数学建模·编辑器·web
周Echo周26 分钟前
5、vim编辑和shell编程【超详细】
java·linux·c++·后端·编辑器·vim
徐小夕@趣谈前端1 小时前
从零到一开发电子病历编辑器(源码+教程)
前端·javascript·vue.js·编辑器·ecmascript
Liudef063 小时前
deepseek v3 0324实现工作流编辑器
前端·css·编辑器·deepseek
txz20354 小时前
CMake在Windows环境下Visual Studio Code的使用
ide·vscode·编辑器
代码很单纯,复杂的是人。6 小时前
platfromIO(VScode)串口print输出中文乱码
ide·vscode·编辑器
大哥喝阔落6 小时前
vscode_拼写关闭
ide·vscode·编辑器
qq_297504617 小时前
【解决】VsCode中code runner无法使用cin 输入
ide·vscode·编辑器
朴拙数科9 小时前
plaintext 流程图和Markdown 的 Mermaid格式流程图的区别要点
编辑器·vim·流程图
森叶1 天前
Git Bash 设置Notepad++作为默认编辑器
git·编辑器·notepad++