前言
本文主要讲解unity中的部分可视化调试与编辑器工具,主要是面向想要做工具链的程序,较为基础,后续如果想要进阶,可以自行学习Unity的UIToolkit与GraphView
基础
Gizmos
Gizmos是在Scene界面用于辅助画图的工具,一般情况下,我们可以用它绘制范围(比如攻击范围,敌人仇恨范围等);
它的使用也很简单,在Mono脚本中有两个方法:OnDrawGizmos与OnDrawGizmosSelected,前者会始终在Scene界面绘制,而后者只有你选中挂载该脚本的物体时才会绘制,一般的绘制流程如下:
挑选喜欢的颜色->选中对应的图形与范围->结束
请注意,范围是你自己设置的,并不能像UE的轨道检测一样自动绘制范围大小
public class MyGizmos : MonoBehaviour
{
public float attackRange = 5f;
public float viewAngle = 60f;
public Transform target;
// 始终显示(选中不选中都画)
void OnDrawGizmos()
{
// 设置颜色
Gizmos.color = Color.yellow;
// 画线
Gizmos.DrawLine(transform.position, transform.position + Vector3.up * 2);
// 画立方体
Gizmos.DrawWireCube(transform.position + Vector3.up, Vector3.one);
}
// 只在选中时显示
void OnDrawGizmosSelected()
{
// 攻击范围 - 红色球体
Gizmos.color = new Color(1, 0, 0, 0.3f);
Gizmos.DrawSphere(transform.position, attackRange);
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(transform.position, attackRange);
// 视野扇形(用线模拟)
if (target != null)
{
Gizmos.color = Color.cyan;
Gizmos.DrawLine(transform.position, target.position);
}
}
}

Inspector
检查器是我们平时打交道比较多的,当然我们可以利用一些特性将部分字段显示出来
-
SerializeField\] :序列化字段,顾名思义就是将字段序列化,使其显示在检查器面板上,注意,私有字段也能序列化,属性不能序列化
-
Tooltip("")\]:放在字段上,用于在检查器介绍该字段的功能,需要你把鼠标放到对应的字段上
-
HideInInspector\]:将该字段在检查器中隐藏
以下是一个例子,相信看过之后你就能明白这些标签的作用

public class Test1 : MonoBehaviour
{
public int Value1;//public 变量默认会序列化
protected int Value2;
private int Value3;
public int A_mode1;
public int A_mode2;
public int B_mode1;//不同模块不会区分
public int B_mode2;
public string Tool;
public string MyName;
}

public class Test2 : MonoBehaviour
{
[HideInInspector]
public int Value1;//public 变量默认会序列化
[SerializeField]
protected int Value2;
[SerializeField]
private int Value3;
[Header("A模块")]
public int A_mode1;
public int A_mode2;
[Header("B模块")]
public int B_mode1;//不同模块不会区分
public int B_mode2;
[Tooltip("这是一个工具")]
public string Tool;
[TextArea(5, 5)]
public string MyName;
}
EditorWindow
我们在Unity中能看到各种各样的窗口,它们都继承自EditorWindow类,我们同样能自己写出这样的窗口。首先需要注意的:脚本放在Assets文件夹的Editor文件夹(没有就新建)中,并引入UnityEditor命名空间
以下是一个例子,我们绕着这个例子处理

public class NativeWindow :EditorWindow
{
//这是核心打开方式,你可以在编辑器的对应目录下打开对应的窗口了
[MenuItem("Tools/EditorWindows/原生窗口")]
static void Open() => GetWindow<NativeWindow>("原生").Show();
string playerName = "Player";//窗口字段,可通过输入框修改其值
int level = 1;
float hp = 100f;
bool isActive = true;
GameObject test;
void OnGUI()
{
// 1. Label - 纯文本显示
GUILayout.Label("=== 基础信息 ===", EditorStyles.boldLabel);
GUILayout.Label("这是一个普通文本标签");
// 2. 输入框
playerName = EditorGUILayout.TextField("玩家名称", playerName);
level = EditorGUILayout.IntField("等级", level);
hp = EditorGUILayout.FloatField("血量", hp);
test = (GameObject)EditorGUILayout.ObjectField("测试对象", test, typeof(GameObject), true);
// 3. Slider - 滑条
hp = EditorGUILayout.Slider("血量滑条", hp, 0f, 200f);
GUILayout.Space(20); // 留空
// 4. 水平布局
GUILayout.Label("操作按钮:", EditorStyles.boldLabel);
GUILayout.BeginHorizontal();//有开始就要有结束
if (GUILayout.Button("打印你好", GUILayout.Height(30)))
{
Debug.Log("你好");
}
if (GUILayout.Button("打印你坏", GUILayout.Height(30)))
{
Debug.Log("你坏");
}
GUILayout.EndHorizontal();
// 5. 垂直布局
GUILayout.Space(10);
GUILayout.BeginVertical("box"); // 带边框的垂直组
GUILayout.Label("高级设置:");
GUILayout.BeginHorizontal();
GUILayout.Label("是否激活:", GUILayout.Width(60));
isActive = EditorGUILayout.Toggle(isActive);//bool变量用toggle框
GUILayout.EndHorizontal();
GUILayout.EndVertical();
GUILayout.Space(10);
}
}
编辑器窗口使用流程如下:该窗口类有一些成员变量(通常是bool,int,float,string,GameObject)等,在OnGUI函数中画出输入框或者滑条等,设置完这些值再通过 if (GUILayout.Button("按钮名", GUILayout.Height(30)))绘制按钮并判断是否按下来调用方法,需要注意OnGUI()中的各种绘制的方式。
一般情况下,我们可以创建以下的窗口:
- 关卡编辑器窗口:一般用于批量在场景中生成或销毁预制体,值得注意的是此时生成需要用PrefabUtility.InstantiatePrefab()之后再Undo.RegisterCreatedObjectUndo(go, "名称");销毁时用Undo.DestroyObjectImmediate();因为此时不是运行时
- 修改Asset中的资产,这里需要了解AssetDatabase和GUID,不再赘述
- 运行时的编辑器,在游戏运行时动态直接修改玩家的属性,用FindObjectOfType获取对应Mono组件即可,也可以获取场景信息,场景中的敌人数量等
附加内容Odin
Odin插件是一个编辑器扩展插件,里面有一些好用的特性,可以在原有的编辑器基础上扩展,这里简单介绍一下:
常用:
-
LabelText("")\]:用于将字段在检查器中显示括号中的名字
-
TableList\]:将列表中的内容按表格展开
-
Button("")\]:放在方法上,会自动在检查器上创建按钮,按下后即可调用,按钮大小和颜色都可以自定义
-
ShowIf("")\]\[EnableIf("")\]\[HideIf("")\]:这三个是一套,括号中可以放布尔方法的方法名或布尔字段的字段名,满足条件才会显示或隐藏

public class MyToolWindow :OdinEditorWindow//注意这里继承自OdinEditorWindow
{
[MenuItem("Tools/Windows/测试")]
static void Open()=>GetWindow<MyToolWindow>().Show();
[BoxGroup("设置")]//上面提到的特性这里也可以用
public string PlayerName;
[BoxGroup("设置"), MinMaxSlider(0, 100)]
public Vector2Int HpRange;
[Button("生成"),GUIColor(0.2f,0.8f,0.2f)]
void Generate()
{
Debug.Log("生成玩家");
}
}
结语
由于GraphView过于繁杂,笔者对其不感兴趣,请读者自行学习,它的作用是利用EditorWindow创建出节点编辑器,比如ShaderGraph,技能树,对话系统,行为树与状态机等,这里不再赘述