【Unity】编辑器小教程

这篇教程详细介绍了如何利用Unity编辑器进行扩展,包括绘制Gizmos、自定义面板参数、List对象绘制、可排序数组、预览效果、自定义Scene视图工具、脚本对象在Scene视图的预览等关键点,适合想要深入学习Unity编辑器编程的开发者。

写在前面

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可以打开内置对话框

这里写图片描述

  1. 首先在面板上隐藏默认的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>();
        ......
  2. 为了让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;
    }
  3. 实现自定义的绘制方程:

    [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:自定义绘制面板函数。
    
  4. 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 ]
评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值