Unity编辑器扩展(外挂)

发布时间:2024年01月10日

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

目录

什么是编译器开发

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

文章来源:https://blog.csdn.net/m0_63330263/article/details/135503652
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。