Unity编辑器扩展(外挂)

每日一句:未来的样子藏在现在的努力里

目录

什么是编译器开发

C#特性[System.Serializable]

特殊目录

命名空间

/*检视器属性控制*/

    //添加变量悬浮提示文字

    //给数值设定范围(最小0,最大150)

//指定输入框,拥有5行

//默认显示5行,最多显示10行内容,再多用滚动条控制显示区域

//给小齿轮增加一个回调函数

编辑器外挂

弹窗

编辑器扩展案例


什么是编译器开发

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

C#特性[System.Serializable]

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

特殊目录

Plugins:需要跨语言调用的代码逻辑代码存储目录,手机SDK接入

Resources:存储跟随游戏包的资源目录

StreamingAssets:只读,存储跟随游戏包的资源目录

编辑器目录:Editor

制作多目录合并

项目中建立的Editor目录,编译器相关的逻辑和资源会放在其内部,相关内容,在打包生成时,不会一起生成到项目中,玩家也不会使用到编译器相关的内容

Editor目录下的脚本,无法挂载在场景对象下

命名空间

Unity代码逻辑命名空间:UnityEngine

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

/*检视器属性控制*/

如果对象不标记为可序列化,则Unity在存储的时候,会认为它不可被序列化,那么也就无法被显示

[Serializable]

public class Numerical

{

    public int Atk;

    public int Def;

}

public class Test : MonoBehaviour

{

    //监视面板显示对象

    public Numerical nul;

}

//一个成员变量可以添加多个特性

    //添加变量悬浮提示文字

    [Tooltip("不要填写大于150岁的年龄")]

    //给数值设定范围(最小0,最大150)

    [Range(0,150)]

    public int Age;

//指定输入框,拥有5行

    [Multiline(5)]

public string NickName;

//默认显示5行,最多显示10行内容,再多用滚动条控制显示区域

    [TextArea(5, 10)]

    public string Description;

//给小齿轮增加一个回调函数

    [ContextMenu("输出攻防比")]

    public  void PrintAD()

    {

        Debug.Log("角色攻防比");

}

编辑器外挂

//使生命周期函数,在编辑器状态下可以执行,游戏中也可以正常使用

//Update()在场景中对象发生变化或项目组织发生变化时会执行

[ExecuteInEditMode]

//当前组件依赖于盒子碰撞体

//当前组件挂载在对象时,盒子碰撞体会一起被添加上去

//当Player组件没有被移除时,盒子碰撞体不能被删除

[RequireComponent(typeof(BoxCollider))]

public class Player : MonoBehaviour

{

    public int ID;

    public GameObject Weapon;

    public Texture Cloth;

    public PlayerProfression Pro;

    public PlayerLoveColor LoveColor;

    public List<string> Items;

    public float Ack;

}

//单选使用十进制理解,即不同数代表不同选项

public enum PlayerProfression

{

    Warrior=0,

    Wizard=1,

}

//多选使用二进制理解,即不同位代表不同选项

public enum PlayerLoveColor

{

    Green=1,

    Red=2,

    Pink=4,

}

using UnityEditor;

[CustomEditor(typeof(Player))]

//将编辑器开发脚本与需要编辑的组件脚本建立外挂关联关系

//外挂脚本因为存储在Editor目录下,所以不会被打入最终的游戏包

public class PlayerEditor : Editor

{

    Player _Component;

    //当关联组件所在对象被选中或被添加时调用

    private void OnEnable()

    {

        _Component = target as Player;

    }

    //当关联组件所在对象被取消或被移出时调用

    private void OnDisable()

    {

        _Component = null;

    }

    public override void OnInspectorGUI()

    {

        //标题显示

        EditorGUILayout.LabelField("人物相关属性");

        _Component.ID = EditorGUILayout.IntField("玩家ID", _Component.ID);

        //.TextField()文本    .FolatField()浮点数    .Toggle()布尔     .Vector3Field()向量

        //.ColorField()颜色

        //对象数据类型绘制(1.标题 2.原始组件的值 3.成员变量的类型

        //4.是否可以将场景中的对象拖给这个成员变量)

        _Component.Weapon = EditorGUILayout.ObjectField("持有武器", _Component.Weapon, typeof(GameObject), true) as GameObject;

        //纹理

        _Component.Cloth= EditorGUILayout.ObjectField("衣服材质贴图", _Component.Weapon, 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.Ack = EditorGUILayout.Slider(new GUIContent("玩家攻击力"), _Component.Ack, 0, 100);

        if(_Component.Ack>80)

        {

            //显示消息框(红色)

            EditorGUILayout.HelpBox("攻击力太高了", MessageType.Error);

        }

        if (_Component.Ack <20)

        {

            //显示消息框(红色)

            EditorGUILayout.HelpBox("攻击力太低了", MessageType.Warning);

        }

        //按钮显示和元素排列

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

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

        if(GUILayout.Button("测试点击"))

        {

            Debug.Log("测试点击");

        }

        //开始横向排列绘制

        EditorGUILayout.BeginHorizontal();

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

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

        //结束横向排列绘制

        EditorGUILayout.EndHorizontal();

    }

}

弹窗

using UnityEditor;

using UnityEngine;

public class PopWindw : EditorWindow

{

