Unity editor editor plug-in production basis: four, sub-window panel menu UI components in the Scene window, and object object control component function extension

a note

The function extension of this page refers to the extension for editing a selected object, such as joystick, prompt and so on. If the object is not selected, related functions cannot be triggered.
A large number of directly callable scene components can be found here https://docs.unity3d.com/ScriptReference/Handles.html

1.1 Reference library

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine.SceneManagement;

1.1 Script structure

// 需要与场景中物体的脚本相关联
[CustomEditor(typeof(SceneTools))]
// 允许编辑多个物体
[CanEditMultipleObjects]
// 必须继承Editor
public class SceneToolsEditor : Editor{
    
    
    // 设定绑定的目标脚本
    private SceneTools targetScript;
    // 支持普通生命周期,Awake在这里是点击到物体既触发
    private void Awake(){
    
    
        targetScript = target as SceneTools;
    }
    // ui绘制周期 所有组件必须在这里实现更新
    private void OnSceneGUI(){
    
    

    }
}

Two Scene window menu implementation

The scene window menu is only displayed in the scene window.
The script implements clicking on the object bound by the script to display the menu, and canceling the click to close the menu

    // 必须在该周期运行
    protected virtual void OnSceneGUI(){
    
    
        this.DrawWindowInScene();
    }
    // 用于存储按钮状态
    private bool _toggleStatus;
    public void DrawWindowInScene()
    {
    
    
        // 在该标签内,才会把3位控件转为2维控件,相当于在canvas中
        Handles.BeginGUI();
        // 垂直布局
        GUILayout.BeginVertical("My Tools", "", new[] {
    
     GUILayout.Height(400), GUILayout.Width(100) });
        //定义 ui stayle
        var buttonStyle = new[] {
    
     GUILayout.Height(30), GUILayout.Width(100) };
        //按钮组写法 1 先获取存储值 2 初始化两个按钮的状态 3 如果按钮状态改变,就返回状态值,4 将状态值返回给存储值,5 下一帧更新按钮状态
        bool toggleStatus = _toggleStatus;
        toggleStatus = GUILayout.Toggle(toggleStatus, "button 1", "button", buttonStyle);
        toggleStatus = !GUILayout.Toggle(!toggleStatus, "button 2", "button", buttonStyle);
        _toggleStatus = toggleStatus;
        //空行
        GUILayout.Space(10);
        //按钮
        if (GUILayout.Button("button 3", buttonStyle))
        {
    
    
            Debug.Log("show menu 1");
        }

        GUILayout.EndVertical();
        Handles.EndGUI();
    }

