目录

一、Inspector行为面板扩展

1.创建一个脚本

2.创建行为面板扩展的Editor脚本

(1)重写面板绘制方法

(2)获取序列化字段,并进行名称修改

(2.1)EditorGUILayout.PropertyField

(2.2)Update()与ApplyModifiedProperties()

(2.3)FindProperty方法

二、使用EditWindow自定义窗口


        编辑器工具大致可分为脚本Inspector的扩展和独立窗口两大部分,这两大部分又涉及SceneView绘制和Preview窗口绘制等。

一、Inspector行为面板扩展

1.创建一个脚本

        Enemy_Type1类有三个属性:血量、速度、攻击力,并有默认的初始值。

using UnityEngine;

public class Enemy_Type1 : MonoBehaviour
{
    public int hp = 100;                         //血量
    public float speed = 300;                    //速度
    public int atk = 10;                         //攻击力
}

2.创建行为面板扩展的Editor脚本

        Editor脚本实现CustomEditor特性,并重写OnInspectorGUI方法。通过OnInspectorGUI方法的重写,我们可以为其自定义行为面板属性内容,比如增加几个按钮预设,方便初始化不同类型的参数。

  • CustomEditor是Unity编辑器中的一个特性,允许开发者为特定组件创建自定义的检视器界面,替代默认的Inspector面板。
  • [CustomEditor(typeof(目标组件类型))]特性关联目标组件。
  • OnInspectorGUI是CustomEditor类中的一个关键方法,负责绘制自定义的UI界面。每当Inspector面板需要刷新时,Unity会调用这个方法,所以在这里添加GUI元素可以实时更新显示。

(1)重写面板绘制方法

using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(Enemy_Type1))]          //这里链接到MonoBehaviour脚本
public class Enemy_Type1Inspector : Editor
{
    /// <summary>
    /// 重写面板绘制方法:增加几个按钮预设,方便初始化不同类型的参数
    /// </summary>
    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();
        //获取绑定的MonoBehaviour脚本对象
        var concertTarget = base.target as Enemy_Type1;
        if (GUILayout.Button("preset1"))          //角色属性预设1
        {
            concertTarget.hp = 100;
            concertTarget.speed = 600;
            concertTarget.atk = 6;
        }
        if (GUILayout.Button("preset2"))          //角色属性预设2
        {
            concertTarget.hp = 200;
            concertTarget.speed = 550;
            concertTarget.atk = 4;
        }
    }
}

(2)获取序列化字段,并进行名称修改

(2.1)EditorGUILayout.PropertyField

        通过EditorGUILayout.PropertyField可将SerializedProperty序列化属性转化为Inspector面板的交互控件(如滑动条、勾选框等),无需手动编写不同数据类型的UI逻辑

EditorGUILayout.PropertyField(serializedObject.FindProperty("rotationSpeed"));

        此代码会自动根据rotationSpeed字段的类型(如floatint)生成对应的控件,在Inspector面板进行展示。

(2.2)Update()与ApplyModifiedProperties()
  • Update():从目标对象读取最新属性值到SerializedObject中,保证数据同步。
  • ApplyModifiedProperties():将用户在Inspector的修改回写至实际对象,自动处理撤销操作和多对象批量修改。
(2.3)FindProperty方法

        通过字段名字符串获取SerializedProperty对象(如_previewAngle),需确保字段名称与脚本中的序列化字段完全匹配。

    public override void OnInspectorGUI()
    {
        serializedObject.Update();                                   //首先更新序列化对象
                                                                     //生命值,序列化字段对象
        var hpProp = serializedObject.FindProperty("hp");
        //速度,序列化字段对象
        var speedProp = serializedObject.FindProperty("speed");
        //攻击力,序列化字段对象
        var atkProp = serializedObject.FindProperty("atk");
        using (var change = new EditorGUI.ChangeCheckScope())//监测GUI内容改变
        {
            EditorGUILayout.PropertyField(hpProp, new GUIContent("Enemy hp"));
            EditorGUILayout.PropertyField(speedProp, new GUIContent("Enemy speed"));
            EditorGUILayout.PropertyField(atkProp, new GUIContent("Enemy atk"));
            //直接进行序列化字段绘制
            if (GUILayout.Button("preset1"))               //预设1按钮
            {
                hpProp.intValue = 100;
                speedProp.floatValue = 600;
                atkProp.intValue = 6;
            }
            if (GUILayout.Button("preset2"))               //预设2按钮
            {
                hpProp.intValue = 200;
                speedProp.floatValue = 550;
                atkProp.intValue = 4;
            }
            if (change.changed)                              //如果改变则应用修改
            {
                serializedObject.ApplyModifiedProperties();
            }
        }
    }

二、使用EditWindow自定义窗口

        在Unity开发中我们还可以使用自定义窗口进行编辑器扩展。unity建议将自定义的插件启动项置于Tools目录下。附上一篇关于编辑器开发的文章:Unity | 编辑器开发-CSDN博客

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

public class MyWindows : EditorWindow
{
    //unity建议将自定义的插件启动项置于Tools目录中
    [MenuItem("Tools/Battle Debuger")]
    static void Setup()
    {
        //在这个静态方法中执行窗口的创建
        GetWindow<MyWindows>();
    }
    void Awake()
    {
        //打开MyWindows窗口时,会调用Awake方法
        Debug.Log("Awake");
        //事件委托,它会在 Scene 视图的每一帧 GUI 渲染时被调用。
        SceneView.duringSceneGui += OnSceneGUIDelegateBind;
    }
    void OnDestroy()
    {
        //场景视图GUI取消绑定
        SceneView.duringSceneGui -= OnSceneGUIDelegateBind;
    }
    void OnGUI()   //绘制窗口的GUI内容
    {
        if (GUILayout.Button("Generate Enemy_Type1"))
        {
            //省略其他代码
        }
        if (GUILayout.Button("Generate Enemy_Type1 x10"))
        {
            //省略其他代码
        }
        if (GUILayout.Button("Killed All Enemy"))
        {
            //省略其他代码
        }
    }

    void OnSceneGUIDelegateBind(SceneView sceneView)
    {
        Debug.Log("OnSceneGUIDelegateBind");
        var enemies = GetEnemies();
        for (int i = 0; i < enemies.Length; i++)
        {
            var enemy = enemies[i];
            //Unity的Handles类提供的绘图API,用于在Scene视图中绘制线框等图形,并且这些图形只在编辑器下可见,不影响运行时。
            Handles.DrawWireCube(enemy.Position, new Vector3(0.5f, 1f, 0.5f));
        }//将敌人以方块绘制出来
    }

    struct EnemyInfo { public Vector3 Position { get; set; } }     //调试数据
    EnemyInfo[] GetEnemies()
    {
        return new EnemyInfo[] {
             new EnemyInfo()
             {
                Position = new Vector3(0f, 0f, 0f)
             },
             new EnemyInfo() {
               Position = new Vector3(1.5f, 0f, 0f)
             }
    };
    }
}

——本文总结自微信读书《Unity3D动作游戏开发实战》

Logo

分享前沿Unity技术干货和开发经验,精彩的Unity活动和社区相关信息

更多推荐