    [MenuItem("工具/创建窗口")]

    static void OpenWindow()

    {

        PopupWindow window = GetWindow<PopupWindow>(false, "弹窗标题", true);

        window.minSize = new Vector2(400, 300);

        window.maxSize = new Vector2(800, 600);

    }

}

编辑器扩展案例

using System.Collections.Generic;

using UnityEngine;

[ExecuteInEditMode]//在编辑模式下可以执行一些生命周期函数

public class NodeManager : MonoBehaviour

{

    //存储了所有编辑器下点击生成的点,并使用预制体显示

    public List<GameObject> nodes;

    private void Update()

    {

        for (int i = 0; i < nodes.Count - 1; i++)

        {

            Debug.DrawLine(nodes[i].transform.position, nodes[i + 1].transform.position, Color.red, Time.deltaTime);

        }

    }

}

using UnityEditor;

using UnityEngine;

[CustomEditor(typeof(NodeManager))]

public class NodeManagerEditor : Editor

{

    NodeManager manager;

    bool isEditor = false;

    //当选中带有NodeManager组件对象时,获得组件

    private void OnEnable()

    {

        manager = (NodeManager)target;

    }

    //绘制组件的生命周期函数

    public override void OnInspectorGUI()

    {

        serializedObject.Update();

        SerializedProperty nodes = serializedObject.FindProperty("nodes");

        //Debug.Log("nodes" + nodes);

        EditorGUILayout.PropertyField(nodes, new GUIContent("路径"), true);

        serializedObject.ApplyModifiedProperties();

        //返回 bool 如果向 SerializedObject 应用了任何待处理的属性更改,则返回 true。

        if (!isEditor && GUILayout.Button("开始编辑"))

        {

            NodeWindow.OpenWindow(manager.gameObject);

            //调用打开界面方式

            isEditor = true;

        }

        else if (isEditor && GUILayout.Button("结束编辑"))

        {

            NodeWindow.CloseWindow();

            isEditor = false;

        }

        if (GUILayout.Button("删除最后一个节点"))

        {

            RemoveAtLast();

        }

        else if (GUILayout.Button("删除所有节点"))

        {

            RemoveAll();

        }

    }

    RaycastHit hit;

    //当选中关联的脚本挂载的物体

    //当鼠标在Scene视图下发生时,执行该方法,如鼠标移动

    private void OnSceneGUI()

    {

        if (!isEditor)//非编辑器下不能生成路点

        {

            return;

        }

        //当鼠标按下左键时发射一条射线

        //非运行时,使用Event类

        if (Event.current.button == 0 && Event.current.type == EventType.MouseDown)

        {

            //从鼠标的位置需要发射射线了

            //因为是从Scene视图下发射射线,跟场景中的摄像机并没有关系,所以不能使用相机发射射线的方式

            //从编辑器GUI中的一个点向世界定义一条射线,参数一般都是鼠标的坐标

            Debug.Log("aaa");

            //将 2D GUI 位置转换为世界空间射线HandleUtility.GUIPointToWorldRay

            Ray ray = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);

            Debug.Log(ray);

            //Debug.DrawLine(Event.current.mousePosition,  Color.red);

            Debug.Log(Physics.Raycast(ray, out hit, 1000));

            if (Physics.Raycast(ray, out hit, 1000))

            {

                Debug.Log("bbb");

                //需要在检测到的点实例化路点

                InstancePathNode(hit.point + Vector3.up * 0.1f);

            }

        }

    }

    //生成节点

    void InstancePathNode(Vector3 position)

    {

        GameObject prefab = Resources.Load<GameObject>("PathNode");

        Debug.Log(prefab);

        GameObject pathNode = Instantiate<GameObject>(prefab, position, Quaternion.identity, manager.transform);

        manager.nodes.Add(pathNode);//把生成的路点添加到列表里

    }

    //删除最后一个节点

    void RemoveAtLast()

