Unity学习笔记之编辑器开发

什么是编辑器开发:

  • 对编辑器实现功能扩展,一般会使用它开发项目工具或实现Unity插件。

C#中的特性:

        

  • 用于在C#运行时,传递程序中各种元素(类、结构体、变量、方法、枚举、组件)的行为信息的声明标签。一个声明标签是通过放置在它所在应用元素的前面的方括号 [] 中来描述。

特殊目录:

  • Plugins:需要跨语言调用的逻辑代码存储目录,手机SDK接入
  • Resources:存储跟随游戏包的资源目录
  • StreamingAssets:只读,存储跟随游戏包的资源目录
  • Editor:编辑器目录,项目中建立的Editor目录,编辑器相关的逻辑和资源会放在其内部,相关内容在打包生成时不会一起生成到项目中,玩家也不会使用到编辑器相关的内容。Editor目录下的脚本,无法挂载在场景对象下。

命名空间:

  • Unity代码逻辑命名空间:UnityEngine
  • Unity编辑器命名空间:UnityEditor,此命名空间不要出现在游戏被发布的逻辑代码中,会导致项目打包失败!。

检视器属性控制

对检视器原有属性的控制,通过在代码中给变量加特性实现。

  • [HideInInspector]
  • 可以隐藏公共成员变量,防止Inspector的值影响到他,同时保证脚本中变量的可访问度;
  • [SerializeField]
  • 将私有变量设置为检视面板可见可修改,Unity会将对象进行序列化存储,即使是私有的,标记为可序列化后也会显示,公有变量默认是可序列化的;
  • [Serializable]
  • 对象序列化,对象如果不标记为可序列化,则Unity在存储的时候,会认为他不可被序列化,那么也就无法被显示
  • [Space(50)]
  • 在当前成员变量上方留 50 像素空白区域
  • [Header("标题")]
  • 在当前成员变量上方加入一个标题文字
  • [Tooltip("鼠标移入后的提示")]
  • 添加变量悬浮提示,当鼠标放入后会有提示
  • [Range(0, 150)]
  • 给数值设定范围
  • [Multiline(5)]
  • 指定输入行字符,参数为行数
  • [TextArea(5, 10)]
  • 设置默认显示 5 行,最多显示 10 行内容,再多用滚动条控制显示
  • ---------------------分割线,下方特性用于方法-------------------
  • [ContextMenu("输出攻防比")]
  • 在小齿轮中添加一个回调函数,参数为函数名称,用于调用该特性标记的方法
  • [ContextMenuItem("执行我的方法", "MyFunc")]
  • 给一个变量添加右键菜单,第一个参数是菜单名称,第二个参数是回调函数
  • ---------------------分割线,下方特性用于类-------------------
  • [AddComponentMenu("自定义控制器/玩家控制器", 1)]
  • 将脚本类如 Player 作为组件添加到AddComponent上,第一个参数:分类名/组件名,第二个参数:列表中显示的顺序
  • [ExecuteInEditMode]
  • 使生命周期函数,在编辑器状态下可以执行,游戏中也可以正常使用,Update()在场景中对象发生变化或项目组织发生变化时会在编辑器下执行
  • [RequireComponent(typeof(BoxCollider))]
  • 当前组件依赖于盒子碰撞体,当前组件挂载在对象上时,盒子碰撞体会一起被添加上去,当Player组件没有被移除时,盒子碰撞体不能被删除

检视器高级修改(外挂式编程)

高级控制可以将所关联组件检视面板的属性隐藏,然后重新绘制,Editor目录下建立外挂式开发脚本,将编辑器脚本与原始脚本关联

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//步骤1:引入编辑器的命名空间,检视器属于编辑器开发范畴
using UnityEditor;

[CustomEditor(typeof(Player))]//步骤3:将编辑器开发脚本与需要编辑的组件脚本建立外挂关联关系
//外挂脚本因为存储在Editor目录下,所以不会被打入最终的游戏包
//不继承自Mono,而是继承自Editor
public class PlayerEditor : Editor //步骤2:继承Editor类,使用编辑器相关的成员变量和生命周期函数
{
    //存储获得的需要编辑显示的组件
    private Player _Component;

    //步骤4:需要在当前的外挂脚本中,获得需要被扩展的Player组件对象
    //当关联组件所在对象被选中或组件被添加时,自动调用
    private void OnEnable()
    {
        //Debug.Log("enable");
        //步骤5:获取Player组件对象
        _Component = target as Player;

    }

    //当关联组件所在对象被取消或组件被移除时,调用
    private void OnDisable()
    {
        //Debug.Log("disable");
        _Component = null;
    }
    
