接上文编辑器菜单栏扩展测试
具体代码如下:
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视图点击三次的效果)如图:


删除最后一个节点及删除所有节点的具体功能即字面意思。
该系列专栏为网课课程笔记,仅用于学习参考。