Three Scene control implementation

    // 必须在该周期内实现
    protected virtual void OnSceneGUI()
    {
    
    
        // serializedObject.Update();
        // 绘制指示方向用的滑竿头  与button组合就能制作出类似移动或是旋转的操作杆
        this.DrawHandleCap(1f);
        // 绘制可调节范围的球盒
        this.DrawRadiusHandle();
        // 绘制可调节位置的滑竿
        // this.DrawSliderHundle();
        // 旋转控件
        this.RotateHandle();
        // 尺寸控件
        this.ScaleHandle();
        // 移动控件
        this.PositionHandle();
        this.SliderHandle();
        //虚拟按钮,与cap组合可以制作控件
        this.VirButton();
    }
    public void DrawHandleCap(float size)
    {
    
    
        // Handles.:
        // ArrowHandleCap
        // CircleHandleCap
        // ConeHandleCap
        // CubeHandleCap
        // DotHandleCap
        // RectangleHandleCap
        // ShperehandleCap
        if (Event.current.type == EventType.Repaint)
        {
    
    
            Transform transform = targetScript.transform;
            Handles.color = Handles.xAxisColor;
            Handles.ArrowHandleCap(
                0,
                transform.position + new Vector3(3f, 0f, 0f),
                transform.rotation * Quaternion.LookRotation(Vector3.right),
                size,
                EventType.Repaint
            );
            Handles.color = Handles.yAxisColor;
            Handles.ArrowHandleCap(
                0,
                transform.position + new Vector3(0f, 3f, 0f),
                transform.rotation * Quaternion.LookRotation(Vector3.up),
                size,
                EventType.Repaint
            );
            Handles.color = Handles.zAxisColor;
            Handles.ArrowHandleCap(
                0,
                transform.position + new Vector3(0f, 0f, 3f),
                transform.rotation * Quaternion.LookRotation(Vector3.forward),
                size,
                EventType.Repaint
            );
        }
    }
    float areaOfEffect = 2f;
    public void DrawRadiusHandle()
    {
    
    
        // 圆形盒 可以像设置碰撞盒那样用作范围设置
        // 当handle发生改变触发
        EditorGUI.BeginChangeCheck();
        float areaOfEffect = Handles.RadiusHandle(Quaternion.identity, targetScript.transform.position, this.areaOfEffect);
        if (EditorGUI.EndChangeCheck())
        {
    
    
            Undo.RecordObject(target, "Changed Area Of Effect");
            this.areaOfEffect = areaOfEffect;
        }
    }
    public void PositionHandle()
    {
    
    
        EditorGUI.BeginChangeCheck();
        Vector3 newTargetPosition = Handles.PositionHandle(targetScript.transform.position, Quaternion.identity);
        if (EditorGUI.EndChangeCheck())
        {
    
    
            Undo.RecordObject(targetScript.transform, "Change Look At Target Position");
            targetScript.transform.position = newTargetPosition;
        }
    }
    public void RotateHandle()
    {
    
    
        EditorGUI.BeginChangeCheck();
        Quaternion rot = Handles.RotationHandle(targetScript.transform.rotation, Vector3.zero);
        if (EditorGUI.EndChangeCheck())
        {
    
    
            //记录操作点 用于ctrl+z 动作回退
            Undo.RecordObject(targetScript.transform, "Rotated RotateAt Point");
            targetScript.transform.rotation = rot;
        }
    }
    public void ScaleHandle()
    {
    
    
        EditorGUI.BeginChangeCheck();
        Vector3 scale = Handles.ScaleHandle(targetScript.transform.localScale, Vector3.zero, Quaternion.identity, 1);
        if (EditorGUI.EndChangeCheck())
        {
    
    
            // 操作回退记录,一定要保存transform
            Undo.RecordObject(targetScript.transform, "Scaled ScaleAt Point");
            targetScript.transform.localScale = scale;
        }
    }
    public void SliderHandle()
    {
    
    
        float size = HandleUtility.GetHandleSize(targetScript.targetTransform.position) * 0.5f;
        float snap = 0.1f;

        EditorGUI.BeginChangeCheck();
        Vector3 newTargetPosition = Handles.Slider(targetScript.targetTransform.position, Vector3.right, size, Handles.ConeHandleCap, snap);
        if (EditorGUI.EndChangeCheck())
        {
    
    
            Undo.RecordObject(targetScript.targetTransform, "Change Look At Target Position");
            targetScript.targetTransform.position = newTargetPosition;
        }
    }

    public void VirButton()
    {
    
    
        // Vector3 position = targetScript.transform.position + Vector3.up * 2f;
        // float size = 2f;
        // float pickSize = size * 2f;

        // if (Handles.Button(position, Quaternion.identity, size, pickSize, Handles.RectangleHandleCap))
        //     Debug.Log("The button was pressed!");
    }

Four SnapGrid adsorption auxiliary lines and incremental modification functions

The adsorption auxiliary line refers to the auxiliary line in the unity editor. Using this function will automatically close to the nearby auxiliary line. The
incremental modification function means that each movement, rotation, and scaling are operated according to fixed interval values. Commonly used functions in general map editors.
The above two are used in conjunction with operators such as Rotate Move scale

    protected virtual void OnSceneGUI()
    {
    
    
        this.SetSnapRotate();
        this.SnapToGrid();
    }
    public void SetSnapRotate(){
    
    
        // 物体增量操作 返回增量值
        //计算修正值,可以是flaot,vector2,vector3
        Vector3 snapValue = Handles.SnapValue(Vector3.one,Vector3.one); 
        // 增量操作
        targetTranform[0].Rotate(snapValue,Space.World);
	}
    public void SnapToGrid(){
    
    
        // 物体对齐辅助线  可以多个物体多种对齐方式,通过SnapAxis设置
        Transform[] targetTranform = {
    
    GameObject.Find("").transform};
        Handles.SnapToGrid(targetTranform, SnapAxis.All);
	}

Five all codes

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine.SceneManagement;