    {

        //保证有节点才能删节点

        if (manager.nodes.Count > 0)

        {

            //从场景中删除游戏物体

            DestroyImmediate(manager.nodes[manager.nodes.Count - 1]);

            //把该节点从列表中移除

            manager.nodes.RemoveAt(manager.nodes.Count - 1);

        }

    }

    //删除所有的节点

    void RemoveAll()

    {

        //遍历删除所有的节点物体

        for (int i = 0; i < manager.nodes.Count; i++)

        {

            if (manager.nodes[i] != null)

            {

                DestroyImmediate(manager.nodes[i]);

            }

            manager.nodes.Clear();

        }

    }

}

public class NodeWindow : EditorWindow

{

    static NodeWindow window;

    static GameObject nodeManager;

    public static void OpenWindow(GameObject manager)

    {

        nodeManager = manager;

        //真正开启了一个窗口

        window = EditorWindow.GetWindow<NodeWindow>();

    }

    private void Update()

    {

        Selection.activeGameObject = nodeManager;

    }

    public static void CloseWindow()

    {

        window.Close();

    }

}

链接:https://pan.baidu.com/s/1Kr2vYQKIoA897YY-l3j8kg?pwd=acap

提取码:acap

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/235734.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

@JsonFormat与@DateTimeFormat

JsonFormat注解很好的解决了后端传给前端的格式&#xff0c;我们通过使用 JsonFormat可以很好的解决&#xff1a;后台到前台时间格式保持一致的问题 其次&#xff0c;另一个问题是&#xff0c;我们在使用WEB服务的时&#xff0c;可 能会需要用到&#xff0c;传入时间给后台&am…

在Gitee上维护Erpnext源

在Gitee上维护Erpnext源 官方的frappe和erpnext地址: GitHub - frappe/frappe: Low code web framework for real world applications, in Python and Javascript GitHub - frappe/erpnext: Free and Open Source Enterprise Resource Planning (ERP) 1, 仓库地址输入frappe的官…

Java中异常处理-详解

异常&#xff08;Exception&#xff09; JVM 默认处理方案 把异常的名称&#xff0c;异常的原因&#xff0c;及异常出错的位置等信息输出在控制台程序停止执行 异常类型 编译时异常必须显示处理&#xff0c;否则程序会发生错误&#xff0c;无法通过编译运行时异常无需显示处理…

华为鸿蒙凉了?谣言还是

华为鸿蒙系统凉了吗&#xff1f;我们从目前的一系列新闻来看。鸿蒙并没有凉&#xff0c;反而愈发强大。从下面的一些新闻事实可以看出华为鸿蒙已经和Android、ios形成竞争对手了。 1、华为宣布鸿蒙4.0的发布 2023年7月&#xff0c;华为开发者大会上正式宣布。华为发布了备受期…

算法第4版 第2章排序

综述&#xff1a;5个小节&#xff0c;四种排序应用&#xff0c;初级排序、归并排序、快速排序、优先队列 2.1.初级排序 排序算法模板&#xff0c;less(), exch(), 排序代码在sort()方法中&#xff1b; 选择排序&#xff1a;如升序排列&#xff0c;1.找到数组中最小的元素&am…

一种DevOpts的实现方式:基于gitlab的CICD(一)

写在之前 笔者最近准备开始入坑CNCF毕业的开源项目&#xff0c;看到其中有一组开源项目的分类就是DevOpts。这个领域内比较出名的项目是Argocd&#xff0c;Argo CD 是一个用于 Kubernetes 的持续交付 (Continuous Delivery) 工具&#xff0c;它以声明式的方式实现了应用程序的…

【Docker】配置阿里云镜像加速器

默认情况下&#xff0c;将来从docker hub &#xff08;https://hub.docker.com )上下载镜像太慢&#xff0c;所以一般配置镜像加速器。 没有账号的注册一个账号并登录 登录之后点击控制台 查看 cat /etc/docker/daemon.json

【大数据进阶第三阶段之Hive学习笔记】Hive安装

目录 1、环境准备 2、下载安装 3、配置环境变量 4、配置文件 4.1、配置hive-env.sh ​编辑4.2、配置hive-site.xml 5、上传配置jar 6、启动 1、环境准备 安装hadoop 以及 zookeeper、mysql 【大数据进阶第二阶段之Hadoop学习笔记】Hadoop 运行环境搭建-CSDN博客 《z…

C++上位软件通过Snap7开源库访问西门子S7-200/LOGO PLC/合信M226ES PLC V存储区的方法

前言 在前面例程中谈到了C 通过Snap7开源库S7通信库跟西门子S7-1200PLC/S7-1500PLC以及合信CTMC M226ES PLC/CPU226 PLC通信的方式方法和应用例程。但是遗憾的是Snap7中根据官方资料显示只能访问PLC的 DB区、MB区、C区、T区 、I区、Q区&#xff0c;并没有提到有关如何访问S7-20…

