Unity编辑器扩展(外挂)

news2024/12/23 20:54:38

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

目录

什么是编译器开发

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.coloradmin.cn/o/1372706.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

寄快递选哪个平台便宜?快递优惠券免费领取!

寄快递选哪个平台便宜&#xff1f;快递优惠券免费领取&#xff01; 对于市场来说&#xff0c;快递业是非常重要的一部分&#xff0c;它业既贯通市场流通消费投资出口的各环节&#xff0c;又关联一二三各产业。根据相关数据显示&#xff0c;我国的快递行业正呈现势如破竹的劲头&…

双位置继电器DLS-5/2TH 额定电压:110VDC 触点形式:7开3闭 柜内安装

系列型号&#xff1a; DLS-5/1电磁式双位置继电器; DLS-5/2电磁式双位置继电器; DLS-5/3电磁式双位置继电器; DLS-5/2G电磁式双位置继电器; DLS-5/3 220VDC双位置继电器 一、用途 1.1用途 DLS-5双位置继电器(以下简称产品)用于各种保护与自动控制系统中&#xff0c;作为切换…

x-cmd pkg | magick - 开源图像处理工具

目录 简介首次用户功能特点类似工具与竞品进一步探索 简介 magick 是由 ImageMagick 提供的一个功能强大且多功能的开源图像处理工具&#xff0c;可以灵活高效地处理图像文件&#xff0c;例如格式转换、图像大小调整、图像裁减、图像拼接、图像色彩校正和图像合成等常见的图像…

神州战神z7ra7重装教程

UEFI模式下装的系统&#xff0c;开机速度明显比Legacy模式下装的系统开机速度更快 关键点&#xff1a; ①.U盘格式必须为FAT32 ②.不可以使用ISO镜像制作UEFI安装U盘&#xff0c;而是使用微软官方的工具。 ③.开机BIOS设置&#xff0c;最好将Secure boot设置为Disabled&#xf…

[Kubernetes]8. K8s使用Helm部署mysql集群(主从数据库集群)

上一节讲解了K8s包管理工具Helm、使用Helm部署mongodb集群(主从数据库集群),这里来看看K8s使用Helm部署mysql集群(主从数据库集群) 一.Helm 搭建mysql集群 1.安装mysql不使用persistence(无本地存储) 无本地存储:当重启的时候,数据库消失 (1).打开官网的应用中心 打开应用中…

Linux内存管理:(六)页交换算法

文章说明&#xff1a; Linux内核版本&#xff1a;5.0 架构&#xff1a;ARM64 参考资料及图片来源&#xff1a;《奔跑吧Linux内核》 Linux 5.0内核源码注释仓库地址&#xff1a; zhangzihengya/LinuxSourceCode_v5.0_study (github.com) 1. 引言 在Linux操作系统中&#x…

【AI】CycleGan对抗生成网络遥感影像生成地图效果测试

今天看到一个有趣的项目&#xff0c;CycleGan对抗生成网络把马生成成斑马&#xff0c;还有一个测试用例是用遥感影像生成平面地图的效果&#xff0c;效果如下图所示&#xff0c;我大学是遥感专业&#xff0c;看到遥感影像就触动了我的原神&#xff0c;于是原神启动&#xff0c;…

@Transactional 事务注解

第一、先简单介绍一下Spring事务的传播行为 所谓事务的传播行为是指&#xff0c;如果在开始当前事务之前&#xff0c;一个事务上下文已经存在&#xff0c;此时有若干选项可以指定一个事务性方法的执行行为。在TransactionDefinition定义中包括了如下几个表示传播行为的常量&…

C++学习笔记——返回对象

一、返回对象 当我们说一个函数返回对象时&#xff0c;意味着该函数的返回值是一个对象。这种情况下&#xff0c;函数可以通过创建对象的副本、返回对象的引用或者返回对象的指针来实现。 返回对象的副本&#xff1a; 当一个函数返回对象的副本时&#xff0c;函数内部会创建一…

VSCode使用MinGW编译器,配置C/C++环境

目录 一、安装VSCode 二、安装MinGW编译器 1、配置环境变量 2、测试配置是否成功 三、配置VSCode 1、安装所需扩展 2、新建代码存放文件夹 3、添加配置文件 4、配置文件内容 &#xff08;1&#xff09;c_cpp_properties.json &#xff08;2&#xff09;launch.json …