// 网格吸附Handles.SnapToGrid
[CustomEditor(typeof(SceneTools))]
[CanEditMultipleObjects]
public class SceneToolsEditor : Editor
{
    
    
    // 设定绑定的目标脚本
    private SceneTools targetScript;
    // 每次点击挂在物体时就会启动
    private void Awake()
    {
    
    
        targetScript = target as SceneTools;
    }
    // 场景内的组件必须在该周期内绘制
    protected virtual void OnSceneGUI()
    {
    
    
        // serializedObject.Update();
        // 绘制指示方向用的滑竿头  与button组合就能制作出类似移动或是旋转的操作杆
        this.DrawHandleCap(1f);
        // 绘制可调节范围的球盒
        this.DrawRadiusHandle();
        // 绘制可调节位置的滑竿
        // this.DrawSliderHundle();
        // 旋转控件
        this.RotateHandle();
        // 尺寸控件
        this.ScaleHandle();
        // 移动控件
        this.PositionHandle();
        this.SliderHandle();
        //虚拟按钮,与cap组合可以制作控件
        this.VirButton();
        // 绘制scene内的窗口
        this.DrawWindowInScene();
    }
    public void SetSnapRotate(){
    
    
        // 物体增量操作 返回增量值
        //计算修正值,可以是flaot,vector2,vector3
        Vector3 snapValue = Handles.SnapValue(Vector3.one,Vector3.one); 
        // 增量操作
        targetTranform[0].Rotate(snapValue,Space.World);
	}
    public void SnapToGrid(){
    
    
        // 物体对齐辅助线  可以多个物体多种对齐方式,通过SnapAxis设置
        Transform[] targetTranform = {
    
    GameObject.Find("").transform};
        Handles.SnapToGrid(targetTranform, SnapAxis.All);
	}
    // 绘制提示用的模型,比如箭头,圆环,圆柱,方块等
    public void DrawHandleCap(float size)
    {
    
    
        // Handles.:
        // ArrowHandleCap
        // CircleHandleCap
        // ConeHandleCap
        // CubeHandleCap
        // DotHandleCap
        // RectangleHandleCap
        // ShperehandleCap
        if (Event.current.type == EventType.Repaint)
        {
    
    
            Transform transform = targetScript.transform;
            Handles.color = Handles.xAxisColor;
            Handles.ArrowHandleCap(
                0,
                transform.position + new Vector3(3f, 0f, 0f),
                transform.rotation * Quaternion.LookRotation(Vector3.right),
                size,
                EventType.Repaint
            );
            Handles.color = Handles.yAxisColor;
            Handles.ArrowHandleCap(
                0,
                transform.position + new Vector3(0f, 3f, 0f),
                transform.rotation * Quaternion.LookRotation(Vector3.up),
                size,
                EventType.Repaint
            );
            Handles.color = Handles.zAxisColor;
            Handles.ArrowHandleCap(
                0,
                transform.position + new Vector3(0f, 0f, 3f),
                transform.rotation * Quaternion.LookRotation(Vector3.forward),
                size,
                EventType.Repaint
            );
        }
    }
    float areaOfEffect = 2f;
    public void DrawRadiusHandle()
    {
    
    
        // 圆形盒 可以像设置碰撞盒那样用作范围设置
        // 当handle发生改变触发
        EditorGUI.BeginChangeCheck();
        float areaOfEffect = Handles.RadiusHandle(Quaternion.identity, targetScript.transform.position, this.areaOfEffect);
        if (EditorGUI.EndChangeCheck())
        {
    
    
            Undo.RecordObject(target, "Changed Area Of Effect");
            this.areaOfEffect = areaOfEffect;
        }
    }
    public void PositionHandle()
    {
    
    
        EditorGUI.BeginChangeCheck();
        Vector3 newTargetPosition = Handles.PositionHandle(targetScript.transform.position, Quaternion.identity);
        if (EditorGUI.EndChangeCheck())
        {
    
    
            Undo.RecordObject(targetScript.transform, "Change Look At Target Position");
            targetScript.transform.position = newTargetPosition;
        }
    }
    public void RotateHandle()
    {
    
    
        EditorGUI.BeginChangeCheck();
        Quaternion rot = Handles.RotationHandle(targetScript.transform.rotation, Vector3.zero);
        if (EditorGUI.EndChangeCheck())
        {
    
    
            //记录操作点 用于ctrl+z 动作回退
            Undo.RecordObject(targetScript.transform, "Rotated RotateAt Point");
            targetScript.transform.rotation = rot;
        }
    }
    public void ScaleHandle()
    {
    
    
        EditorGUI.BeginChangeCheck();
        Vector3 scale = Handles.ScaleHandle(targetScript.transform.localScale, Vector3.zero, Quaternion.identity, 1);
        if (EditorGUI.EndChangeCheck())
        {
    
    
            // 操作回退记录,一定要保存transform
            Undo.RecordObject(targetScript.transform, "Scaled ScaleAt Point");
            targetScript.transform.localScale = scale;
        }
    }
    public void SliderHandle()
    {
    
    
        float size = HandleUtility.GetHandleSize(targetScript.targetTransform.position) * 0.5f;
        float snap = 0.1f;

        EditorGUI.BeginChangeCheck();
        Vector3 newTargetPosition = Handles.Slider(targetScript.targetTransform.position, Vector3.right, size, Handles.ConeHandleCap, snap);
        if (EditorGUI.EndChangeCheck())
        {
    
    
            Undo.RecordObject(targetScript.targetTransform, "Change Look At Target Position");
            targetScript.targetTransform.position = newTargetPosition;
        }
    }

