首页 > 代码库 > Unity 编辑器扩展 场景视图内控制对象

Unity 编辑器扩展 场景视图内控制对象

如果有一个敌人生成器类。当中有个属性range用来表示敌人生成的范围区域大小。那么能够用OnDrawGizmos函数来绘制它在场景视图所代表的区域大小。便于开发调试。这个敌人生成器类,类似例如以下:
using UnityEngine;
using System.Collections;

public class EnemySpawn : MonoBehaviour 
{
    public float range;

    void OnDrawGizmos()
    {
        Gizmos.color = Color.green;
        Gizmos.DrawWireCube(transform.position, new Vector3(range, 0, range));
    }

    void OnDrawGizmosSelected()
    {
        Gizmos.color = Color.yellow;
        Gizmos.DrawWireCube(transform.position, new Vector3(range, 0, range));
    }
}
能够在场景视图看到。当未选中改对象时,为绿色边框表示区域大小,当选中时,则为黄色。

例如以下图所看到的:

未选中:
技术分享
选中:
技术分享

怎样直接在场景视图进行调整区域的大小呢。即直接调整range值,而不用在检视器里进行设置。这能够用控制柄来达到,即Handles类。创建一个编辑器扩展类,代码例如以下:
using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(EnemySpawn))]
public class EnemySpawnEditor : Editor 
{
    void OnSceneGUI()
    {
        EnemySpawn enemySpawn = target as EnemySpawn;
        Vector3 pos = enemySpawn.transform.position;

        // 计算4个角落
        Vector3 v0 = new Vector3(pos.x - enemySpawn.range / 2, pos.y, pos.z - enemySpawn.range / 2);
        Vector3 v1 = new Vector3(pos.x + enemySpawn.range / 2, pos.y, pos.z + enemySpawn.range / 2);

        float y = v0.y;
        Vector3[] corners = new Vector3[4];
        corners[0] = new Vector3(v0.x, y, v0.z);
        corners[1] = new Vector3(v0.x, y, v1.z);
        corners[2] = new Vector3(v1.x, y, v1.z);
        corners[3] = new Vector3(v1.x, y, v0.z);

        // 4个缩放值控制柄
        for (int i = 0; i < 4; ++i)
        {
            enemySpawn.range = Handles.ScaleValueHandle(enemySpawn.range,
                        corners[i],
                        Quaternion.identity,
                        1,
                        Handles.SphereCap,
                        1);

            Handles.Label(corners[i], i.ToString());
        }

        if (GUI.changed)
        {
            EditorUtility.SetDirty(target);
        }
    }
}
如今能够在场景视图里看到这个对象边框各有一个球体,进行拉动球体。能够看到区域大小值range进行变化了,例如以下图所看到的:
技术分享
如今用控制柄改变的range值,无法进行撤销。这样就无法进行回滚操作,为其加入撤销功能,代码更改为例如以下:
using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(EnemySpawn))]
public class EnemySpawnEditor : Editor 
{
    void OnSceneGUI()
    {
        EnemySpawn enemySpawn = target as EnemySpawn;
        Vector3 pos = enemySpawn.transform.position;

        // 计算4个角落
        Vector3 v0 = new Vector3(pos.x - enemySpawn.range / 2, pos.y, pos.z - enemySpawn.range / 2);
        Vector3 v1 = new Vector3(pos.x + enemySpawn.range / 2, pos.y, pos.z + enemySpawn.range / 2);

        float y = v0.y;
        Vector3[] corners = new Vector3[4];
        corners[0] = new Vector3(v0.x, y, v0.z);
        corners[1] = new Vector3(v0.x, y, v1.z);
        corners[2] = new Vector3(v1.x, y, v1.z);
        corners[3] = new Vector3(v1.x, y, v0.z);
        
        EditorGUI.BeginChangeCheck();
        float scaleValue = enemySpawn.range;
        float newRange = scaleValue;


        // 4个缩放值控制柄
        for (int i = 0; i < 4; ++i)
        {
            scaleValue = Handles.ScaleValueHandle(enemySpawn.range,
                        corners[i],
                        Quaternion.identity,
                        1,
                        Handles.SphereCap,
                        1);
            newRange = scaleValue == enemySpawn.range ? newRange : scaleValue;

            Handles.Label(corners[i], i.ToString());
        }

        if (EditorGUI.EndChangeCheck())
        {
            // 记录对象以便进行撤销
            Undo.RecordObject(enemySpawn, "Change Range");
            enemySpawn.range = newRange;

            EditorUtility.SetDirty(target);
        }

    }
}
如今再次进行拉动,就可以在Edit→Undo下看到撤销信息了。例如以下图所看到的:
技术分享
对其它人来讲,可能看到这些球体。不知道是可以拖动的。也不明确拖动它之后会产生什么样的效果,那么,假设可以在鼠标移动到球体上面的时候,可以提示出这些信息就好了。这里,添加下鼠标在球体上时,鼠标箭头会变化成带缩放提示的图标,代码更改为例如以下:
using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(EnemySpawn))]
public class EnemySpawnEditor : Editor
{
    void OnSceneGUI()
    {
        EnemySpawn enemySpawn = target as EnemySpawn;
        Vector3 pos = enemySpawn.transform.position;

        // 计算4个角落
        Vector3 v0 = new Vector3(pos.x - enemySpawn.range / 2, pos.y, pos.z - enemySpawn.range / 2);
        Vector3 v1 = new Vector3(pos.x + enemySpawn.range / 2, pos.y, pos.z + enemySpawn.range / 2);

        float y = v0.y;
        Vector3[] corners = new Vector3[4];
        corners[0] = new Vector3(v0.x, y, v0.z);
        corners[1] = new Vector3(v0.x, y, v1.z);
        corners[2] = new Vector3(v1.x, y, v1.z);
        corners[3] = new Vector3(v1.x, y, v0.z);

        EditorGUI.BeginChangeCheck();
        float scaleValue = enemySpawn.range;
        float newRange = scaleValue;
        float sizeHandle = 1.0f;
        float realSize = sizeHandle * 0.1f; // 实际为0.15f,比它小才干更精确显示鼠标


        // 4个缩放值控制柄
        for (int i = 0; i < 4; ++i)
        {
            v0 = new Vector3(corners[i].x - realSize, y, corners[i].z - realSize);
            v1 = new Vector3(corners[i].x + realSize, y, corners[i].z + realSize);

            // 换算成GUI坐标
            Vector2[] handles = new Vector2[4];
            handles[0] = HandleUtility.WorldToGUIPoint(new Vector3(v0.x, y, v0.z));
            handles[1] = HandleUtility.WorldToGUIPoint(new Vector3(v0.x, y, v1.z));
            handles[2] = HandleUtility.WorldToGUIPoint(new Vector3(v1.x, y, v1.z));
            handles[3] = HandleUtility.WorldToGUIPoint(new Vector3(v1.x, y, v0.z));

            Bounds b = new Bounds(handles[0], Vector3.zero);
            for (int j = 1; j < 4; ++j)
            {
                b.Encapsulate(handles[j]);
            }

            Vector2 min = b.min;
            Vector2 max = b.max;
            Rect rect = new Rect(min.x, min.y, max.x - min.x, max.y - min.y);

            // 调试绘制是否计算正确
            Handles.BeginGUI();
            GUI.Box(rect, rect.ToString());
            Handles.EndGUI();

            // 是否鼠标在控制柄上
            if (rect.Contains(Event.current.mousePosition))
            {
                // 显示缩放图标的箭头
                EditorGUIUtility.AddCursorRect(rect, MouseCursor.ScaleArrow);
            }


            scaleValue = Handles.ScaleValueHandle(enemySpawn.range,
                        corners[i],
                        Quaternion.identity,
                        sizeHandle,
                        Handles.SphereCap,
                        1);
            newRange = scaleValue == enemySpawn.range ?

 newRange : scaleValue;

            Handles.Label(corners[i], i.ToString());
        }

        if (EditorGUI.EndChangeCheck())
        {
            // 记录对象以便进行撤销
            Undo.RecordObject(enemySpawn, "Change Range");
            enemySpawn.range = newRange;

            EditorUtility.SetDirty(target);
        }
    }
}

这里仅仅是模糊地计算了下球体控制柄的响应范围。

如今能够在场景视图看到每一个球体都有一个矩形包围着。当鼠标进入到这个矩形里面。就会改变鼠标图形,例如以下图所看到的:
技术分享

如今能够把调试绘制矩形框的给删除掉了。即删除下面代码:
// 调试绘制是否计算正确
Handles.BeginGUI();
GUI.Box(rect, rect.ToString());
Handles.EndGUI();

參考资料:
1.Unity3D研究院之拓展Scene视图(三) http://www.xuanyusong.com/archives/2303
2.Unity3D 场景编辑器扩展学习笔记-Handles&Event http://blog.csdn.net/kun1234567/article/details/19477787

Unity 编辑器扩展 场景视图内控制对象