    //用于绘制检视面板的生命周期函数,决定检视面板内容的关键
    public override void OnInspectorGUI()
    {
        //标题显示
        EditorGUILayout.LabelField("人物相关属性");

        _Component.ID = EditorGUILayout.IntField("玩家ID", _Component.ID);
        //文本
        _Component.Name = EditorGUILayout.TextField("玩家名称", _Component.Name);
        //浮点数
        _Component.Atk = EditorGUILayout.FloatField("玩家攻击力", _Component.Atk);
        //布尔
        _Component.isMan = EditorGUILayout.Toggle("是否为男性", _Component.isMan);
        //向量
        _Component.HeadDir = EditorGUILayout.Vector3Field("头部方向", _Component.HeadDir);
        //颜色
        _Component.Hair = EditorGUILayout.ColorField("头发颜色", _Component.Hair);

        对象数据类型绘制
        //参数1:标题
        //参数2:原始组件的值
        //参数3:成员变量的类型
        //参数4:是否可以将场景中的对象拖给这个成员变量
        _Component.Weapon = EditorGUILayout.ObjectField("持有武器", _Component.Weapon, typeof(GameObject), true) as GameObject;
        //纹理
        _Component.Cloth = EditorGUILayout.ObjectField("衣服材质贴图", _Component.Cloth, typeof(Texture), false) as Texture;

        枚举数据类型绘制
        //整数转枚举
        //int id = 0;
        //PLAYER_PROFESSION p = (PLAYER_PROFESSION)id;

        //单选枚举(标题, 组件上的原始值)
        _Component.Pro = (PlayerProfression)EditorGUILayout.EnumPopup("玩家职业", _Component.Pro);

        //多选枚举(标题, 组件上的原始值)
        _Component.LoveColor = (PlayerLoveColor)EditorGUILayout.EnumFlagsField("玩家喜欢的颜色", _Component.LoveColor);

        终极数据类型绘制
        //更新可序列化数据
        serializedObject.Update();
        //通过成员变量名找到组件上的成员变量
        SerializedProperty sp = serializedObject.FindProperty("Items");
        //可序列化数据绘制(取到的数据,标题,是否将所有获得的序列化数据显示出来)
        EditorGUILayout.PropertyField(sp, new GUIContent("道具信息"), true);
        //将修改的数据,写入到可序列化的原始数据中
        serializedObject.ApplyModifiedProperties();

        滑动条绘制
        //滑动条显示(1.标题,2.原始变量,最小值,最大值)
        _Component.Atk = EditorGUILayout.Slider(new GUIContent("玩家攻击力"), _Component.Atk, 0, 100);

        if (_Component.Atk > 80)
        {
            //显示消息框(红色)
            EditorGUILayout.HelpBox("攻击力太高了", MessageType.Error);
        }

        if (_Component.Atk < 20)
        {
            //显示消息框(黄色)
            EditorGUILayout.HelpBox("攻击力太低了", MessageType.Warning);
        }

        //按钮显示和元素排列
        //(按钮是否被按下)显示按钮(按钮名称)
        GUILayout.Button("来个按钮");
        GUILayout.Button("来个按钮");

        if (GUILayout.Button("测试点击"))
        {
            Debug.Log("测试点击");
        }

        //开始横向排列绘制
        EditorGUILayout.BeginHorizontal();

        GUILayout.Button("再来个按钮");
        GUILayout.Button("再来个按钮");

        //结束横向排列绘制
        EditorGUILayout.EndHorizontal();
    }
}

创建顶部菜单

用于在顶部菜单添加一些常用的固定功能,比如一键导出AB包

//顶部菜单类
public class Menu
{
    //在顶部显示"工具"菜单,下方有"导出AB资源包",点击执行函数
    [MenuItem("工具/导出AB资源包")]
    static void BuildAB()
    {
        //Debug.Log("导出AB资源包存放路径"),也可以写具体导包逻辑;
        Debug.Log(Application.persistentDataPath);
    }
}

创建窗口

可以弹出一个自定义窗口

public class PopWindow : EditorWindow
{
    [MenuItem("工具/创建窗口")]
    static void OpenWindow()
    {
        //泛型T 窗口类型。必须派生自 EditorWindow。
        //第一个参数设置为 true 可创建浮动实用程序窗口,设置为 false 可创建正常窗口。
        //第三个参数设置是否为窗口提供焦点(如果已存在)。
        PopWindow window = GetWindow<PopWindow>(false, "弹窗标题", true);
        window.minSize = new Vector2(40, 30);
        window.minSize = new Vector2(80, 60);
    }

    //开窗口调用
    private void OnEnable()
    {
        Debug.Log("enable");
    }

    //关窗口调用
    private void OnDisable()
    {
        Debug.Log("disable");
    }

    //窗口开启就调用
    private void Update()
    {
        Debug.Log("update");
    }

    //用于绘制窗口内容
    private void OnGUI()
    {
        if (GUILayout.Button("测试点击"))
        {
            Debug.Log("测试点击");
        }
    }

    //场景结构发生变化,执行回调函数
    private void OnHierarchyChange()
    {
        Debug.Log("hierarchy");
    }

    //项目结构发生变化,执行回调函数
    private void OnProjectChange()
    {
        Debug.Log("project");
    }

    //选中物体发生变化,执行回调函数
    private void OnSelectionChange()
    {
        //获取当前选中的物体的名称
        Debug.Log(Selection.activeGameObject.name);
    }
}

猜你喜欢

转载自blog.csdn.net/qq_63122730/article/details/132615680