分享

Unity 拓展Scene视图自定义操作

 蜕变在2016 2019-02-20

描述

在我们开发项目的时候,可能会遇到一些特殊的情况,比如场景里面有几个十几个甚至几十个看起来相同的物体,但是其实有几个物体可能在部分组件数值是特殊的,需要经常修改测试,想直观的在Scene标识这个特殊物体。又或者一个正方体(不管如何旋转90度,scene视图看起来都一样),我们想直观的在Scene视图了解这个正方体的朝向,等等。这些需求我们都可以通过自定义Scene视图来实现(其实是实现MonoBehaviour下OnDrawGizmosOnDrawGizmosSelected方法,实现自定义绘制,并只能在Scene视图下查看)


再比如,我们做一个类似《我的世界》这样的游戏,所有物体的大小位置都是按一个定好的基数。比如定好所有的物体都是1*1*1的,那么他们移动的时候也需要1单位1单位的移动,如图拼积木一般。加入我们有十个这样的物体需要把它拼成一个门的形状,那么一种方法自然是一个个物体去修改他的Inspector下的坐标。这样我们不仅要计算每个点的坐标,还要一个个修改,要是物体更多场景更大就很麻烦。当然我们也可以在Scene视图下拖动物体,但是我们很难精确到每次拖动都是1单位。这时候,我们就可以自定义Scene视图来处理。(实现Editor下的OnSceneGUI方法)


实现过程

首先,我们讲一讲MonoBehaviour下OnDrawGizmos或OnDrawGizmosSelected方法。既然是MonoBehaviour类,我们就需要创建一个脚本,继承于MonoBehaviour,然后实现这个两个方法,最后将脚本挂在我们需要的物体上即可。

OnDrawGizmos:每帧调用,里面实现的绘制在Scene视图可见

OnDrawGizmosSelected:物体被选中的时候每帧调用

  1. namespace Tool {
  2. public class CustomCube : MonoBehaviour {
  3. void OnDrawGizmos() {
  4. //每帧调用
  5. //绘制一个cube边框
  6. Gizmos.color = Color.black;
  7. Gizmos.DrawWireCube(GetComponent<Renderer>().bounds.center, GetComponent<Renderer>().bounds.size);
  8. //绘制一条直线,指向物体正上方
  9. Gizmos.color = Color.yellow;
  10. Gizmos.DrawLine(transform.position, transform.position + transform.up * 2);
  11. }
  12. void OnDrawGizmosSelected() {
  13. //被选中的时候每帧调用
  14. Gizmos.color = Color.red;
  15. Gizmos.DrawWireCube(GetComponent<Renderer>().bounds.center, GetComponent<Renderer>().bounds.size);
  16. }
  17. }
  18. }

如果我们想要在Scene视图上显示一些GUI控件,即上述例子的第二种情况,我们就需要用到OnSceneGUI方法。需要注意的是这个方法是在Editor基类下的,所以我们生成的脚本要继承Editor并放在Editor目录下。

OnSceneGUI:只有当物体选中的时候每帧会调用。

下面这个方法实现的是,带有CustomCube组件的物体,被选中的时候,Scene场景下会显示该物体的名称和坐标,同时会显示四个按钮提供按单位长度位移。并且当你在Scene视图使用箭头拖动该物体时,也是按单位长度位移。

  1. namespace EditorTool {
  2. //声明要处理的组件类型
  3. [CustomEditor(typeof(CustomCube))]
  4. public class CustomCubeEditor : Editor {
  5. CustomCube m_cube;
  6. Transform m_trans;
  7. void OnEnable() {
  8. //包含该组件的物体被选中的时候调用
  9. m_cube = (CustomCube)target;
  10. m_trans = m_cube.transform;
  11. }
  12. void OnSceneGUI() {
  13. //坐标位置取整
  14. Vector3 posTemp = m_trans.localPosition;
  15. float x = Mathf.RoundToInt(posTemp.x);
  16. float y = Mathf.RoundToInt(posTemp.y);
  17. float z = Mathf.RoundToInt(posTemp.z);
  18. m_trans.localPosition = new Vector3(x, y, z);
  19. //显示坐标
  20. Handles.Label(m_trans.position + Vector3.up * 3, m_cube.name + " : " + m_trans.position.ToString());
  21. Handles.BeginGUI();
  22. //规定GUI显示区域
  23. GUILayout.BeginArea(new Rect(100, 100, 100, 100));
  24. //GUI绘制按钮
  25. if(GUILayout.Button("上移")) {
  26. m_trans.position += Vector3.up;
  27. }
  28. if(GUILayout.Button("下移")) {
  29. m_trans.position += Vector3.down;
  30. }
  31. if(GUILayout.Button("左移")) {
  32. m_trans.position += Vector3.left;
  33. }
  34. if(GUILayout.Button("右移")) {
  35. m_trans.position += Vector3.right;
  36. }
  37. GUILayout.EndArea();
  38. Handles.EndGUI();
  39. }
  40. }
  41. }

上述的方法需要在物品选中的情况下,如果我们要处理未选中物品,在Scene视图额外显示一些属性的话(比如显示物品名称)。可以使用DrawGizmo标签来实现,如下,脚本要放在Editor文件夹下。

  1. namespace EditorTool {
  2. public class SceneEditor : MonoBehaviour {
  3. [DrawGizmo(GizmoType.NonSelected)]
  4. static void DrawGameObjectName(Transform transform, GizmoType gizmoType) {
  5. Handles.Label(transform.position, transform.gameObject.name);
  6. }
  7. }
  8. }

[DrawGizmo(GizmoType.NonSelected)],即没有选中物体的时候会调用下面的方法。方法名可以自定义,第一个参数可以是自定义的一个class(Transform,GameObject或者你自己写的组件,如果物品不包含该类型,则不会调用该方法),第二个参数必须为GizmoType。

效果如下:


GizmoType支持的类型如下:(NotSelected 和 SelectedOrChild已弃用)

Pickablegizmo在编辑器中可被选中
NotInSelectionHierarchy物品没有被选中并且其父节点也没被选中
Selected物体选中的时候
Active物体可见的时候
InSelectionHierarchy物品被选中或选中其中一个子物体
NonSelected物体没有被选中的时候

备注:上述三种方法都可以在Scene视图上做一些扩展,其实很多情况也是互通的,比如你可以在OnDrawGizmos方法去添加Handles.Label显示物体名称,或者做拖动的单位长度限制等等。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多