unity3d的方便之处在于,它很容易地扩展编辑器套件。每款游戏都对加工有着不同的需求,可以快速地以完全集成的方法来构建这些内容并极大地提升开发速度。 目前有大量复杂的软件包提供以基本Unity功能套件为基础的复杂工具,从视觉脚本编辑器到编辑器内导航网格生成。但是,有关如何自行构建此类事物的程序说明却很少。我将在下文列举某些在自己的工作中总结的编辑器定制相关信息。 Unity-Window(from gamasutra) 如何构建编辑器脚本 因为你不想在游戏中包含所有的编辑器定制,而且你也不想游戏对某些Unity编辑器内的东西有所依赖,所以Unity将运行时间和编辑器代码放置在单独的编译中。 在编辑命令中,运行时间代码在编辑器代码之前执行,这样编辑器类型就可以可靠地联系至运行时间组件(游戏邦注:否则就会变得难以编辑),但是你的运行时间组件并不涉及任何编辑器代码。 你必须维持严格的层次。Unity 3.4版本中这个方面做得更加具体,现在其产生的项目文件与其提供的4个编辑阶段相对应,这样就不会混淆文件的构建时间。 在某个点上的程序说明有些不太清楚。当我首次开始使用时,我认为需要在我的项目上创建单个“Editor”文件夹,然后把所有的编辑器类型放入其中。事实上,系统的灵活性要更高些,你可以在项目中创建任意数量的“Editor”文件夹,将其埋藏在“资产”文件夹的任何地方,所有这些都可以存放编辑器代码。 所以,现在通常情况下我会以功能(游戏邦注:比如命名为“AI”)为单位来创建文件夹,然后纳入所有功能相关组件,然后在旁边放上Editor文件夹(游戏邦注:比如命名为“AI/Editor”),装上所有运行这些组件的编辑器扩展。 只要Unity的内在类型能够发挥作用,运行时间类型都会存在于UnityEngine命名空间中,而所有的编辑器类型都会存在于UnityEditor命名空间里。 unity-projectlist(from gamasutra) UnityEditor.Editor类 到目前为止,我设立的最普遍的定制是一个自定义检查器。Unity的Inspector面板提供看到组件状态的窗口,但是这种基本设置只能理解有限的类型,而且只能展示公共区域。 自定义检查器让你可以完全控制用户查看和编辑你的组件的方式。比如,它们可以让你呈现只读资产、强迫性价值限制或只改变选项呈现的方式。 Unity中的Inspector都是Editor类的子类别,所以你应该从这里开始。但是,我对编辑器类处理样式的方法不是很喜欢。里面有个“Target”用来提及检查器正在编辑的物体,但是只是基本的“Object”样式,所以你要不断将其转变成更有用的样式。为避开这个问题,我使用了一个非常简单的类别,具体如下: public class InspectorBase : Editor where T : UnityEngine.Object { protected T Target { get { return (T) target; } } } 现在,如果我想要为MyCustomComponent创造检查器,我就可以从InspectorBase得到检查器,然后使用“Target”,这样我就不用时常更改了。 应当注意的是,你还需要将CustomEditor属性附到检查器类中,Unity才能够真正使用它们。 编辑器GUI 一旦你创造自定义检查器后,你通常想要执行的方法就是OnInspectorGUI()。OnInspectorGUI()可用来指定在检查器中展示的所有东西,使用的是Unity的GUI系统。 因为这是编辑器代码,我们可以使用UnityEditor命名空间中的类型,这包括EditorGUILayout。EditorGUILayout使得了大量的简单控制,可以在编辑器中使用,比Unity普通运行时间GUI系统提供的更好。比如,假如我想向用户展示进入3D位置的领域,我可以使用EditorGUILayout.Vector3Field():Target.somePosition = EditorGUILayout.Vector3Field(“Some position”, Target.somePosition)。 在检查器中产生的效果如下图所示: unity-vec3field(from gamasutra) 正因为GUI系统能够发挥作用,所以如果我改变UI中的值,Vector3Field就会传回新的值,Target.somePosition就会得到更新。在将其指派给目标之前,你可以自由改变值(游戏邦注:比如将值定义在某个范围内),你也可以完全忽略传回的值。 值并不一定来自于域,你可以曝光检查器中的资产,可以采用调用一个功能来获得当前值并使用另一个功能来保存。 当然,Unity会默认处理这个事情。如果你只是想要在Unity已经展示的为基础来构建,你就不必要重新执行所有那些域。Editor有个DrawDefaultInspector()方法,告诉Unity调用所有通常调用的控制,但是在这个过程完成之后,你仍然有机会添加额外域和按键。 说到按键,EditorGUILayout的用途确实很广泛,但是你或许已经注意到存在漏洞。比如,如果我想要在导航网格组件上添加“重新计算”按键,这又会怎么样呢?技巧在于EditorGUILayout仍然构建于常规运行时间GUILayout之上,所以你还是可以使用GUILayout中的所有东西。 你对检查器中的域做出改变并且为目标物体的域指派新值时,Unity会察觉到你正在改变物体,所以下次保存屏幕或项目时就会将其写入磁盘。这种察觉是有限的,它只能识别公共资产的直接指派。如果你通过资产或调用方法来修改目标物体,你可能就需要自行调用EditorUtility.SetDirty了。 扩展组件背景菜单 在测试时,有时手动引发某些行为还是很有用的。你可以通过在自定义检查器上安放按键来触发行为:if(GUILayout.Button(“Explode now!”)) Target.ExplodeNow()。 但是还有个更加简单的方法,这个方法完全不需要自定义检查器。你可以使用的是UnityEngine.ContextMenu属性: /* In the target class… */ [ContextMenu("Explode now!")] public void ExplodeNow() { … } 右键点击组件的检查器(游戏邦注:无论是否自定义化),你会看到背景菜单,其中有额外的功能。可以快速地进行测试。 扩展主菜单 到这里为止,我所说的所有东西都是围绕某个特别组件为中心的定制。其他种类的扩展又会如何呢? 在我的游戏中,动画系统将其资产存放在文件夹架构中,这样每个文件夹都对应enum的一个入口。当我改变enum时,如果可以同步文件夹结构会起到很大作用,添加任何丢失的文件夹并删除 任何多余的文件夹。所以我采用了以下较为简单的方法: public class AnimationSystem{ public static void SyncFolderS***cture() { … } } 但是我要何时以及如何调用呢?我采用的做法是将其连同到Assets菜单中的菜单项目中,使用MenuItem属性: [MenuItem("Assets/Sync folder s***cture")] public static void SyncFolderS***cture() { … } 点击菜单项目就可以调用功能。应当注意的是,功能需要是静态的,但是其中的类可以是多种类型的。 Wizards Editor GUI元素并不一定要在Inspector中。它还可以创造主观编辑器窗口,可以像任何Unity内置窗口那样一动,而且可以像在检查器中那样使用GUI命令。最简单的方法就是使用ScriptableWizard,这很像一个对话盒。你在呈现后设定某些值,然后点击按键让其施展“魔法”。 unity-ragdollwizard(from gamasutra) 在默认情况下,ScriptableWizard的作用很像检查器:类中的任何公共域都会自动呈现在wizard窗口中。你的wizard会像一大串公共域那样简单,而且还有个OnWizardCreate()方法,当用户点击“Create”按键时Unity就会调用这个方法。而且,你可以改变按键上的文字,“Apply”或“OK”之类的会显得更加直观。 wizard的另一个层面是决定用户如何开启,常用方法是使用有静态功能的菜单选项,如上图所示: [MenuItem("GameObject/Create Other/Explosion")] public static void CreateExplosion() { ScriptableWizard.DisplayWizard(“Create explosion”); } |
关于如何对unity3d编辑器进行扩展的教程
最新推荐文章于 2024-07-10 18:33:32 发布