unity3d 怎么用代码创建unity获取gameobjectt对象

【Unity3D基础教程】给初学者看的Unity教程(一):GameObject,Compoent,Time,Input,Physics作者:王选易,出处:/neverdie/ 欢迎转载,也请保留这段声明。如果你喜欢这篇文章,请点推荐。谢谢!Unity3D重要模块的类图最近刚刚完成了一个我个人比较满意的小项目:【深入Cocos2d-x】使用MVC架构搭建Four,在这个游戏中,我使用了自己搭建的MVC架构来制作一个游戏,做到了比较好的SoC(关注点分离)。但是苦于Cocos2d-x没有一个比较完善的编辑器,所以我开始学习另一个非常流行的游戏引擎-Unity3D。Unity3D是一个Component-Based的游戏引擎,并且为GamePlay ogrmmer提供了很多游戏性层上的支持。比如可以在图形界面上设计动画状态转换的Animator。比如可以直接在场景编辑器中方便进行调整的Collider。比如可以动态调整动画曲线的Animation。总的来说,Unity是一个架构比Cocos2d-x精巧许多的游戏引擎。但是很遗憾的是,Unity本身并不开源,还好,Unity在不开源的情况下却做了比较详尽的文档支持。同时,Unity的社区也是很友好的,stackoverflow也有许多值得一看的问题。顺便推荐几个学习Unity的网站:Unity圣典Unity的官方文档Unity的知乎话题我在学Unity3D的知识的时候,发现Unity内部涉及的重要类比Cocos2d-x要多,有点理不清的感觉,所以制作了下面的设计类图来为像我一样的初学者提供一个Unity的初步印象。GameObject和Component由于Unity是一个Component-Based的游戏引擎,所以游戏中所有的物体都是一个GameObject,为了给这个GameObject附加上各种各样的属性,所以我们引入了Component这个概念。GameObject是由Component组合成的,Component的生命周期和GameObject息息相关。一旦GameObject的Destroy方法,它的子对象和对应的所有Component都会被销毁,同时,我们也可以一次只销毁一个单独的Component。Component有如下这些种类,我制作了一张表格来记录它们的用途:附属于游戏物体.把一个 Renderer (渲染器)组件附到游戏对象,可以使游戏对象显示到场景,附一个 Camera (摄像机)可以把物体变成一个摄像机物体.所有脚本都是组件,因此都能附到游戏对象上.常用的组件可以通过简单的成员变量取得:附在游戏对象上的组件或脚本可以通过GetComponent获取.如下代码示例:using UnityEusing System.Cpublic class example : MonoBehaviour {
void Awake() {
transform.Translate(0, 1, 0);
GetComponent&Transform&().Translate(0, 1, 0);
}}Input和InputManager关于Input的深入解读请参考这篇文章:Input 输入Unity支持,键盘,操纵杆和游戏手柄输入。在输入管理器(Input Manager)可以创建虚拟轴和按钮,并终端用户可以在屏幕配置对话框配置键盘输入。如果想添加新的虚拟轴,选择菜单Edit-&Project Settings-&Input menu。这里可以改变每个轴的设置。即可进入Input Manager的配置界面。从脚本,所有虚拟轴通过它们的名字(name)来访问。当创建时,每个项目都具有下面的默认输入轴:Horizontal and Vertical are mapped to w, a, s, d and the arrow keys. 水平和垂直被映射到w, a, s, d键和方向键 Fire1, Fire2, Fire3 are mapped to Control, Option (Alt), and Command, respectively.
Fire1, Fire2, Fire3被分别映射到Ctrl,Option(Alt)和Command键 Mouse X and Mouse Y are mapped to the delta of mouse movement.
Mouse X 和 Mouse Y被映射到鼠标移动增量 Window Shake X and Window Shake Y is mapped to the movement of the window.
Window Shake X 和 Window Shake Y 被映射到窗口的移动 TimeTime类是Unity中的一个全局变量,它记载了和游戏相关的时间,帧数等数据。Time 类包含一个非常重要的变量叫deltaTime.这个变量包含从上次调用Update 或FixedUpdate到现在的时间(根据你是放在Update函数还是FixedUpdate函数中).(另注: Update每帧调用一次)依照上面的例子,使得物体在一个匀速的速度下旋转,不依赖帧的速率,如下:using UnityEusing System.Cpublic class example : MonoBehaviour {
void Update() {
transform.Rotate(0, 5 * Time.deltaTime, 0);
}}当然了,在使用Time这个类的时候,我们也要记住使用各种各样的Lerp函数来减少自己的工作量,在Unity3D中,Vector3,Vector2,Color等类都提供了相应的Lerp函数给我们调用。Physics和TransformPhysics类是Unity重的一个工具函数类,它主要提供了Linecast和Raycast两种射线投射方式。其中Linecast是以投射的起始位置和终止位置为参数,来判断这个投射有没有和某个Collider发生了碰撞。而Raycast则是以投射的起始位置和投射方向为参数,来判断这个投射有没有和某个Collider发生了碰撞。相应的实例可以看下面的这一段程序:using UnityEusing System.Cpublic class Example : MonoBehaviour {
void Update() {
// 使用Raycast
Vector3 fwd = transform.TransformDirection(Vector3.forward);
if (Physics.Raycast(transform.position, fwd, 10))
print("There is something in front of the object!");
// 使用Linecast
if (!Physics.Linecast(transform.position, target.position))
ProcessData.AndDoSomeCalculations();
}}在Physics这个模块中,有三个Component是至关重要的,分别是RigidBody,Collision,Joint。在新的版本中,又引入了RigidBody2D,Collision2D,Joint2D这些Component来处理2D中的Physics事件。这三个类都是处理物理相关的事件的,那么它们有什么区别呢?RgidBody是作为一个受力物体而存在的,所以可以向一个RigidBody施加Force(力),Drag(阻力)。同时RigidBody还有 velocity (速度),mass(质量),position(位置),旋转(rotation)等等。Collider是为了处理物理中的碰撞事件而出现的类,就像上面表格中所说的,如果没有Collider,两个RigidBody之间是无法发生碰撞的。对同一个GameObject可以绑定多个Collider构建更加复杂的碰撞体结构。Collider另外一个很值得注意的就是我们可以为Collider设置material,即Collider的物理材质。 物理材质用于调整摩擦力和碰撞单位之间的反弹效果。当发生碰撞时,会触发毁掉函数OnCollisionEnter,OnCollisionStay,OnCollisionExit等等。这几个函数与OnTriggerXXX的区别会在接下来的博客中提到。Joint用于连接两个RigidBody,当Joint断掉的时候会触发OnJointBreak的回调函数。总结本来想把类图重的各种类的用法都说一说,但限于篇幅有限,今天就只写了这几个类,那么请期待我的下一篇博客 - MonoBehaviour的前世今生
优质网站模板Unity3d开发中如何创建Inspecter的对象
Unity3d开发中如何创建Inspecter的对象
Unity3d开发中如何创建Inspecter的对象
浏览次数:784
3天前浏览次数:28
3天前浏览次数:21
3天前浏览次数:16
3天前浏览次数:16
浏览次数:79
浏览次数:393
浏览次数:253
浏览次数:249
如果你对以下课程意犹未尽,,查看全部课程
HTML5全栈开发
HTML5最新课程
156 人在学
c#编程概述
C#快速入门
简单又好玩
120 人在学
没有账号?
s后重新发送
已有账号?
已有账号?
验证码确认
话题标题:
400-877-8190
登录后反馈[译]如何在Unity编辑器中添加你自己的工具
在这篇教程中你会学习如何扩展你的Unity3D编辑器,以便在你的项目中更好的使用它。你将会学习如何绘制你自己的gizmo,用代码来实现创建和删除物体,创建编辑器窗口,使用组件,并且允许用户撤销他们所作出的任何动作,这些全部都是用编辑器脚本来实现的。
这篇教程假设你已经熟悉Unity的基本工作流程。如果你知道如何在编辑器中创建物体、预设、场景并且知道如何移动它们,知道如何添加组件,那么你可以开始本教程的学习了。
最终结果预览
让我们看一下我们做出的最终结果是什么样子:
如你所见,我们会创建一个编辑器窗口,里面有一个颜色拾取器,我们可以用所选取的颜色来绘制网格。我们也能够创建和删除物体,把他们对齐到网格,并且能够撤销这些动作。
Step 1: Gizmos
首先我们来学习如何使用gizmo。这里有一些内置gizmo的例子:
你可能会经常在Unity中看到这个gizmo,因为它会为每一个拥有transform组件的物体绘制,因此基本上每一个被选中的物体都会有这个gizmo。
这是另一个gizmo,它能够让我们知道绑定在我们游戏对象上的BoxCollider的大小。
Step 2: 创建一个Grid脚本
创建一个C#脚本Grid.cs,我们用它来为一个物体绘制我们自己的gizmo;我们会在编辑器中绘制一个简单的网格(Grid)来作为一个例子。
using UnityE
using System.C
public class Grid : MonoBehaviour
void Start()
void Update()
对于一个网格,我们需要添加2个变量:width和height
public class Grid : MonoBehaviour
public float width = 32.0f;
public float height = 32.0f;
void Start()
void Update()
为了在编辑器中绘图,我们需要使用OnDrawGizmo回调函数,因此让我们创建它。
public class Grid : MonoBehaviour
public float width = 32.0f;
public float height = 32.0f;
void Start()
void Update()
void OnDrawGizmos()
Step 3: 绘制网格(Grid)
要绘制一个网格,我们需要一系列水平线和垂直线,并且还要知道编辑器摄像机(也叫场景摄像机)的当前位置以便我们知道我们应该把我们的网格绘制在什么地方。首先,把摄像机的位置保存在一个单独的变量中
void OnDrawGizmos()
Vector3 pos = Camera.current.transform.
如你所见,我们可以使用Camera.current引用来获取编辑器摄像机。
现在,我们需要2个for循环来绘制水平线和垂直线。
void OnDrawGizmos()
Vector3 pos = Camera.current.transform.
for (float y = pos.y - 800.0f; y & pos.y + 800.0f; y += height)
Gizmos.DrawLine(new Vector3(-f, Mathf.Floor(y/height) * height, 0.0f),
new Vector3(f, Mathf.Floor(y/height) * height, 0.0f));
for (float x = pos.x - 1200.0f; x & pos.x + 1200.0f; x += width)
Gizmos.DrawLine(new Vector3(Mathf.Floor(x/width) * width, -f, 0.0f),
new Vector3(Mathf.Floor(x/width) * width, f, 0.0f));
我们使用Gizmos.DrawLine()来绘制线。注意Gizmos类有很多绘制API,因此绘制一个cube或者sphere甚至是它们的线框这样的几何图元(primitive)都不成问题。如果需要,你也可以绘制一幅图片。
网格线应该是无限长的,但是float.positiveInfinity和float.negativeInfinity似乎在绘制直线时不能正常工作,因此我们只是简单的放置一个任意的大数来替代它。同时,线的数量严格取决于我们在for循环中使用的常数;技术上来说,我们不应该在代码中放置这样的常数,但这仅仅是一个测试代码,所以无视它就好。
要观察网格,你需要创建一个空物体,然后把我们的脚本Grid.cs绑定到它上面:
Step 4: 创建一个自定义的Inspector
接下来的工作就是自定义Inspector。我们需要创建一个编辑器脚本来做这件事。创建一个新的C#文件,命名为GridEditor。这个脚本需要放置在Editor文件夹中;如果你还没有一个Editor文件夹,那么现在就创建它吧。
using UnityE
using UnityE
using System.C
[CustomEditor (typeof(Grid))]
public class GridEditor : Editor
这一次我们也需要using UnityEditor以便我们能够使用编辑器的类和函数。为了覆盖我们的Grid物体的默认inspector,我们需要在我们的类声明之前添加一个属性,[CustomEditor(typeof(Grid))]会告诉Unity我们想要自定义Grid的inspector。为了能够使用编辑器的回调函数,我们需要继承Editor类而不是MonoBehaviour类。
要改变当前inspector,我们需要覆盖掉旧的那个。
public class GridEditor : Editor
public override void OnInspectorGUI()
现在如果你检查编辑器中的Grid物体的inspector,你会发现即便它有一些public成员,inspector里面也是空空如也。这是因为通过覆盖OnInspectorGUI()我们丢弃了默认的inspector,取而代之的是一个自定义的inspector。
Step 5: 使用GUILayout来填充我们的自定义Inspector
在我们创建任何字段(field)之前,我们需要取得自定义inspector所对应的那个对象的引用。我们实际上已经有了该对象的引用&&它的名字叫target&&但是为了方便我们会创建一个该对象上Grid组件的引用。首先,让我们先声明它。
public class GridEditor : Editor
我们应该在OnEnable()函数中为它赋值,该函数会在inspector可用时调用。
public class GridEditor : Editor
public void OnEnable()
grid = (Grid)
现在,让我们为inspector创建一些字段。我们会使用GUILayout和EditorGUILayout类来做这件事。
public override void OnInspectorGUI()
GUILayout.BeginHorizontal();
GUILayout.Label("Grid Width");
grid.width = EditorGUILayout.FloatField(grid.width, GUILayout.Width(50));
GUILayout.EndHorizontal();
第一行的GUILayout.BeginHorizontal()表示,我们想要把接下来的inspector元素彼此从左到右放置。最后一行GUILayout.EndHorizontal(),如你所想的那样,表示我们不想再那样做了。实际添加的项目位于这两行之间。第一个添加的是一个简单的标签(在我们的案例中,它会显示为Grid Width文本),紧挨着它,我们创建了一个EditorGUILayout.FloatField,如你所想的那样,它是一个float字段。注意我们把FloatField的值赋给了grid.width,而FloatField本身又显示grid.width的值。我们设置它的宽度为50像素。
让我们看看字段是否正确地添加到了inspector:
Step 6: 填充inspector并且重绘场景
现在让我们添加另一个项目到inspector中;这一次它是grid.height
public override void OnInspectorGUI()
GUILayout.BeginHorizontal();
GUILayout.Label("Grid Width");
grid.width = EditorGUILayout.FloatField(grid.width, GUILayout.Width(50));
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Label("Grid Height");
grid.height = EditorGUILayout.FloatField(grid.height, GUILayout.Width(50));
GUILayout.EndHorizontal();
这就是我们Grid对象的全部字段了,如果你想知道在inspector中还可以使用哪些字段和项目,你可以查看Unity手册中的EditorGUILayout和GUILayout类。
注意我们在新inspector中所作出的改变,只有当我们选中了场景视图窗口之后才会可见。为了让它在作出改变之后立即可见,我们可以调用SceneView.RepaintAll()
public override void OnInspectorGUI()
GUILayout.BeginHorizontal();
GUILayout.Label("Grid Width");
grid.width = EditorGUILayout.FloatField(grid.width, GUILayout.Width(50));
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Label("Grid Height");
grid.height = EditorGUILayout.FloatField(grid.height, GUILayout.Width(50));
GUILayout.EndHorizontal();
SceneView.RepaintAll();
现在,我们不必再点击inspector的外面就可以看到变化后的结果了。
Step 7: 处理编辑器输入
现在,让我们尝试处理编辑器的输入,就像我们在游戏中所做的那样。任何按键或鼠标状态对我们都可用。为了取得这样的效果,我们需要在我们的SceneView中添加一个onSceneGUIDelegate回调函数。让我们调用我们自己的更新函数GridUpdate().
public void OnEnable()
grid = (Grid)
SceneView.onSceneGUIDelegate = GridU
void GridUpdate(SceneView sceneview)
现在,我们只需获取输入事件(input Event)
void GridUpdate(SceneView sceneview)
Event e = Event.
Step 8: 创建一个预设(Prefab)
为了进一步把弄编辑器脚本,我们需要一个能够使用的游戏对象。让我们创建一个简单的cube并把它做成prefab。
你可以调整网格的大小来匹配cube或者相反(调整cube的大小来匹配网格),最终让cube和网格对齐。
如你所见,Hierarchy面板中的cube文本变成蓝色的,这意味着它链接到了一个prefab。你可以在Project面板中看到那个预设(prefab)。
Step 9: 从编辑器脚本中创建一个物体
现在,我们将从编辑器脚本中创建一个物体。让我们回到我们的GridEditor.cs中来扩展我们的GridUpdate()函数。
当键盘上的A键被按下时,我们就创建一个物体。
void GridUpdate(SceneView sceneview)
Event e = Event.
if (e.isKey && e.character == 'a')
如你所见,我们简单地检查一下触发的事件是否是一个按键的状态改变事件并且按下的那个按键的字符是否是'a'。然后我们为我们的新物体创建了一个引用。现在,让我们实例化它。
void GridUpdate(SceneView sceneview)
Event e = Event.
if (e.isKey && e.character == 'a')
if (Selection.activeObject)
obj = (GameObject)Instantiate(Selection.activeObject);
obj.transform.position = new Vector3(0.0f, 0.0f, 0.0f);
Selection.activeObject是编辑器中当前所选中物体的一个引用。如果选中任何一个物体,那么我们就简单地克隆它,并把克隆体的位置设置为(0,0,0).
让我们测试一下它是否正常工作。你必须知道的一件事:无论什么时候我们的资源(assets)被重新导入/刷新,我们的GridUpdate()都会停止工作。(这通常都是当我们修改完代码后回到编辑器中时)这时候,你可以选中编辑器脚本所指向的对象(例如在Hierarchy视图中选中该对象)&在我们的例子中它是Grid物体,然后重新激活一下就可以了。另一件你需要知道的事情是输入事件只有在场景视图被选中的情况下才会被捕捉到。(也就是场景视图获得焦点)
Step 10: 从编辑器脚本中实例化一个预设(prefab)
尽管我们想方设法想要克隆对象,但克隆的对象所链接的那个预设(prefab)并不存在。
正如你所看到的,Cube(Clone)物体的名字被显示成纯黑色字体,也就意味着它并没有链接到原始立方体所链接的那个预设上。如果我们在编辑器中手动复制(duplicate)那个原始的立方体,那么克隆的立方体将会链接到Cube预设上。为了让它按这样的方式为我们工作,我们需要使用InstantiatePrefab()函数,该函数来自EditorUtility类。
在我们使用该函数之前,我们需要取得被选中物体的预设(prefab)。我们可以使用GetPrefabParent()函数来做这件事,该函数同样来自于EditorUtility类。
void GridUpdate(SceneView sceneview)
Event e = Event.
if (e.isKey && e.character == 'a')
Object prefab = EditorUtility.GetPrefabParent(Selection.activeObject);
if (prefab)
当Selection.activeObject没有预设(prefab)时,我们可以停止检查,因为如果它的预设(prefab)不存在,那么prefab变量会等于null,因此我们可以仅仅使用一个prefab引用来来避免检查。
现在,让我们实例化我们的预设并设置它的位置。
void GridUpdate(SceneView sceneview)
Event e = Event.
if (e.isKey && e.character == 'a')
Object prefab = EditorUtility.GetPrefabParent(Selection.activeObject);
if (prefab)
obj = (GameObject)EditorUtility.InstantiatePrefab(prefab);
obj.transform.position = new Vector3(0.0f, 0.0f, 0.0f);
这就是它的真实面目&现在让我们检查一下克隆的立方体是否链接到了预设(prefab)。
Step 11: 把屏幕上的鼠标坐标转换为世界坐标
Event类不告诉我们鼠标在世界空间中的位置,它只告诉我们鼠标在屏幕空间中的坐标。下面展示了我们如何在这两者之间进行转换,以便我们可以得到一个近似的世界空间中的鼠标坐标。
void GridUpdate(SceneView sceneview)
Event e = Event.
Ray r = Camera.current.ScreenPointToRay(new Vector3(e.mousePosition.x, -e.mousePosition.y + Camera.current.pixelHeight));
Vector3 mousePos = r.
首先,我们使用编辑器摄像机的ScreenPointToRay方法来得到一个从屏幕坐标发出的射线,但不幸的是,在那之前我们需要把事件的屏幕空间转换成ScreenPointToRay()可接受的一个屏幕空间。 e.mousePosition里面保存着鼠标在一个特殊屏幕空间中的位置,这个特殊的屏幕空间的左上角的坐标是(0,0)点,右下角的坐标等于(Camera.current.pixelWidth, -Camera.current.pixelHeight)。我们需要把它转换成ScreenPointToRay()可接受的屏幕空间,也就是左下角的坐标是(0,0),右上角的坐标是(Camera.current.pixelWidth, Camera.current.pixelHeight),这种转换是很简单的。
接下来我们应该做的就是把射线的原点存放在我们的mousePos向量中,以便它可以很容易的访问。
现在,我们可以把克隆物体的位置设置为鼠标所在的位置:
if (prefab)
obj = (GameObject)EditorUtility.InstantiatePrefab(prefab);
obj.transform.position = new Vector3(mousePos.x, mousePos.y, 0.0f);
注意 when the camera is set really flat then the approximation of mouse position on one of the axes is really really bad,那就是为什么我要手动设置克隆体的z坐标。现在,cube应该会在鼠标所在的位置被创建。
Step 12: 把立方体对齐到网格
因为我们已经设置好了我们的网格,如果不用它的话那会是一种耻辱;就让我们使用我们的鼠标位置来把创建的立方体对齐到网格上。
if (prefab)
obj = (GameObject)EditorUtility.InstantiatePrefab(prefab);
Vector3 aligned = new Vector3(Mathf.Floor(mousePos.x/grid.width)*grid.width + grid.width/2.0f,
Mathf.Floor(mousePos.y/grid.height)*grid.height + grid.height/2.0f, 0.0f);
obj.transform.position =
看一下结果:
Step 13: 从编辑器脚本中销毁一个对象物体
在这一步中,我们将在编辑器中编程来删除物体。我们可以使用DestroyImmediate()来实现。在这个案例中让我们充分利用Selection类,当'd'键被按下时删除所有被选中的物体。
if (e.isKey && e.character == 'a')
Object prefab = EditorUtility.GetPrefabParent(Selection.activeObject);
if (prefab)
obj = (GameObject)EditorUtility.InstantiatePrefab(prefab);
Vector3 aligned = new Vector3(Mathf.Floor(mousePos.x/grid.width)*grid.width + grid.width/2.0f,
Mathf.Floor(mousePos.y/grid.height)*grid.height + grid.height/2.0f, 0.0f);
obj.transform.position =
else if (e.isKey && e.character == 'd')
foreach (GameObject obj in Selection.gameObjects)
DestroyImmediate(obj);
当'd'键被按下时,我们遍历所有被选中的物体并且删除它们中的每一个物体。当然,我们也可以在编辑器中按Delete键来删除这些物体,但是这样的话就不是由我们的脚本删除的,而是系统删除的。在编辑器中测试一下。
Step 14: 撤销物体的实例化
在这一步中,我们会使用Undo类,这个类会让我们撤销我们的编辑器脚本所作出的每一个动作。让我们从撤销对象创建开始。
为了能够销毁我们在编辑器中所创建的对象,我们需要调用Undo.RegisterCreatedObjectUndo()函数。它接收2个参数:第一个参数是一个已经创建的对象,第二个参数是撤销动作的名称。这个撤销动作的名称总是显示在Edit-&Undo name下面。
if (prefab)
obj = (GameObject)EditorUtility.InstantiatePrefab(prefab);
Vector3 aligned = new Vector3(Mathf.Floor(mousePos.x/grid.width)*grid.width + grid.width/2.0f,
Mathf.Floor(mousePos.y/grid.height)*grid.height + grid.height/2.0f, 0.0f);
obj.transform.position =
Undo.RegisterCreatedObjectUndo(obj, "Create " + obj.name);
如果你使用a键创建了一些立方体,然后尝试撤销它们,那么现在你会注意到所有被创建的立方体都会被删除。这是由于所有这些被创建的立方体都进入到同一个撤销事件中。
Step 15: 撤销单个物体的实例化
如果我们想把每一个被创建的物体放置在不同的撤销事件中,使我们能够逐个地撤销创建过程,我们需要使用Undo.IncrementCurrentEventIndex().
if (prefab)
Undo.IncrementCurrentEventIndex();
obj = (GameObject)EditorUtility.InstantiatePrefab(prefab);
Vector3 aligned = new Vector3(Mathf.Floor(mousePos.x/grid.width)*grid.width + grid.width/2.0f,
Mathf.Floor(mousePos.y/grid.height)*grid.height + grid.height/2.0f, 0.0f);
obj.transform.position =
Undo.RegisterCreatedObjectUndo(obj, "Create " + obj.name);
现在如果你测试这个脚本,你会看到通过撤销对他们的创建,立方体会被逐个地删除。
Step 16: 撤销物体的删除
要撤销对物体的删除动作,我们需要使用Undo.RegisterSceneUndo()函数。它是一个非常慢的函数,因为它会保存场景的所有状态,以便我们随后可以通过执行撤销动作来恢复到那个状态。不幸的是,它似乎是当前我们能够让被删除的对象回到场景中的唯一办法了。
else if (e.isKey && e.character == 'd')
Undo.IncrementCurrentEventIndex();
Undo.RegisterSceneUndo("Delete Selected Objects");
foreach (GameObject obj in Selection.gameObjects)
DestroyImmediate(obj);
Undo.RegisterSceneUndo()函数只接收一个参数,那就是撤销动作的名称。通过'd'键删除一些立方体之后,你可以撤销那些删除操作。
Step 17: 创建一个编辑器窗口脚本
创建一个新的脚本,并且让它继承EditorWindow而不是Editor。我们把它命名为GridWindow.cs
using UnityE
using UnityE
using System.C
public class GridWindow : EditorWindow
public void Init()
让我们创建一个对我们的Grid对象的引用,以便我们可以在窗口中访问它。
public class GridWindow : EditorWindow
public void Init()
grid = (Grid)FindObjectOfType(typeof(Grid));
现在,我们需要创建这个窗口了,我们可以在我们的GridEditor脚本中来创建。
Step 18: 创建GridWindow
在我们的OnInspectorGUI()函数中添加一个按钮,用来创建GridWindow.
public override void OnInspectorGUI()
GUILayout.BeginHorizontal();
GUILayout.Label("Grid Width");
grid.width = EditorGUILayout.FloatField(grid.width, GUILayout.Width(50));
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Label("Grid Height");
grid.height = EditorGUILayout.FloatField(grid.height, GUILayout.Width(50));
GUILayout.EndHorizontal();
if (GUILayout.Button("Open Grid Window", GUILayout.Width(255)))
GridWindow window = (GridWindow) EditorWindow.GetWindow(typeof(GridWindow));
window.Init();
SceneView.RepaintAll();
我们使用GUILayout来创建一个按钮,同时设置按钮的名称和宽度。当按钮被按下时,GUILayout.Button()返回true,在这种情况下,我们打开我们的GridWindow.
你可以返回到编辑器中,按下我们的Grid对象的inspector面板中的按钮。
一旦你这样做了,GridWindow就会弹出来。
Step 19: 在GridWindow中创建一个颜色字段(Color Field)
在我们编辑我们的窗口之前,让我们添加一个颜色字段到我们的Grid类中,以便我们可以在后面编辑它。
public class Grid : MonoBehaviour
public float width = 32.0f;
public float height = 32.0f;
public Color color = Color.
现在,在OnDrawGizmos()函数中为Gizmos.color赋值。
void OnDrawGizmos()
Vector3 pos = Camera.current.transform.
Gizmos.color =
现在,让我们回到GridWindow脚本中来,在窗口中创建一个颜色字段以便我们可以在窗口中拾取颜色。我们可以在OnGUI()回调函数中做这件事。
public class GridWindow : EditorWindow
public void Init()
grid = (Grid)FindObjectOfType(typeof(Grid));
void OnGUI()
grid.color = EditorGUILayout.ColorField(grid.color, GUILayout.Width(200));
好了,现在你可以在编辑器中检查是否一切工作正常。
Step 20: 添加一个委托(Delegate)
现在,我们是使用符号=来设置一个委托,这个委托用来从场景视图中获取输入事件。这种做法并不是一个好的方法,因为它会覆盖掉所有其他的回调函数。我们应该使用+=符号来代替=。让我们回到我们的GridWindow.cs脚本中,改变这种做法。
public void OnEnable()
grid = (Grid)
SceneView.onSceneGUIDelegate += GridU
我们也需要创建一个OnDisable()回调函数用来删除我们的GridUpdate(),如果我们不这样做的话,它就会进行叠加,然后在一次事件中被调用多次。
public void OnEnable()
grid = (Grid)
SceneView.onSceneGUIDelegate += GridU
public void OnDisable()
SceneView.onSceneGUIDelegate -= GridU
这就是编辑器脚本的介绍。如果你想扩充你的知识,在中有很多可阅读的话题&&根据你的需要你可能想要查看Resources, AssetDatabase或者FileUtil类来了解更多内容。
不幸的是,一些类还没有被写进文档。由于这个原因,很容易导致不能工作。例如,SceneView类和它的函数或者是Undo类中的Undo.IncrementCurrentEventIndex()函数。如果中没有提供你要找的答案,你可能需要在或者中搜索一下了。
感谢您花时间阅读这篇文章!
写在最后的话
点击,查看英文原版。 点击,查看中文翻译版。
该文章来源于Envato网站上的Tuts+,版权属原作者所有!如果转载,请保留到原文的链接!谢谢!
版权所有 爱编程 (C) Copyright 2012. . All Rights Reserved.
闽ICP备号-3
微信扫一扫关注爱编程,每天为您推送一篇经典技术文章。}

我要回帖

更多关于 unity3d gameobject 的文章

更多推荐

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

点击添加站长微信