企业做网站营销的四大途径,网站设计可以吗,付银行的网站建设费的会计科目,wordpress 空间安装文章目录 前言一、扩展Project视图1、右键扩展菜单#xff08;Asset#xff09;2、监听事件3、拓展布局 二、扩展Hierarchy视图1、拓展菜单#xff08;GameObject#xff09;2、拓展布局3、重写菜单 三、扩展Inspector视图1、扩展原生组件2、扩展继承组件 四、扩展Scene视图… 文章目录 前言一、扩展Project视图1、右键扩展菜单Asset2、监听事件3、拓展布局 二、扩展Hierarchy视图1、拓展菜单GameObject2、拓展布局3、重写菜单 三、扩展Inspector视图1、扩展原生组件2、扩展继承组件 四、扩展Scene视图1、绘制辅助元素2、辅助UI3、常驻辅助UI 五、扩展Game视图总结 前言
本篇文章是对前文关于编辑器拓展的探讨的延伸。即使内置的Unity编辑器再强大也无法满足所有不同产品和游戏的需求。为了解决这个问题Unity提供了编辑器拓展的API接口。我们可以通过代码反射的方式修改内置的系统编辑器同时游戏开发者也可以利用EditorGUI接口编写适合自己的专属游戏编辑器。这涵盖了从简单的一键换字体、材质、一键打包、管理、优化到复杂的技能编辑器、关卡编辑器等功能。
特别需要注意的是由于内容涉及较多且较为复杂会分2节进行详细讨论。在本文的第一部分中我们将总结最基础和最实用的编辑器拓展知识。
本文所有代码均在Gitee参考工程如有需要请自取。 一、扩展Project视图
Project视图是掌握Unity项目的生死大权的地方包括创建、删除等重要操作。在这里我们可以通过右键点击实现Asset菜单的拓展。在进行这项任务之前首先需要将脚本文件保存到名为Editor的文件夹下并引入UnityEditor命名空间。
1、右键扩展菜单Asset
右键创建物体
using UnityEngine;
using UnityEditor;public class AssetEditor
{[MenuItem(Assets/Tools/CreateSphere,false,1)]//数值越小越靠前static void Createxx() {GameObject.CreatePrimitive(PrimitiveType.Sphere);}
}
如图我点击CreateSphere按钮就创建了一个球体到场景当中。
2、监听事件
在大型或规范的项目中通常会有严格的项目规范包括对资源的归类等方面。例如如果你将贴图移动到了脚本文件夹项目可能会判断这样的操作是不合法的并阻止你进行修改。 1、监听资源的删除、创建、移动、保存等操作在进行操作后会输出绑定的委托。 //监听事件[InitializeOnLoadMethod]static void InitializeOnLoadMethod() {EditorApplication.projectChanged delegate (){Debug.Log(怎么回事老弟。你是不是刚动了资源);};}嘿嘿知识点还没完[InitializeOnLoadMethod]写在方法 前则会使该方法在C#代码编译完成后首先调用。 2、当需要重新具体的删除、创建方法时必须继承UnityEditor.AssetModificationProcessor具体方法如下
public class AssetEventEditor : UnityEditor.AssetModificationProcessor
{//监听事件[InitializeOnLoadMethod]static void InitializeOnLoadMethod(){EditorApplication.projectChanged delegate (){Debug.Log(怎么回事老弟。你是不是刚动了资源);};}//监听双击左键打开资源事件public static bool IsOpenForEdit(string assetPath, out string message){message null;Debug.LogFormat(assetPath:{0}, assetPath);return true;//true表示该资源可以打开false表示不允许打开}//监听资源即将被创建事件public static void OnWillCreateEdit(string path){Debug.LogFormat(创建资源的路径:{0}, path);}//监听资源即将被保存事件public static string[] OnWillSaveAssets(string[] paths) {if (paths ! null){Debug.LogFormat(保存资源的路径:{0},string.Join(,,paths));}return paths;}//监听资源即将被移动事件public static AssetMoveResult OnWillMoveAsset(string oldPath,string newPath) {Debug.LogFormat(资源从路径{0}移动到路径{1}, oldPath,newPath);return AssetMoveResult.DidNotMove;//DidNotMove表示可以移动DidMove表示不可以移动}//监听资源即将被删除事件public static AssetDeleteResult OnWillDeleteAsset(string assetPath) {Debug.LogFormat(资源从路径{0}删除, assetPath);return AssetDeleteResult.DidNotDelete;//DidNotDelete表示可以移动DidDelete表示不可以移动}}3、拓展布局
选中资源后出现按钮并监听按钮的点击事件 //选中资源后出现按钮并监听按钮的点击事件[InitializeOnLoadMethod]static void InitializeOnLoadMethod(){EditorApplication.projectWindowItemOnGUI delegate (string guid, Rect selectionRect){//在Project试图中选择一个资源if (Selection.activeObject guid AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(Selection.activeObject))){//设置拓展按钮区域float width 80f;selectionRect.x (selectionRect.width - width);selectionRect.y 2;selectionRect.width width;GUI.color Color.red;//点击事件if (GUI.Button(selectionRect,click)){Debug.LogFormat(点击:{0}, Selection.activeObject.name);}GUI.color Color.white;}};}二、扩展Hierarchy视图
在Hierarchy层次视图中右键点击相当于打开菜单栏的GameObject栏目。
1、拓展菜单GameObject
细心的读者已经看出来了下面代码对比上面写的仅仅将菜单栏目从Assets换成了GameObject。 //右键创建物体[MenuItem(GameObject/Tools/CreateSphere, false, 1)]//数值越小越靠前static void Createxx(){GameObject.CreatePrimitive(PrimitiveType.Sphere);}2、拓展布局
粗心的读者这下也已经看出来了下面的代码复刻了之前的代码将 EditorApplication 后的 GUI 委托修改为 Hierarchy 窗口专属的并将参数从资源的 GUID 变为 instanceID 实例 ID。此外按钮引入了本地图片。在各种插件中编辑器引入图片的操作屡见不鲜有时为了资源规范会整理插件的图标和图片位置别忘了根据实际情况修改相关代码。
3、重写菜单
通过以上学习我们了解了如何在原有基础上扩展编辑器。那么能否完全重写呢当然可以。
1、下面我们将学习如何重新创建 Image 的逻辑。因为在创建 Image 时Unity 默认会自动勾选 RaycastTarget如果我们不需要它具有点击功能就会有额外的性能开销。使用下面的代码我们可以创建不勾选 RaycastTarget 的 Image。 //创建Image默认不勾选RaycastTarget[MenuItem(GameObject/UI/Image0)]static void CreateImage() {if (Selection.activeTransform){if (Selection.activeTransform.GetComponentInParentCanvas()){Image image new GameObject(image).AddComponentImage();image.raycastTarget false;image.transform.SetParent(Selection.activeTransform, false);//设置选中状态Selection.activeTransform image.transform;}}}完整版会有检测视图是否有Canvas组件没有则自动创建等功能。
2、重写菜单 //重写菜单[InitializeOnLoadMethod]static void StartInitializeOnLoadMethod(){EditorApplication.hierarchyWindowItemOnGUI OnHierarchyGUI;}static void OnHierarchyGUI(int instanceID, Rect selectionRect){//Event.current监听当前事件如果监听到鼠标抬起则执行自定义事件也就是我们的自定义菜单if (Event.current ! null selectionRect.Contains(Event.current.mousePosition) Event.current.button 1 Event.current.type EventType.MouseUp){GameObject selectedGameObject EditorUtility.InstanceIDToObject(instanceID) as GameObject;//判断是否满足条件if (selectedGameObject){Vector2 mousePosition Event.current.mousePosition;EditorUtility.DisplayPopupMenu(new Rect(mousePosition.x, mousePosition.y, 0, 0), Window/Test, null);Event.current.Use();}}}[MenuItem(Window/Test/Test1)]static void Test1(){}[MenuItem(Window/Test/Test2)]static void Test2(){}重写完成后右键视图中的实例将会弹出自定义菜单
三、扩展Inspector视图
Inspector检视视图是用来展示组件及资源的详细信息面板。unity自身提供的各类组件的面板能够满足我们正常的需求但我们偶尔会希望在某些面板上添加快捷按钮或者某些逻辑。
1、扩展原生组件
摄像机是典型的原生组件我们CustomEditor进行自定义组件重写OnInspectorGUI在base.OnInspectorGUI()这个原有元素接口上下添加按钮。
using UnityEditor;
using UnityEngine;[CustomEditor(typeof(Camera))]
public class CameraEditor : Editor
{public override void OnInspectorGUI(){if (GUILayout.Button(拓展按钮-上)){}base.OnInspectorGUI(); if (GUILayout.Button(拓展按钮-下)){}}
}
如下便绘制了两个按钮不过要注意该组件限制了按钮必须加在最上面或者最下面。
2、扩展继承组件
1、Unity将大量的Editor绘制方法封装进了DLL通常来讲我们无法调用其中方法。想要解决可以使用反射获取内部对象然后调用想要使用的未公开的方法。
using System.Reflection;
using UnityEditor;
using UnityEngine;[CustomEditor(typeof(Transform))]
public class TransformEditor : Editor
{private Editor m_Editor;private void OnEnable(){m_Editor Editor.CreateEditor(target, Assembly.GetAssembly(typeof(Editor)).GetType(UnityEditor.TransformInspector, true));}public override void OnInspectorGUI(){if (GUILayout.Button(拓展按钮)){}m_Editor.OnInspectorGUI();//原有信息面板// base.OnInspectorGUI();}
}
2、Context菜单 点击组件的设置按钮或鼠标右键会弹出Context菜单里面有Copy、Reset等操作按钮。我们有时候想对特定组件进行自定义的操作例如我想在Transform的Context菜单添加NewContext按钮只需更改MenuItem里第一个参数为CONTEXT/Transform/NewContext接口。想给Camer加就将Transform替换成Camera想给所有组件加就替换成Compoment。 [MenuItem(CONTEXT/Transform/New Context)]static void NewContext(MenuCommand menuCommand){Debug.LogFormat(组件名称:{0},menuCommand.context.name);}下面演示如何重写特定脚本的系统方法。作者建议最好延迟一帧以防止在编辑模式下代码同步出现问题。在测试 Unity 2021 版本时貌似没有出现问题。此外需要注意的是书中的部分接口可能已经过时个人已经替换成最新的版本以2021.3为准。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class ContextScript : MonoBehaviour
{[ContextMenu(Remove Component)]void RemoveComponent(){Debug.Log(RemoveComponent);//等一帧再删除自己防止引擎底层错误UnityEditor.EditorApplication.delayCall delegate () {DestroyImmediate(this);};}
}
脚本中使用宏定义使用宏定义的原因是为了在发布后剔除无效代码联动脚本中的变量在编辑模式下实现功能。下面代码就让NewContext按钮操作了脚本中的变量——将ContextScript脚本中的str变量从原始改成了原神。
using System.Collections;
using System.Collections.Generic;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;public class ContextScript : MonoBehaviour
{public string str 原始;
#if UNITY_EDITOR//宏定义操作脚本变量[MenuItem(CONTEXT/ContextScript/New Context)]static void NewContext(MenuCommand menuCommand){ContextScript contextScript menuCommand.context as ContextScript;contextScript.str 原神;}
#endif
} 四、扩展Scene视图
Unity的Scene视图是一个用于编辑场景的窗口。在Scene视图中你可以直观地查看、编辑和组织你的游戏场景。
1、绘制辅助元素
场景编辑中我们有时会需要线段、不同形状的元素来帮助我们快速编辑。下面我们将使用Gizmos.cs工具类绘制简单元素。
using UnityEngine;public class GizmoScirpt : MonoBehaviour
{ //在鼠标点击到脚本挂载的物体的身上的时候运行private void OnDrawGizmosSelected(){Gizmos.color Color.red;//画线Gizmos.DrawLine(transform.position, Vector3.one);//立方体Gizmos.DrawCube(Vector3.one, Vector3.one);}
} 我们发现未点击挂载脚本的物体时立方体和线条消失了。如果想让绘制的物体一直出现可以使用 OnDrawGizmos 方法。具体用法有很多比如技能范围展示、地形和陷阱的实际范围绘制等都能使项目更高效进行。 //不依赖对象会一直执行private void OnDrawGizmos(){Gizmos.DrawSphere(transform.position, 2.0f);}2、辅助UI
我们在Scene视图中可以在各种组件中添加EditorGUI以获得便利。下面演示如何在Scene中给Camer添加位置信息。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;[CustomEditor(typeof(Camera))]
public class GUIEditor : Editor
{private void OnSceneGUI(){Camera camera target as Camera;if (camera ! null){Handles.color Color.red;Handles.Label(camera.transform.position, camera.transform.position.ToString());Handles.BeginGUI();GUI.backgroundColor Color.red;if (GUILayout.Button(click,GUILayout.Width(200f))){Debug.LogFormat(click {0}, camera.name);}GUILayout.Label(Label);Handles.EndGUI();}}
} 最后注意如果你的脚本不生效可能是该脚本与CameraEditor脚本互斥冲突因为都用了[CustomEditor(typeof(Camera))],默认先创建的脚本会生效。
3、常驻辅助UI
常驻辅助UI或者说固定辅助UI顾名思义无需游戏对象即可常驻Scene视图有些类似OnDrawGizmosSelected和OnDrawGizmos。
using UnityEngine;
using UnityEditor;//常驻辅助UI
public class GUIEditor2 : MonoBehaviour
{[InitializeOnLoadMethod]static void InitializeOnLoadMethod() {SceneView.duringSceneGui delegate (SceneView sceneView){Handles.BeginGUI();GUI.Label(new Rect(0, 0, 50f, 50f), 标题);GUI.Button(new Rect(0, 20f, 50f, 50f), AssetDatabase.LoadAssetAtPathTexture(Assets/unity.png));Handles.EndGUI();};}
}
如下Scene视图左上角多出了一个UI
五、扩展Game视图
通常来讲运行游戏才能执行脚本的生命周期。如果想在非运行模式下也可以执行脚本在脚本上添加[ExecuteInEditMode]那么该脚本可以在编辑模式下生效如果不想在发布后出现可以使用宏定义来剔除。
using UnityEngine;#if UNITY_EDITOR//编辑器模式下依然执行生命周期
[ExecuteInEditMode]
public class GameScript : MonoBehaviour
{private void OnGUI(){if (GUILayout.Button(Click)){Debug.Log(Click);}GUILayout.Label(Click!);}
}#endif总结
累死了本篇详细讲述了 Unity 编辑器的五大视图的拓展方法。原本想分为多篇但为了整体性将其整合在一起。下一篇文章字数减少但会更加深入地探讨并详细解释面板和编辑器源码的相关内容。有不愿透露姓氏的杨姓砖家建议认真阅读完本篇并亲自进行代码试验然后再查看下一篇。 创作不易觉得有用的请大家多点赞、评论、收藏毕竟不收钱甚至说不定因为哪个知识点恰巧能在面试里帮助到你提升你的薪资哈哈。