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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

机器学习激活函数

激活函数 激活函数是人工神经网络中的一个重要组成部分。它们用于向神经网络中添加非线性因素&#xff0c;使得网络能够解决复杂问题&#xff0c;如图像识别、语言处理等。激活函数的作用是决定一个神经元是否应该被激活&#xff0c;也就是说&#xff0c;它帮助决定神经元的输…

jupyter notebook 配置conda 虚拟环境python

conda创建python环境 conda create -n openvoice python3.9 激活环境 source activate openvoice 在虚拟环境中安装ipykernel pip install ipykernel 添加虚拟环境进到 jupyter notebook python -m ipykernel install --user --name openvoice --display-name openvoice …

计算机网络必考大题

TCP / IP 五层协议或OSI七层参考模型 CRC校验码&#xff08;也称为循环冗余码&#xff09; 1、根据生成多项式P(x)确定除数&#xff1b; 2、给生成多项式的P(x)的最高阶补0&#xff1b; 3、给信息位(补0后)与除数做异或运算&#xff0c;得到余数。 不相同为1 ^ 4、得到的余数补…

免费申请eu.org域名,开启个人网站之旅

介绍 eu.org的免费域名注册服务是由OpenTLD B.V.提供的。相比于其他免费域名注册服务&#xff0c;eu.org的域名后缀更加独特。同时&#xff0c;eu.org的域名注册也比较简单&#xff0c;只需要填写一些基本信息&#xff0c;就可以获得自己的免费域名。 注册账号 点击进入登…

如何在Github上快速下载代码

由于网络环境问题&#xff0c;有时候比较难从Github上下载代码&#xff0c;我归纳了以下三种从Github上下载代码的方法&#xff0c;如何选择使用&#xff0c;可根据你的实际情况&#xff1a; 目录 方法一&#xff1a;使用 “Download ZIP” 按钮 方法二&#xff1a;使用 Git…

代码随想录刷题笔记(DAY 10)

今日总结&#xff1a;快要期末考试了&#xff0c;现在在疯狂速成&#xff0c;今天稍微缓和了一点&#xff0c;应该能保证继续每天刷题&#xff0c;欠下的那些寒假补上。 Day 10 01. 用栈实现队列&#xff08;No. 232&#xff09; 题目链接 代码随想录题解 1.1 题目 请你仅…

【JAVA基础】JVM之类加载--双亲委派机制

目录 1. 类加载的过程描述&#xff1a;看图&#xff1a;解释&#xff1a; 2. 那么类加载器都有哪些呢3. 双亲委派机制3.1 双亲委派机制的过程3.2 图看委派过程3.3 为什么要设计双亲委派机制 4. 自定义类加载器4.1 如何定义自己的类加载器&#xff1f; 1. 类加载的过程 描述&am…

YOLOv8 + openVINO 多线程数据读写顺序处理

多线程数据读写顺序处理 一个典型的生产者-消费者模型&#xff0c;在这个模型中&#xff0c;多个工作线程并行处理从共享队列中获取的数据&#xff0c;并将处理结果以保持原始顺序的方式放入另一个队列。 多线程处理模型&#xff0c;具体细节如下&#xff1a; 1.数据:数据里必…

Java学习,一文掌握Java之SpringBoot框架学习文集(4)

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…

GBASE南大通用SQL API 中的 SQL

ESQL 产品为GBASE南大通用数据库 GBase 8s SQL API&#xff08;应用程序编程接口&#xff09;。 GBase 为 C 编程语言产生 SQL API。 下图展示 SQL API 产品如何工作。您编写您在其中将 SQL 语句处理作为可执行代码的源 程序。嵌入式 SQL 预处理器处理您的源程序&#xff0c;它…

【QML COOK】- 008-自定义属性

前面介绍了用C定义QML类型&#xff0c;通常在使用Qt Quick开发项目时&#xff0c;C定义后端数据类型&#xff0c;前端则完全使用QML实现。而QML类型或Qt Quick中的类型时不免需要为对象增加一些属性&#xff0c;本篇就来介绍如何自定义属性。 1. 创建项目&#xff0c;并编辑Ma…

Qt 6之六:Qt Designer介绍

Qt 6之六&#xff1a;Qt Designer介绍 Qt Designer是一个可视化的用户界面设计工具&#xff0c;用于创建Qt应用程序的用户界面&#xff0c;允许开发人员通过拖放和布局来设计和创建GUI界面。 Qt 6之一&#xff1a;简介、安装与简单使用 https://blog.csdn.net/cnds123/articl…

作用域与作用域链

作用域与作用域链 一、什么是作用域 作用域就是一个独立的代码区域&#xff0c;域内的变量不会暴露到外部&#xff0c;外部无法访问&#xff0c;也就是说具有隔离性。 function outFun() {var inVariable "内层变量2"; } outFun(); // inVariable 的作用域仅在函…

matlab绘图修改坐标轴数字字体大小及坐标轴自定义间隔设置

一、背景 在matlab使用plot函数绘图后&#xff0c;生成的图片坐标轴数字字体大小及间隔可能并不符合我们的要求&#xff0c;因此需要自定义修改&#xff0c;具体方法如下 二、修改坐标轴数字字体大小 只需添加以下命令即可&#xff1a; set(gca,FontName,Times New Roman,F…

echarts -- 柱状图之柱状条如何显示白色侧阴影且鼠标移入时高亮

有个图表是要求柱状条的右下侧显示一个白色的侧阴影&#xff0c;一直没找到合适的方法&#xff0c; 加border或者shadowColor都达不到需求的效果。 因为柱状图 中series里可以包含多组数据&#xff0c;有几组就代表一个系列中有几个数据。这就代表series里要写七组数据。 对于上…

你了解计算机网络的发展历史吗?

1.什么是计算机网络 计算机网络是指将一群具有独立功能的计算机通过通信设备以及传输媒体被互联起来的&#xff0c;在通信软件的支持下&#xff0c;实现计算机间资源共享、信息交换或协同工作的系统。计算机网络是计算机技术与通信技术紧密结合的产物&#xff0c;两者的迅速发展…

电阻如何读取阻值

前言&#xff1a;大家经常见到的贴片电阻上的丝印有纯数字、数字与R组合、数字与除R之外的字母组合的&#xff0c;但大家知不知道这样的标注与贴片电阻的i精度相关&#xff1f;同一个阻值因为精度不同&#xff0c;标注也会不同。例如封装为0805的贴片电阻&#xff0c;丝印473和…

STL-list的使用简介

目录 ​编辑 一、list的底层实现是带头双向循环链表 二、list的使用 1、4种构造函数&#xff08;与vector类似&#xff09;​编辑 2、迭代器iterator 3、容量&#xff08;capicity&#xff09;操作 4、element access 元素获取 5、增删查改 list modifiers 6、list的迭…

【计算机组成原理】-指令系统

&#x1f3b5;1.指令的发展 &#x1f308;1.1基础概念 计算机的程序是由一系列的机器指令组成的。指令就是要计算机执行某种操作的命令。从计算机的层次结构来说&#xff0c;有微指令&#xff0c;机器指令和宏指令之分。微程序中用到微指令&#xff0c;属于硬件&#xff0c;而…

Fluids —— MicroSolvers DOP

目录 Gas SubStep —— 重复执行对应的子步 Switch Solver —— 切换解算器 Gas Attribute Swap —— 交换、复制或移动几何体属性 Gas Intermittent Solve —— 固定时间间隔计算子解算器 Gas External Forces —— 计算外部力并更新速度或速度场 Gas Particle Separate…