HNU-数据库系统-作业

数据库系统-作业 计科210X 甘晴void 202108010XXX 第一章作业 10.09 1.(名词解释)试述数据、数据库、数据库管理系统、数据库系统的概念。 数据&#xff0c;是描述事物的符号记录。 数据库&#xff08;DB&#xff09;&#xff0c;是长期存储在计算机内、有组织、可共享的大量…

蓝桥杯练习题(二)

&#x1f4d1;前言 本文主要是【算法】——蓝桥杯练习题&#xff08;二&#xff09;的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 …

音乐制作软件Studio One mac有哪些特点

Studio One mac是一款专业的音乐制作软件&#xff0c;该软件提供了全面的音频编辑和混音功能&#xff0c;包括录制、编曲、合成、采样等多种工具&#xff0c;可用于制作各种类型的音乐&#xff0c;如流行音乐、电子音乐、摇滚乐等。 Studio One mac软件特点 1. 直观易用的界面&…

软件测试|详解 Pytest 参数化:简化测试用例的编写

简介 Pytest 是一个广泛使用的 Python 测试框架&#xff0c;它提供了丰富的功能来编写和执行测试用例。其中一个强大的特性是参数化&#xff0c;它允许我们通过一种简洁的方式运行多个输入参数的相似测试用例&#xff0c;从而减少冗余的代码。本文将详细介绍 Pytest 的参数化功…

如何给字符串字段添加索引

MySQL是支持前缀索引的&#xff0c;可以定义字符串的一部分作为索引&#xff0c;如果创建索引的语句不指定前缀长度&#xff0c;那么索引就会包含整个字符串。 alter table SUser add index index1(email);alter table SUser add index index2(email(6)); 如上两个创建索引的语…

嵌入式培训机构四个月实训课程笔记(完整版)-Linux系统编程第十天-Linux下mplayer音乐播放器练习题(物联技术666)

更多配套资料CSDN地址:点赞+关注,功德无量。更多配套资料,欢迎私信。 物联技术666_嵌入式C语言开发,嵌入式硬件,嵌入式培训笔记-CSDN博客物联技术666擅长嵌入式C语言开发,嵌入式硬件,嵌入式培训笔记,等方面的知识,物联技术666关注机器学习,arm开发,物联网,嵌入式硬件,单片机…

Mondo备份linux操作系统为iso镜像 —— 筑梦之路

简介 Mondo Rescue&#xff08;以下简称Mondo&#xff09;可以说是Linux 下的Ghost&#xff0c;它可以将你的系统像照相一样备份至磁带&#xff0c;CD-R&#xff0c;CD-RW&#xff0c;NFS或硬盘分区。Mondo广泛支援LVM&#xff0c;RAID&#xff0c;ext2, ext3, JFS, XFS,Reise…

第十二届全国大学生GIS技能大赛试题、数据以及解题思路

一、赛题说明 第十二届全国大学生GIS应用技能大赛试题&#xff0c;共分为上午和下午两部分 上午题 上午的题和往届的没啥区别&#xff0c;还是偏向于考数据整合、空间配准、数字化等基础的数据处理能力。 下午两套题都需要做哟 第一套 第二套&#xff0c;考察三维分析 关于…

知识引导的分子生成扩散模型 - KGDiff 评测

一、背景介绍 KGDiff模型是一个基于口袋的知识引导的3D分子生成的扩散模型&#xff0c;来源于上海交通大学计算机学院涂仕奎教授的文章&#xff1a; 《KGDiff: towards explainable target-aware molecule generation with knowledge guidance》。文章链接&#xff1a;*KGDiff…

LeetCode 145. 二叉树的后序遍历

145. 二叉树的后序遍历 给你一棵二叉树的根节点 root &#xff0c;返回其节点值的 后序遍历 。 示例 1&#xff1a; 输入&#xff1a;root [1,null,2,3] 输出&#xff1a;[3,2,1]示例 2&#xff1a; 输入&#xff1a;root [] 输出&#xff1a;[]示例 3&#xff1a; 输入&…

20240107让Firefly的AIO-3399J开发板的Android11下配置为默认1080p录像

20240107让Firefly的AIO-3399J开发板的Android11下配置为默认1080p录像 2024/1/7 23:01 开发板&#xff1a;Firefly的AIO-3399J【RK3399】 SDK&#xff1a;rk3399-android-11-r20211216.tar.xz【Android11】 Android11.0.tar.bz2.aa【ToyBrick】 Android11.0.tar.bz2.ab Androi…