写在前面
Unity最强大的地方之一是它扩展性非常强的编辑器。Unite Europe 2016上有一个视频专门讲编辑器编程的:
这里大概记录一下里面的关键点。
场景一
关注点:
- 绘制重要区域,Gizmos.DrawXXX
- OnDrawGizmos和OnDrawGizmosSelected回调函数
- 点击Gizmos按钮就可以在Game视图也看到线框了
// OnDrawGizmos()会在编辑器的Scene视图刷新的时候被调用
// 我们可以在这里绘制一些用于Debug的数据
void OnDrawGizmos()
{
Gizmos.color = new Color( 1f, 0f, 0f, 1f );
Gizmos.DrawWireCube( transform.position + BoxCollider.center, BoxCollider.size );
Gizmos.color = new Color( 1f, 0f, 0f, 0.3f );
Gizmos.DrawCube( transform.position + BoxCollider.center, BoxCollider.size );
}
// OnDrawGizmosSelect()类似于OnDrawGizmos(),它会在当该组件所属的物体被选中时被调用
void OnDrawGizmosSelected()
{
Gizmos.color = new Color( 1f, 1f, 0f, 1f );
Gizmos.DrawWireCube( transform.position + BoxCollider.center, BoxCollider.size );
Gizmos.color = new Color( 1f, 1f, 0f, 0.3f );
Gizmos.DrawCube( transform.position + BoxCollider.center, BoxCollider.size );
}
场景二
关注点:
- 组织面板上的参数,添加滑动条、Header、空白等
[Space( 10 )]
public float MaximumHeight;
public float MinimumHeight;
[Header( "Safe Frame" )]
[Range( 0f, 1f )]
public float SafeFrameTop;
[Range( 0f, 1f )]
public float SafeFrameBottom;
注意到上面面板的最小面有个Camera Height,调节它可以改变摄像机的高度。这个改变是可以发生在编辑器模式下的,而且也不需要脚本添加ExecuteInEditor。这是通过实现自定义的Editor脚本来实现的:
using UnityEngine;
using UnityEditor;
using System.Collections;
// 我们可以通过为一个类定义它的Editor类型的[CustomEditor]来自定义该类的绘制界面
// 这需要把这个文件放在Editor目录下
[CustomEditor( typeof( GameCamera ) )]
public class GameCameraEditor : Editor
{
GameCamera m_Target;
// 重载OnInspectorGUI()来绘制自己的编辑器
public override void OnInspectorGUI()
{
// target可以让我们得到当前绘制的Component对象
m_Target = (GameCamera)target;
// DrawDefaultInspector告诉Unity按照默认的方式绘制面板,这种方法在我们仅仅想要自定义某几个属性的时候会很有用
DrawDefaultInspector();
DrawCameraHeightPreviewSlider();
}
void DrawCameraHeightPreviewSlider()
{
GUILayout.Space( 10 );
Vector3 cameraPosition = m_Target.transform.position;
cameraPosition.y = EditorGUILayout.Slider( "Camera Height", cameraPosition.y, m_Target.MinimumHeight, m_Target.MaximumHeight );
if( cameraPosition.y != m_Target.transform.position.y )
{
// 改变状态前,使用该方法来记录操作,以便之后Undo
Undo.RecordObject( m_Target, "Change Camera Height" );
m_Target.transform.position = cameraPosition;
}
}
}
场景三
关注点:
- 自定义绘制List对象
- 使用serializedObject来修改参数的话Unity会自动有各种帮助函数,例如自动添加Undo功能
- 如果直接修改参数的话,需要使用EditorUtility.SetDirty来告诉Unity需要保存数据
- BeginChangeCheck()和EndChangeCheck()会检测它们之间的GUI有没有被修改,如果修改了的话可以据此修改参数
- Undo.RecordObject可以为下一步修改添加Undo/Redo
- EditorUtility.DisplayDialog可以打开内置对话框
首先在面板上隐藏默认的List绘制方法,使用HideInInspector隐藏属性:
public class PistonE03 : MonoBehaviour { public float Speed; public Vector3 AddForceWhenHittingPlayer; //We are hiding this in the inspector because we want to draw our own custom //inspector for it. [HideInInspector] public List<PistonState> States = new List<PistonState>(); ......
为了让PistonState可以显示在面板上,需要序列化PistonState:
//[System.Serializable] tells unity to serialize this class if //it's used in a public array or as a public variable in a component [System.Serializable] public class PistonState { public string Name; public Vector3 Position; }
实现自定义的绘制方程:
[CustomEditor( typeof( PistonE03 ) )] public class PistonE03Editor : Editor { PistonE03 m_Target; public override void OnInspectorGUI() { m_Target = (PistonE03)target; DrawDefaultInspector(); DrawStatesInspector(); } //Draw a beautiful and useful custom inspector for our states array void DrawStatesInspector() { GUILayout.Space( 5 ); GUILayout.Label( "States", EditorStyles.boldLabel ); for( int i = 0; i < m_Target.States.Count; ++i ) { DrawState( i ); } DrawAddStateButton(); }
DrawDefaultInspector:先绘制默认的,DrawStatesInspector:自定义绘制面板函数。
DrawState函数:
void DrawState( int index ) { if( index < 0 || index >= m_Target.States.Count ) { return; } // 在我们的serializedObject中找到States变量 // serializedObject允许我们方便地访问和修改参数,Unity会提供一系列帮助函数。例如,我们可以通过serializedObject来修改组件值,而不是直接修改,Unity会自动创建Undo和Redo功能 SerializedProperty listIterator = serializedObject.FindProperty( "States" ); GUILayout.BeginHorizontal(); { // 如果是在实例化的prefab上修改参数,我们可以模仿Unity默认的途径来让修改过的而且未被Apply的值显示成粗体 if( listIterator.isInstantiatedPrefab == true ) { //The SetBoldDefaultFont functionality is usually hidden from us but we can use some tricks to //access the method anyways. See the implementation of our own EditorGUIHelper.SetBoldDefaultFont //for more info EditorGUIHelper.SetBoldDefaultFont( listIterator.GetArrayElementAtIndex( index ).prefabOverride ); } GUILayout.Label( "Name", EditorStyles.label, GUILayout.Width( 50 ) ); // BeginChangeCheck()和EndChangeCheck()会检测它们之间的GUI有没有被修改 EditorGUI.BeginChangeCheck(); string newName = GUILayout.TextField( m_Target.States[ index ].Name, GUILayout.Width( 120 ) ); Vector3 newPosition = EditorGUILayout.Vector3Field( "", m_Target.States[ index ].Position )