unity地图编辑器 插件怎么编辑45度场景

4501人阅读
[Unity引擎](86)
今天博主想和大家分享的是Unity3D场景编辑器的扩展开发,相关的话题我们在这篇文章中我们已经有所涉及,今天博主想特别针对场景编辑器的扩展开发来进行下深入研究。对于一个场景编辑器来说,它主要的作用是3D场景视图中实时显示、输入反馈和相关信息的更新。在Unity3D中提供了Editor、EditorWindow、GUILayout、EditorGUILayout、GUIUtility、EditorGUIUtility、Handles、Event等来完成这些工作。其中基于EditorWindow的这种扩展方式我们已经研究过了,这种扩展方式拥有自己的独立窗口使用OnGUI方法进行界面的绘制。今天我们想说的是基于Editor的这种扩展方式,这种扩展方式只能针对脚本,从脚本内容在Inspector里的显示布局到变量在Scene视图的可视化编辑,它都可以完全胜任。这里特别想说的是Handles和Event这两个类,这两个类分别提供了3D显示和输入反馈的功能,我们下面就来学习如何使用这些类来扩展Unity3D的场景编辑器。
创建一个扩展的Transform组件
Transform是Unity3D中一个基本的组件,下面我们来创建一个扩展的Transform组件,该组件可以对游戏体的坐标、旋转、缩放进行重置。首先,我们创建一个ExtendTransform的类,该类继承自Editor类:
using UnityE
using System.C
using UnityE
[CustomEditor(typeof(Transform),true)]
public class ExtendTransform : Editor
Position属性
private SerializedProperty mP
private SerializedProperty mS
void OnEnable()
mPos = serializedObject.FindProperty("m_LocalPosition");
mScale = serializedObject.FindProperty("m_LocalScale") ;
Inspector相关GUI函数
public override void OnInspectorGUI()
EditorGUIUtility.labelWidth = 15;
serializedObject.Update();
DrawPosition();
DrawRotate();
DrawScale();
serializedObject.ApplyModifiedProperties();
private void DrawPosition()
GUILayout.BeginHorizontal();
bool Reset = GUILayout.Button("P", GUILayout.Width(20f));
EditorGUILayout.LabelField("Position");
EditorGUILayout.PropertyField(mPos.FindPropertyRelative("x"));
EditorGUILayout.PropertyField(mPos.FindPropertyRelative("y"));
EditorGUILayout.PropertyField(mPos.FindPropertyRelative("z"));
if(Reset) mPos.vector3Value = Vector3.
GUILayout.EndHorizontal();
private void DrawRotate()
Vector3 eulerAngles = ((Transform)target).eulerA
GUILayout.BeginHorizontal();
bool Reset = GUILayout.Button("R", GUILayout.Width(20f));
EditorGUILayout.LabelField("Rotation", GUILayout.Width(70f));
EditorGUILayout.LabelField("X", GUILayout.Width(13f));
float angleX=EditorGUILayout.FloatField(eulerAngles.x, GUILayout.Width(56f));
EditorGUILayout.LabelField("Y", GUILayout.Width(13f));
float angleY = EditorGUILayout.FloatField(eulerAngles.y, GUILayout.Width(56f));
EditorGUILayout.LabelField("Z", GUILayout.Width(13f));
float angleZ = EditorGUILayout.FloatField(eulerAngles.z, GUILayout.Width(56f));
((Transform)target).eulerAngles = new Vector3(angleX, angleY, angleZ);
eulerAngles = Vector3.
((Transform)target).eulerAngles = Vector3.
GUILayout.EndHorizontal();
private void DrawScale()
GUILayout.BeginHorizontal();
bool Reset = GUILayout.Button("S", GUILayout.Width(20f));
EditorGUILayout.LabelField("Scale");
EditorGUILayout.PropertyField(mScale.FindPropertyRelative("x"));
EditorGUILayout.PropertyField(mScale.FindPropertyRelative("y"));
EditorGUILayout.PropertyField(mScale.FindPropertyRelative("z"));
if (Reset) mScale.vector3Value = Vector3.
GUILayout.EndHorizontal();
首先我们注意到ExtendTransform继承自Editor,这是我们开发这类编辑器扩展的第一个前提。其次我们注意到在该类的声明位置有这样一个标记:
[CustomEditor(typeof(Transform),true)]
该标记表明我们这个编辑器扩展是针对Transform组件进行扩展的,即当物体存在Tranform组件时会在编辑器中响应这个编辑器扩展程序。我们在这个编辑器扩展程序中都做了哪些事情呢?第一,我们实现了OnEnable()方法,该方法相当于一个初始化的方法;第二,我们重写了OnOnInspectorGUI()方法,该方法将覆盖默认的Inspector窗口外观。
好了,现在我们点击场景中默认的相机MainCamera可以发现默认的Transform会变成具有重置功能的扩展型Transform。下面我们来介绍这段程序中较为重要的核心内容:
Unity3D中的可序列化对象
通常我们所说的序列化是指将一个对象的实例转化为字符串的过程,而在Unity3D中可序列化对象更像是一种智能对象,它可以将脚本中的属性显示在Inspector窗口中,当场景发生变化时这些属性值将自动被更新。例如我们可以定义这样一个简单的脚本:
定义一个可序列化类
[System.Serializable]
public class ExampleClass
[SerializeField]
public int ID;
[SerializeField]
public string N
[SerializeField]
public Vector3[] P
private bool editable = false;
定义一个简单的脚本
public class ExampleScript : MonoBehaviour
public ExampleClass E
此时如果我们给场景中的某个物体附加上该脚本,则我们在Inspector窗口可以看到Example类的实例Example将被序列化到编辑器面板中,同时我们可以注意到私有的editable字段并没有被序列化出来,这是因为在Unity3D中,公有的字段默认支持序列化,私有的字段除非显式的增加[SerializeField]标记,否则都不会被序列化,这一点希望大家注意。好了,那么我们为什么要讲这部分内容呢,这是因为它和我们下面要讲的Editor基类中的属性和方法有着十分密切的关联。
Editor基类中的属性和方法
Editor基类中有两个重要的属性,即target和serializedObject。target表示当前受检查的物体我们可以通过它获得当前物体;而serializedObject表示当前物体的全部可序列化信息,我们可以通过它获得指定的序列化字段及其数值。Editor基类中重要的方法有:
* OnInspectorGUI():该方法可对Inspector窗口面板进行扩展或者重写,比如我们可以通过DrawDefaultInspector()方法来绘制默认Inspector窗口面板然后在此基础上使用GUILayout或者EditorGUILayout等辅助类进行自定义的绘制。在这个示例中我们对整个面板进行了重写,值得注意的是为了让Inspector窗口面板正常工作,如果要重绘该窗口请确保对该方法进行覆盖。
* OnSceneGUI():该方法可对场景视图进行绘制,在实际的使用中可以配合Handles类和Event类来进行网格编辑、地形绘制或高级Gizmos等方面的工作。在本文的第二个示例中,我们将利用这一特性来编写一个用于NPC寻路的路径节点编辑工具。
对第一个示例的总结
在第一个示例中,可以注意到我们使用了FindProperty()方法来获取一个可序列化物体的属性(字段),然后我们在EditorGUILayout.PropertyField()方法来绘制了各种属性框,这种方式可以实现属性的自动更新。注意到DrawRotate()方法与DrawPositin()及DrawScale()方法在实现方式上略有不同,这是因为Transform组件的Rotation属性是一个Quaternion即四元数的结构,四元数是利用x、y、z、w四个数值来表示物体的三维旋转,这不仅和我们平时习惯的欧拉角相违背而且更为关键的是貌似目前我还没有发现可以直接绘制四元数的API接口,如果有的话希望大家可以告诉我,所以这里我们用了变通的一种方法,即通过Transform的eulerAngles来实现,但是这种方式绘制的属性框大小和EditorGUILayout.PropertyField()方法绘制的属性框大小并不一致,同时我们需要自己去完成属性值的更新。好了,暂时先总结到这里更多的细节大家可以通过代码来了解。
创建一个NPC寻路节点编辑工具
创建这样一个工具的想法来自我实际的工作体验,当我Unity3D中使用的Tween动画库从iTween变成Dotween后,我在使用Dotween的过程中一直没有找到类似于iTweenPath的路径节点编辑工具。作为一个有节操的程序员,去寻找破解版的Dotween Pro这样的事情我是能不干就不干啦,因为我觉得自己有能力做这样一个类似的小工具,所以在一边准备这篇文章的时候,一边开始设计这样一个路径节点编辑工具。相信经过第一个示例的铺垫和相关知识的储备,大家都了解了这些内容,所以这里直接给出代码啦,因为实在是没有多少内容,嘿嘿:
using UnityE
using System.C
using UnityE
[CustomEditor(typeof(PatrolNPC))]
public class PatrolPathEditor : Editor
private Vector3[]
显示寻路信息的GUI
private GUIStyle style=new GUIStyle();
void OnEnable()
paths = ((PatrolNPC)target).P
style.fontStyle = FontStyle.N
style.fontSize = 15;
void OnSceneGUI()
paths = ((PatrolNPC)serializedObject.targetObject).P
Handles.color = Color.
if(paths.Length &= 0 || paths.Length&2) return;
for (int i = 0; i & paths.L i++)
paths[i] = Handles.PositionHandle(paths[i], Quaternion.identity);
Handles.SphereCap(i, paths[i], Quaternion.identity, 0.25f);
Handles.Label(paths[i], "PathPoint" + i, style);
if (i & paths.Length && i + 1 & paths.Length)
Handles.DrawLine(paths[i], paths[i + 1]);
这里的PatrolNPC是一个可寻路NPC类,基本和这篇文章的内容无关,大家只要知道那个Paths字段是一个Vector3[]就好啦,这样当我们在场景中编辑这些路径节点的时候,对应NPC的路径节点信息就会同步发生更新,这样我们就可以随心所欲地规划NPC的移动路径啦,哈哈。好了,今天的内容就是这样啦,写完熬到这个点真心不容易啊,大家晚安,这是这个小工具在场景编辑器中的效果,嘻嘻,感觉还是蛮不错的吧,反正我是很喜欢就对啦!
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:1181877次
积分:13816
积分:13816
排名:第888名
原创:196篇
评论:1288条
人生到处知何似?应似飞鸿踏雪泥。
(1)(2)(3)(1)(3)(2)(5)(1)(4)(2)(5)(4)(1)(5)(4)(2)(1)(5)(4)(1)(7)(13)(1)(2)(1)(9)(8)(2)(5)(9)(23)(8)(13)(18)(7)(1)(1)(2)(1)(1)(1)(2)(1)(3)(1)Unity教程之-制作闪亮的星星Star(四):Unity Editor编辑器实现Undo
继续上篇文章《》,在Unity中没有一种简单的方法来支持Undo事件,但我们可以做到接近支持。在我们的案例中,我们可以检查ValidateCommand事件是否发生,来判断Undo操作。当前被选中的对象这个事件的目标,我们假设它被修改过。
ValidateCommand is a type of GUI event, which indicates that some special action happened, like undo or redo. So why isn’t it called something like ExecuteCommand? Actually, that command type exists as well. While they have a slightly different meaning, in practice you use them for the exact same purpose. Unfortunately, depening on exactly where you’re checking and how you’re constructing your GUI, either one or the other event happens, but not both. Why this is so, I do not know.
So to be perfectly safe, you have to check for both command types. In this case, however, you can suffice with checking ValidateCommand.
using UnityE
using UnityE
[CustomEditor(typeof(Star))]
public class StarInspector : Editor {
private static GUIContent
insertContent = new GUIContent("+", "duplicate this point"),
deleteContent = new GUIContent("-", "delete this point"),
pointContent = GUIContent.none,
teleportContent = new GUIContent("T");
private static GUILayoutOption
buttonWidth = GUILayout.MaxWidth(20f),
colorWidth = GUILayout.MaxWidth(50f);
private SerializedO
private SerializedProperty
frequency,
private int teleportingE
void OnEnable () { … }
public override void OnInspectorGUI () {
star.Update();
GUILayout.Label("Points");
for(int i = 0; i & points.arrayS i++){
EditorGUILayout.BeginHorizontal();
SerializedProperty point = points.GetArrayElementAtIndex(i);
EditorGUILayout.PropertyField(point.FindPropertyRelative("offset"), pointContent);
EditorGUILayout.PropertyField(point.FindPropertyRelative("color"), pointContent, colorWidth);
if(GUILayout.Button(teleportContent, EditorStyles.miniButtonLeft, buttonWidth)){
if(teleportingElement &= 0){
points.MoveArrayElement(teleportingElement, i);
teleportingElement = -1;
teleportContent.tooltip = "start teleporting this point";
teleportingElement =
teleportContent.tooltip = "teleport here";
if(GUILayout.Button(insertContent, EditorStyles.miniButtonMid, buttonWidth)){
points.InsertArrayElementAtIndex(i);
if(GUILayout.Button(deleteContent, EditorStyles.miniButtonRight, buttonWidth)){
points.DeleteArrayElementAtIndex(i);
EditorGUILayout.EndHorizontal();
if(teleportingElement &= 0){
GUILayout.Label("teleporting point " + teleportingElement);
EditorGUILayout.PropertyField(frequency);
EditorGUILayout.PropertyField(centerColor);
star.ApplyModifiedProperties() ||
(Event.current.type == EventType.ValidateCommand &&
mandName == "UndoRedoPerformed")
((Star)target).UpdateStar();
最后,一个舒服的编辑过程!还有什么需要做吗?在编辑器的右上角有一个齿轮图标能够重置组件。当我们重置Star组件的时候我们的Mesh没有及时更新。
你可以定义Reset方法来监听一个组件的重置。这事Unity为Editor及其子类提供的一个方法。当这个事件发生,我们只要及时更新我们的星星就可以了。
using UnityE
[ExecuteInEditMode, RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
public class Star : MonoBehaviour {
[Serializable]
public class Point { … }
public Point[]
public int frequency = 1;
public Color centerC
private Vector3[]
private Color[]
private int[]
public void UpdateStar () { … }
void OnEnable () { … }
void OnDisable () { … }
void Reset () {
UpdateStar();
OK我们开始写Reset。我们要做什么?我们来试试prefabs?
现在使用prefabs对于我们star并没有太多意义,因为每一个star都拥有自己的独立的Mesh。如果你想使用很多个一样的star,那在建立一个3D模型并且导入Mesh是一个好主意。这样所有的star就共享了同一个Mesh。但假设我们使用prefab,就可以实例化多个同样的star然后我们还能够调整它们。
你只要简单的拖拽一个star从层级视图到项目视图,就能建立一个prefab。对prefab的更新能够影响全部的prefab实例,因为每个prefab的修改都会触发OnDisable和OnEnable。将一个实例回复成prefab同样的状态它依然能够工作。
唯一我们没有完全做好的事情是prefab的MeshFilter会显示它的Mesh类型不匹配。这事因为prefab是一个实际的资源,而动态生成的Mesh不是。这不影响功能,但还是让我们解决它吧。
一个修改后提示类型不匹配的prefab
为了停止prefab生成它的Mesh,我们不能再调用UpdateStar方法。不幸的是,这代表我们将不能再看到预览了。我们可以用PrefabUtility.GetPrefabType方法来检测编辑窗口当前的对象是不是prefab。如果是,我们简单的不更新它就行了。
using UnityE
using UnityE
[CustomEditor(typeof(Star))]
public class StarInspector : Editor {
private static GUIContent
insertContent = new GUIContent("+", "duplicate this point"),
deleteContent = new GUIContent("-", "delete this point"),
pointContent = GUIContent.none,
teleportContent = new GUIContent("T");
private static GUILayoutOption
buttonWidth = GUILayout.MaxWidth(20f),
colorWidth = GUILayout.MaxWidth(50f);
private SerializedO
private SerializedProperty
frequency,
private int teleportingE
void OnEnable () { … }
public override void OnInspectorGUI () {
star.Update();
GUILayout.Label("Points");
for(int i = 0; i & points.arrayS i++){
EditorGUILayout.BeginHorizontal();
SerializedProperty point = points.GetArrayElementAtIndex(i);
EditorGUILayout.PropertyField(point.FindPropertyRelative("offset"), pointContent);
EditorGUILayout.PropertyField(point.FindPropertyRelative("color"), pointContent, colorWidth);
if(GUILayout.Button(teleportContent, EditorStyles.miniButtonLeft, buttonWidth)){
if(teleportingElement &= 0){
points.MoveArrayElement(teleportingElement, i);
teleportingElement = -1;
teleportContent.tooltip = "start teleporting this point";
teleportingElement =
teleportContent.tooltip = "teleport here";
if(GUILayout.Button(insertContent, EditorStyles.miniButtonMid, buttonWidth)){
points.InsertArrayElementAtIndex(i);
if(GUILayout.Button(deleteContent, EditorStyles.miniButtonRight, buttonWidth)){
points.DeleteArrayElementAtIndex(i);
EditorGUILayout.EndHorizontal();
if(teleportingElement &= 0){
GUILayout.Label("teleporting point " + teleportingElement);
EditorGUILayout.PropertyField(frequency);
EditorGUILayout.PropertyField(centerColor);
star.ApplyModifiedProperties() ||
(Event.current.type == EventType.ValidateCommand &&
mandName == "UndoRedoPerformed")
if(PrefabUtility.GetPrefabType(target) != PrefabType.Prefab){
((Star)target).UpdateStar();
prefab没有了Mesh和预览
OK,我们完成了,真的?我没还没有对同时存在多个对象的情况进行支持。试试同时选择多个star。
还没有提供多对象编辑
让我们尝试多对象编辑功能吧。首先,我们需要给类添加一个属性让编辑器提供相应的支持。然后我们需要初始化所有target的SerializedObject,而不再只是一个。我们还需要把任何变化同步到全部的target上。
这样就能在编辑器中支持多个对象了,但如果一些star的point个数不一样,就会出错。因为在Unity的编辑器尝试读取全部点的资料的时候,有些点会不存在。我们可以在获得每个point的数据的时候检查一下这个point是否存在,如果不存在,就停止取值。所以我们只需要显示一个star所拥有的数量的point就可以了。
using UnityE
using UnityE
[CanEditMultipleObjects, CustomEditor(typeof(Star))]
public class StarInspector : Editor {
private static GUIContent
insertContent = new GUIContent("+", "duplicate this point"),
deleteContent = new GUIContent("-", "delete this point"),
pointContent = GUIContent.none,
teleportContent = new GUIContent("T");
private static GUILayoutOption
buttonWidth = GUILayout.MaxWidth(20f),
colorWidth = GUILayout.MaxWidth(50f);
private SerializedO
private SerializedProperty
frequency,
private int teleportingE
void OnEnable () {
star = new SerializedObject(targets);
points = star.FindProperty("points");
frequency = star.FindProperty("frequency");
centerColor = star.FindProperty("centerColor");
teleportingElement = -1;
teleportContent.tooltip = "start teleporting this point";
public override void OnInspectorGUI () {
star.Update();
GUILayout.Label("Points");
for(int i = 0; i & points.arrayS i++){
SerializedProperty
point = points.GetArrayElementAtIndex(i),
offset = point.FindPropertyRelative("offset");
if(offset == null){
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PropertyField(offset, pointContent);
EditorGUILayout.PropertyField(point.FindPropertyRelative("color"), pointContent, colorWidth);
if(GUILayout.Button(teleportContent, EditorStyles.miniButtonLeft, buttonWidth)){
if(teleportingElement &= 0){
points.MoveArrayElement(teleportingElement, i);
teleportingElement = -1;
teleportContent.tooltip = "start teleporting this point";
teleportingElement =
teleportContent.tooltip = "teleport here";
if(GUILayout.Button(insertContent, EditorStyles.miniButtonMid, buttonWidth)){
points.InsertArrayElementAtIndex(i);
if(GUILayout.Button(deleteContent, EditorStyles.miniButtonRight, buttonWidth)){
points.DeleteArrayElementAtIndex(i);
EditorGUILayout.EndHorizontal();
if(teleportingElement &= 0){
GUILayout.Label("teleporting point " + teleportingElement);
EditorGUILayout.PropertyField(frequency);
EditorGUILayout.PropertyField(centerColor);
star.ApplyModifiedProperties() ||
(Event.current.type == EventType.ValidateCommand &&
mandName == "UndoRedoPerformed")
foreach(Star s in targets){
if(PrefabUtility.GetPrefabType(s) != PrefabType.Prefab){
s.UpdateStar();
多对象编辑
在场景中编辑 Editing in the Scene View
现在我们拥有了一个很不错的编辑器了,但如果我们能直接在场景里编辑这些point会不会更酷一些?用OnSceneGUI事件,我们可以做到。这个方法会在一个对象被选中即将赋予target时调用。我们不能在这个事件中使用SerializedObject。事实上,你可以认为这个方法与我们编辑器类中的其它部分是完全分离的。
Why does OnSceneGUI mess with target?
Probably for backwards compatibility. Multi-object editing was introduced in Unity 3.5. Versions before that only had the target variable.
using UnityE
using UnityE
[CanEditMultipleObjects, CustomEditor(typeof(Star))]
public class StarInspector : Editor {
private static GUIContent
insertContent = new GUIContent("+", "duplicate this point"),
deleteContent = new GUIContent("-", "delete this point"),
pointContent = GUIContent.none,
teleportContent = new GUIContent("T");
private static GUILayoutOption
buttonWidth = GUILayout.MaxWidth(20f),
colorWidth = GUILayout.MaxWidth(50f);
private SerializedO
private SerializedProperty
frequency,
private int teleportingE
void OnEnable () { … }
public override void OnInspectorGUI () { … }
void OnSceneGUI () {}
让我们设置一个方形的小手柄在star全部的point上面。我们只要在这些point的第一个重复周期里显示手柄就可以了,不需要把全部的重复周期都显示出来。放置这些手柄就好象生成Mesh一样,除了我们使用的是世界坐标系,不是本地坐标系,所以我们要用到star的transform。
我们可以通过Handles.FreeMoveHandle方法来绘制我们的手柄。首先,需要一个世界坐标系的位置,手柄的位置。其次,需要一个绘制手柄的角度,但我们不需要旋转。然后,还需要手柄的尺寸,我们用一个很小的尺寸就够了。我们用一个vector来保存这个尺寸,可以设置成(0.1, 0.1 0.1)。最后一个参数是定义手柄的形状。
How do we convert to world space?
You convert a point from local to world space by appling all transformation matrices of its object hierarchy to it. Unity takes care of this when rendering the scene, but sometimes you need to do it yourself. You can use the Transform.TransformPoint method for this.
using UnityE
using UnityE
[CanEditMultipleObjects, CustomEditor(typeof(Star))]
public class StarInspector : Editor {
private static Vector3 pointSnap = Vector3.one * 0.1f;
private static GUIContent
insertContent = new GUIContent("+", "duplicate this point"),
deleteContent = new GUIContent("-", "delete this point"),
pointContent = GUIContent.none,
teleportContent = new GUIContent("T");
private static GUILayoutOption
buttonWidth = GUILayout.MaxWidth(20f),
colorWidth = GUILayout.MaxWidth(50f);
private SerializedO
private SerializedProperty
frequency,
private int teleportingE
void OnEnable () { … }
public override void OnInspectorGUI () { … }
void OnSceneGUI () {
Star star = (Star)
Transform starTransform = star.
float angle = -360f / (star.frequency * star.points.Length);
for(int i = 0; i & star.points.L i++){
Quaternion rotation = Quaternion.Euler(0f, 0f, angle * i);
Vector3 oldPoint = starTransform.TransformPoint(rotation * star.points[i].offset);
Handles.FreeMoveHandle(oldPoint, Quaternion.identity, 0.04f, pointSnap, Handles.DotCap);
在场景中添加了控制手柄
现在还有什么可以做到更好吗?你可以点击一个手柄,让它变成黄色。我们需要比较一个手柄的初始化位置和返回位置。如果不同,说明用户拖动了手柄,我们需要将改变同步到star。star的Mesh使用本地坐标系,在把坐标改变保存之前,不要忘记转换坐标。
How do we convert to local space?
You have to perform the exact opposite steps for converting to world space, in reverse order. You can use the Transform.InverseTransformPoint method for this. Note that when going to world space we rotated in local space first, then transformed. So to convert back, we inverse transform first, then inverse rotate in local space.
using UnityE
using UnityE
[CanEditMultipleObjects, CustomEditor(typeof(Star))]
public class StarInspector : Editor {
private static Vector3 pointSnap = Vector3.one * 0.1f;
private static GUIContent
insertContent = new GUIContent("+", "duplicate this point"),
deleteContent = new GUIContent("-", "delete this point"),
pointContent = GUIContent.none,
teleportContent = new GUIContent("T");
private static GUILayoutOption
buttonWidth = GUILayout.MaxWidth(20f),
colorWidth = GUILayout.MaxWidth(50f);
private SerializedO
private SerializedProperty
frequency,
private int teleportingE
void OnEnable () { … }
public override void OnInspectorGUI () { … }
void OnSceneGUI () {
Star star = (Star)
Transform starTransform = star.
float angle = -360f / (star.frequency * star.points.Length);
for(int i = 0; i & star.points.L i++){
Quaternion rotation = Quaternion.Euler(0f, 0f, angle * i);
oldPoint = starTransform.TransformPoint(rotation * star.points[i].offset),
newPoint = Handles.FreeMoveHandle
(oldPoint, Quaternion.identity, 0.04f, pointSnap, Handles.DotCap);
if(oldPoint != newPoint){
star.points[i].offset = Quaternion.Inverse(rotation) *
starTransform.InverseTransformPoint(newPoint);
star.UpdateStar();
我们可以在场景中编辑了
有用了!不过我们还没支持Undo!这里我们不能靠SerializedObject来解决问题,不过幸好这些手柄可以支持Undo。我们只需要告诉编辑器哪个对象被改变了,我们还应该为这次改变起一个名字。我们可以用Undo.SetSnapshotTarget来做这些事。
What’s a snapshot?
If an undo step would be created for each GUI event, dragging a handle would result in an undo history filled with dozens of tiny modifications. Instead, the handles make a copy – a snapshot – of the object when movement begins and only register a single undo step with the copy when movement ends. SetSnapshotTarget tells the handles which object to use for this.
All Unity editor GUI elements essentialy do the same thing, whether it’s for draggin handles, sliding numbers, typing text, or whatever.
using UnityE
using UnityE
[CanEditMultipleObjects, CustomEditor(typeof(Star))]
public class StarInspector : Editor {
private static Vector3 pointSnap = Vector3.one * 0.1f;
private static GUIContent
insertContent = new GUIContent("+", "duplicate this point"),
deleteContent = new GUIContent("-", "delete this point"),
pointContent = GUIContent.none,
teleportContent = new GUIContent("T");
private static GUILayoutOption
buttonWidth = GUILayout.MaxWidth(20f),
colorWidth = GUILayout.MaxWidth(50f);
private SerializedO
private SerializedProperty
frequency,
private int teleportingE
void OnEnable () { … }
public override void OnInspectorGUI () { … }
void OnSceneGUI () {
Star star = (Star)
Transform starTransform = star.
//Undo.SetSnapshotTarget(star, "Move Star Point");
Undo.RecordObject(star, "Move star transform");
float angle = -360f / (star.frequency * star.points.Length);
for(int i = 0; i & star.points.L i++){
Quaternion rotation = Quaternion.Euler(0f, 0f, angle * i);
oldPoint = starTransform.TransformPoint(rotation * star.points[i].offset),
newPoint = Handles.FreeMoveHandle
(oldPoint, Quaternion.identity, 0.04f, pointSnap, Handles.DotCap);
if(oldPoint != newPoint){
star.points[i].offset = Quaternion.Inverse(rotation) *
starTransform.InverseTransformPoint(newPoint);
star.UpdateStar();
&&使用Ctrl + z就可以使用了
这样,我们终于完成了!一个有趣的设计过程,不是吗?下面是工程下载地址!
看过本文的人也看了:
我要留言技术领域:
取消收藏确定要取消收藏吗?
删除图谱提示你保存在该图谱下的知识内容也会被删除,建议你先将内容移到其他图谱中。你确定要删除知识图谱及其内容吗?
删除节点提示无法删除该知识节点,因该节点下仍保存有相关知识内容!
删除节点提示你确定要删除该知识节点吗?}

我要回帖

更多关于 unity3d地图编辑器 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信