11 个 Python全栈开发工具集

前言 以下是专注于全栈开发不同方面的 Python 库;有些专注于 Web 应用程序开发&#xff0c;有些专注于后端&#xff0c;而另一些则两者兼而有之。 1. Taipy Taipy 是一个开源的 Python 库&#xff0c;用于构建生产就绪的应用程序前端和后端。 它旨在加快应用程序开发&#xf…

数字战场上的坚固屏障:雷池社区版(WAF)

黑客的挑战 智能语义分析算法&#xff1a; 黑客们常利用复杂技术进行攻击&#xff0c;但雷池社区版的智能语义分析算法能深入解析攻击本质&#xff0c;即使是最复杂的攻击手法也难以逃脱。 0day攻击防御&#xff1a; 传统防火墙难以防御未知攻击&#xff0c;但雷池社区版能有效…

云卷云舒:kubernetes简介

Kubernetes是由google公司在2014年发布的一款开源的容器编排引擎&#xff0c;用于容器化应用程序的自动化部署、扩展与管理。它能够编排多种容器任务&#xff0c;涵盖虚拟机集群管理、负载均衡以及网络流量分配等等。2017年&#xff0c;aws、微软云、阿里云等等著名的云计算公司…

用Linux的视角来理解缓冲区概念

缓冲区的认识 缓冲区&#xff08;buffer&#xff09;是存储数据的临时存储区域。当我们用C语言向文件中写入数据时&#xff0c;数据并不会直接的写到文件中&#xff0c;中途还经过了缓冲区&#xff0c;而我们需要对缓冲区的数据进行刷新&#xff0c;那么数据才算写到文件当中。…

Js-基础语法(二)

运算符 赋值运算符 赋值运算符&#xff1a;对变量进行赋值的运算符 已经学过的赋值运算符&#xff1a; 将等号右边的值赋予给左边, 要求左边必须是一个容器 其他赋值运算符&#xff1a; - */% 使用这些运算符可以在对变量赋值时进行快速操作 一元运算符 众多的 JavaScrip…

10个提高 Python Web 开发效率的VS Code插件

VS Code具有灵活、便捷和丰富的可用插件库&#xff0c;是Web开发人员中非常受欢迎的代码编辑器。 本文介绍10个VS Code插件&#xff0c;它们可以提高你作为Web开发人员的工作效率。 1. Live Preview Live Preview插件支持在VS Code的小型浏览器中查看网站。因此&#xff0c;无…

40-特殊运算符delete,new,.getDate,.setDate,运算符优先级

1.delete删除. 数组 // 可以删除数组元素&#xff0c;可以删除对象键值对// 删除数组的值&#xff0c;数组长度保持不变// 删掉的值变成emptyvar arr [1,2,3,4,5];delete arr[0];console.log(arr); 对象 var obj {"a":"aa","b":"bb&quo…

LeNet-5(fashion-mnist)

文章目录 前言LeNet模型训练 前言 LeNet是最早发布的卷积神经网络之一。该模型被提出用于识别图像中的手写数字。 LeNet LeNet-5由以下两个部分组成 卷积编码器&#xff08;2&#xff09;全连接层&#xff08;3&#xff09; 卷积块由一个卷积层、一个sigmoid激活函数和一个…

数据结构及单链表例题(下)

上次我们已经了解了单链表的数据结构定义以及创建单链表的两种方法,这节介绍几道例题. 文章目录 前言 一、已知L为带头结点的单链表,请依照递归思想实现下列运算 二、单链表访问第i个数据节点 三、在第i个元素前插入元素e 四、删除第i个结点 五、查找带头结点单链表倒数第…

Vivado开发FPGA使用流程、教程 verilog(建立工程、编译文件到最终烧录的全流程)

目录 一、概述 二、工程创建 三、添加设计文件并编译 四、线上仿真 五、布局布线 六、生成比特流文件 七、烧录 一、概述 vivado开发FPGA流程分为创建工程、添加设计文件、编译、线上仿真、布局布线&#xff08;添加约束文件&#xff09;、生成比特流文件、烧录等步骤&a…