    public void VirButton()
    {
    
    
        // Vector3 position = targetScript.transform.position + Vector3.up * 2f;
        // float size = 2f;
        // float pickSize = size * 2f;

        // if (Handles.Button(position, Quaternion.identity, size, pickSize, Handles.RectangleHandleCap))
        //     Debug.Log("The button was pressed!");
    }

    private bool _toggleStatus;
    private int toggleIndex = 0;
    public void DrawWindowInScene()
    {
    
    
        // 在该标签内,才会把3位控件转为2维控件,相当于在canvas中
        Handles.BeginGUI();
        // 垂直布局
        GUILayout.BeginVertical("My Tools", "Window", new[] {
    
     GUILayout.Height(400), GUILayout.Width(100) });
        //定义 ui stayle
        var buttonStyle = new[] {
    
     GUILayout.Height(30), GUILayout.Width(100) };
        //按钮组写法 1 先获取存储值 2 初始化两个按钮的状态 3 如果按钮状态改变,就返回状态值,4 将状态值返回给存储值,5 下一帧更新按钮状态
        bool toggleStatus = _toggleStatus;
        toggleStatus = GUILayout.Toggle(toggleStatus, "button 1", "button", buttonStyle);
        toggleStatus = !GUILayout.Toggle(!toggleStatus, "button 2", "button", buttonStyle);
        _toggleStatus = toggleStatus;
        //空行
        GUILayout.Space(10);
        //按钮
        if (GUILayout.Button("button 3", buttonStyle))
        {
    
    
            Debug.Log("show menu 1");
        }

        if (GUILayout.Button("button 4", buttonStyle))
        {
    
    
            Debug.Log("show menu 2");
        }

        GUILayout.EndVertical();
        Handles.EndGUI();
    }
    // Vector3 PosTarget;
    public void DrawSliderHundle()
    {
    
    
        // 滑竿颜色
        Handles.color = Color.red;
        // 获取绑定的目标物体的位置
        Vector3 targetPostion = targetScript.targetTransform.position;
        Vector3 targetScale = targetScript.targetTransform.localScale;
        // 滑竿尺寸
        float size = HandleUtility.GetHandleSize(targetScale) * 0.3f;
        // 创建滑竿
        Vector3 handle1_pos = Handles.Slider2D(
            targetPostion, // 滑竿位置 设置为 绑定目标物体的位置
            Vector3.forward, // 滑竿方向朝前
            Vector3.up, // 滑竿滑道的方向 1
            Vector3.right,// 竿滑道的方向 2
            size, // 滑竿尺寸
            Handles.CubeHandleCap, // 滑竿图标模型
            Vector2.one); // 滑动时的间隔
        // 如果滑竿位置发生了改变    
        if (handle1_pos != targetPostion)
        {
    
    
            EditorUtility.SetDirty(target);
            EditorSceneManager.MarkSceneDirty(SceneManager.GetActiveScene());
            // 目标物体与滑竿位置同步
            targetPostion = handle1_pos;
        }
        // 绘制第二个滑竿
        Handles.Slider2D(
            Vector3.zero, // 滑竿位置 设置为 (0,0,0)
            Vector3.forward, //滑竿方向
            Vector3.up, //滑道方向
            Vector3.right,
            size, //滑竿尺寸
            Handles.CylinderHandleCap, // 滑竿模型
            Vector2.one);// 滑动的间隔
        //同步位置
        targetScript.targetTransform.position = postion;
        // 绘制线
        Handles.DrawLine(Vector3.zero, postion);
        // 重绘当前视图
        HandleUtility.Repaint();
    }
}

Guess you like

Origin blog.csdn.net/lengyoumo/article/details/113162725