日期:2012年7月5日

Unity3.5.2更新修正列表

这次发行的Unity 3.5.2主要是对Unity 3.5的更新.想要了解更多新特性 ,请查阅Unity 3.5发行说明(http://unity3d.com/unity/whats-new/unity-3.5).

改进
iOS:在AppController.mm中加入UnityGetGLView功能,返回到unity所使用的视图.
iOS:现在如果在“retina”设备上运行,闪屏会使用“retina”影像.
Flash:引入System.Attribute 基类型,从而可以更方便地编译定制属性.
Flash:现在可以启动构造器,构造器给出byref参数.
Flash:Stage3D路径速度有所提高,同时11.1和11.2设置的路径执行变得更快.
Flash:点过滤现在可以使用.
Flash:系统.Bit转换(System.Bitconverter)现在可以使用
Flash:词典.包含价值( Dictionary.ContainsValue)现在可以使用
Flash:在一些情况下占用较小的内存量
Flash:SWFPostProcessing加速
Flash:生成字节码方面的性能优化
Flash:字节[]现在可以使用Flash本地bytearray作为后备存储器(.元素)
Flash:System.Text.Encoding浅执行,用于ascii和utf
Flash:安装启用System.Random, System.IO.MemoryStream, DateTime.
Flash:System.Exception.StackTrace属性
Flash:更多的转换错误,现在还包括相关的源定位.
改变
增加Resources.UnloadAsset(目标资源)功能.如果你知道某些较大的个体资源已不再使用,且不想调用Resources.UnloadUnusedAssets(),你可以使用这一增加功能很方便地卸载该资源.
编辑器:引入[Callbacks.PostProcessBuild]脚本属性.在建立播放器后执行属性方法.
编辑器:引入[Callbacks.PostProcessScene]脚本属性.处理当前场景时,执行属性方法.
Flash:为不同的FlashPlayer版本11.0、11.1和11.2建立了目标.现在还无从知晓Flash 播放器插件版本的具体的执行,但11.2设置意味着使用更快的代码路径;更好的性能.
Flash:现在可以使用www.GetAudioClip(),仅用于非串流mp3’s
Flash:WWW支持
Flash:WWWForm 支持
修复
Android:对Galaxy Nexus ICS上OpenGL ES驱动程序随意死机进行修复
Android:与一些装置上ICS升级有关的触屏问题得到修复.
Android:Caching.CleanCache()总是返回到false(不清除缓存).
Android:修复ICS装置上的Webcam结构初始化问题.
编辑器:当检测Visual Studio安装时,捕捉更多的角落事例(corner cases).
编辑器:修复游戏模式中当显示AudioClip检视图时的内存不足死机
编辑器:将PvrTexTool更新到2.10.87.498版本.
编辑器:修复使用SendMessage时的死机,非空变元传递到无参数接收器.
编辑器:修复已用纹理统计,在统计视图中显示
修复附加分析器后的内存泄漏.
修复下载资源包时的WebPlayer死机.
修复鼠标Δ处理故障,主要是非HID启用型鼠标.
修复音频试听上的内存不足.
修复当为缓存服务器散列资源时的竞态条件.
修复Resources.Load死机,如果装置的资源引用的是不存在的资源.
对因使用大量场景间参照资源创建播放器导致内存不足进而导致的死机进行修复.
标记为Occluders的SkinnedMeshRenderers将导致闭塞,修复这种故障.在PVS计算中,现在SkinnedMeshes只可以标记Occludees,并且使用蒙皮网络合订卷.
启动Unity后重新装载脚本带有编程错误,修复由此引起的死机.
Flash:修复整数除法符号.
Flash:修复本地方法回归结构中的内存泄露.
Flash:构造器将字段作为byref变元进行传递,修复由此引起的死机.
Flash:修复启动但没有应用的RuntimeServices_ToBool_Object.
Flash:修复字节上的字节序错误[].
Flash:修复资源包上monobehaviour反序列化方面的死机.
Flash:修复不能再进行编写脚本的粒子系统.
Flash:修复随意字符串www.错误,即使没有eror.
Flash:修复由协同程序中错误引起的致命错误故障.
Flash:修复www.纹理使用上的致命错误.
Flash:修复黑色背景上的flash预紧器条.
Flash:如果目的尺寸大于源尺寸,array.CopyTo()失败,修复此处的故障.
Flash:修复示例()上类型表字段故障.
Flash:修复列表序列化.
Flash:SwfPostprocessor引起swf不能确认的问题,或引起栈之下-或溢值,对此类问题进行修复.
Flash:修复许多小动作脚本转换.
Flash:GuiTexture再次正确渲染.
Flash:.Particles再次正确渲染.
Flash:带较大纹理的Texture2D setPixels不在死机.
Flash:UnityContent .setSize(宽,高)正确运行.
Flash:useGuiLayout可以使用.
Flash:gui.window不再出现死机.
Flash:不再有物理/角色控制冲突方面的死机.
Flash:物理,修复物体空间内摄影或困难的问题.
Flash:结构序列不再(不正确地)序列化.
Flash:ParticleSystem脚本现在可以使用.
Flash:现在可以使用非公开monobehaviour序列化.
Flash:协同程序问题,引起FlashPlayer核对和栈尺寸问题,对这种问题修复.
Flash:EncodeToPNG现在可以使用.
Flash:.c#阵列数据压缩支持.
Flash:矫正中缀表达式数值投射处理(不包括(int)(f * g)).
Flash:修复序列化表.
Flash:修复SwfPostprocessor;在这些情况下,player插件出现致命错误,显示文件夹不可用/常量出现检验错误.
Flash:修复SwfPostprocessor;修复最大栈尺寸计算.
Flash:修复StartCoroutine死机中的WWW.GetAudioClip()使用.
图形:修复在CombineMeshes中互换的顶点颜色.
图形:修复零点近平面聚光灯错误.
iOS:修复3.5/3.5.1中的动态几何性能回归.
iOS:闪屏旋转使其倾斜,对此修复.
iOS:Application.targetFrameRate <= 0设置将产生60 fps(如同在其他平台上).
iOS:InvalidateState引起INVALID_FRAMEBUFFER_OPERATION_EXT.GL,对此进行修复.
iOS:修复,并在iPhone上的封面屏幕中进行排列.
iOS:闪屏旋转使其倾斜,对此修复.
旧系统粒子:如果Time.timeScale是0,ParticleRenderer和TrailRenderer不会泄漏内存.
移动:修复SkinnedMeshRenderer死机.
导航网格:当Time.timeScale是0时,NavMeshAgent速率不会变成无效.
导航网格:当目的站和位置设为同一矢量时,NavMeshAgent修复偶然的无效位置/速率.
OS X:通过微软鼠标修复不稳定鼠标行为.
Windows Player:当进出全屏模式时,修复多线程渲染中偶然的暂停.
升级指南
在Unity 3.4中,实时灯光不影响静态物体.在Unity 3.5中,实时灯光往往影响静态和动态物体.产生的结果是:如果你之前设定了灯光围绕这个程序错误运行,因为接收了两次灯光,当前的性能将会很差.同时,它看起来也将不同,因为你的场景中增加了两次灯光.使用这些灯光的正确方法是使用单自动平行灯光,去除实时灯光.如果你希望拥有静态灯光以外的不同实时灯光,你需要使用灯光剔除掩码来影响动态的物体.

原文:http://china.unity3d.com/unity/whats-new/unity-3.5.2

在Unity编辑器上增加你自己的工具

本篇教程将教你扩展Unity编辑器,来更好的配合你开发项目.你将学习如何绘制Gizmo,在节点中创建删除对象,创建一个编辑器窗口,和使用组件,并且允许用户撤消任何在你工具上的操作.

首先我们看看最终我们要达成的工具样子:

正如图中所示,我们将创建一个编辑器窗口,还有颜色选择器来设置我们画出的栅格颜色,另外也可以创建和删除对象,对齐到这个栅格,以及对操作的撤消等等.
1:Gizmos
首先我们学习如何使用Gizmos,下面这个是几个常见的内建的Gizmos.

选择任何一个对象时都会显示这个Gizmo.

这是另外一个Gizmo,这使我们能看到所选的对象的boxCollider的大小.

2:创建一个栅格脚本
用C#脚本绘制Gizmo给一个对象,我们将在编辑器中画一个栅格作为示例.

using UnityEngine;
using System.Collections;

public class Grid : MonoBehaviour
{

    void Start ()
    {
    }

    void Update ()
    {
    }
}

这个栅格,我们需要添加长和宽的两个变量.

public class Grid : MonoBehaviour
{
    public float width = 32.0f;
    public float height = 32.0f;

    void Start ()
    {
    }

    void Update ()
    {
    }
}

要绘制栅格,我们需要用OnDrawGizmos,让我们创建它.

public class Grid : MonoBehaviour
{
    public float width = 32.0f;
    public float height = 32.0f;

    void Start ()
    {
    }

    void Update ()
    {
    }

    void OnDrawGizmos()
    {
    }
}

3:绘制栅格
要绘制栅格,我们需要在编辑器的相机位置绘制一些横线竖线.首先我们用一个变量来储存相机的位置.

void OnDrawGizmos()
{
    Vector3 pos = Camera.current.transform.position;
}

我们可以使用Camera.current来获取编辑器的相机.

现在我们需要用两个循环来绘制横线和竖线.

void OnDrawGizmos()
{
    Vector3 pos = Camera.current.transform.position;

    for (float y = pos.y - 800.0f; y < pos.y + 800.0f; y+= height)
    {
        Gizmos.DrawLine(new Vector3(-1000000.0f, Mathf.Floor(y/height) * height, 0.0f),
                        new Vector3(1000000.0f, 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, -1000000.0f, 0.0f),
                        new Vector3(Mathf.Floor(x/width) * width, 1000000.0f, 0.0f));
    }
}

我们用Gizmos.DrawLine()来画线.Gizmos类里面有很多其他绘制方法,所以它可以绘制立方体,球体,甚至线框图.也可以绘制图像.

栅格应该是无限长,但是没有无穷远的浮点数给我们用,所以我们可以简单的用任意大的数字代替.行数取决于循环的次数.

要看到栅格,旧创建一个空对象并将我们这个脚本赋予它.

4:自定义检视面板

接下来我们自定义检视面板,我们需要创建在创建一个C#脚本文件并命名为GridEditor.放在脚本编辑器Editor文件夹里.如果没有这个文件夹,就创建一个.

using UnityEngine;
using UnityEditor;
using System.Collections;

[CustomEditor (typeof(Grid))]
public class GridEditor : Editor
{
}

我们需要用UnityEditor的函数来覆盖栅格默认的检视面板,使用 [CustomEditor (typeof(Grid))] 让Unity知道我们将定制栅格的检视面板.

public class GridEditor : Editor
{
    public override void OnInspectorGUI()
    {
    }
}

如果现在你看编辑器的检视面板,你将看到一个空的窗口.这是因为我们自定义了一个面板用OnInspectorGui()的方法覆盖掉了默认的检视面板.

5:使用GuiLayout填充自定义检视面板.
上面我们创建了一个空的面板,首先我们要给这个面板指定一个目标对象.实际上,已经有个target给这个检视面板.但是方便起见,我们将创建一个引用栅格组件的对象.我们先来声明它.

public class GridEditor : Editor
{
    Grid grid;

在检视面板激活的时候可以用OnEnable()方法来执行一些事件.

public class GridEditor : Editor
{
    Grid grid;

    public void OnEnable()
    {
        grid = (Grid)target;
    }

我们用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()表示下面的元素都将水平排列,同样的,在最后有GUILayout.EndHorizontal()来说明结束上面的排列方式.第一个是一个标签.用来显示网格宽度.然后旁边创建了一个EditorGUILayout.FloatField字段来分配grid.width,并设置默认值为50像素.

下面是在检视面板中实际的效果:

6:填充检视面板和重绘场景.
让我们添加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();
}

这就是控制栅格对象的所有字段,如果想了解其他类型的字段项,可以看脚本参考的EditorGUILayout和GUILayout,这个时候只有在我们选择场景视图窗口的时候,检视面板中做的新变化才是可见的,为了保持实时变化,一旦我们做了修改,我们就可以用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();
}

现在我们就不需要在点击检视面板以外的区域查看改变了.

7:处理编辑器的输入
现在我们来尝试处理编辑器的输入,就像我们做的游戏获得键盘或鼠标的状态.我们用onSceneGUIDelegate来实现场景视图上的交互,让我们来调用GridUpdate().

public void OnEnable()
{
    grid = (Grid)target;
    SceneView.onSceneGUIDelegate = GridUpdate;
}

void GridUpdate(SceneView sceneview)
{
}

现在我们只需要获得输入事件.

void GridUpdate(SceneView sceneview)
{
    Event e = Event.current;
}

8:创建预设
为了进一步学习编辑器脚本,我们需要一个游戏对象,让我们创建一个立方体预设.

可以缩放立方体或者其他方式让它与网格对齐.

你可以在层次面板中看到立方体的名字是蓝色的,这意味着它与一个预设有关联.在项目视图中也可以看到这个预设.

9:从编辑器脚本创建对象
现在我们要做到从编辑器脚本创建对象,打开GirdeEditor.cs脚本来改进GridUpdate()函数.
当按下按键时创建对象.

void GridUpdate(SceneView sceneview)
{
    Event e = Event.current;

    if (e.isKey && e.character == 'a')
    {
        GameObject obj;
    }
}

脚本会监听是否按下a键,同样创建了一个用来引用的新对象.

void GridUpdate(SceneView sceneview)
{
    Event e = Event.current;

    if (e.isKey && e.character == 'a')
    {
        GameObject obj;
        if (Selection.activeObject)
        {
            obj = (GameObject)Instantiate(Selection.activeObject);
            obj.transform.position = new Vector3(0.0f, 0.0f, 0.0f);
        }
    }
}

Selection.activeObject 是目前在编辑器选择的对象.只要有对象被选择,我们就会复制它并改变复制出来的对象的坐标到空间原点.

现在我们来测试一下,你要注意当资源重导入或者刷新时GridUpdate()将停止运行,如果要重新运行,你要选择一个对象(例如从层次视图中),我们这例子要选择网格对象.还需要记得场景视图获得焦点时才能捕获输入事件.

10:从编辑器脚本实例化预设
虽然我们现在可以克隆对象,但克隆的对象是不会有预设关联的.

你可以看到Cube(Clone)是纯黑色的字体,代表它没有连接到原来立方体的预设.而我们如果在编辑器中手动复制原始立方体,克隆出来的立方体将拥有原立方体一样的预设.如果想在脚本里面克隆出来的立方体也有原立方体的预设关联,我们需要使用EditorUtility类里面的InstantiatePrefab() 函数.

使用这个函数前,我们需要得到所选对象的预设,我们需要用到EditorUtility类里面的GetPrefabParent()函数.

void GridUpdate(SceneView sceneview)
{
    Event e = Event.current;

    if (e.isKey&& e.character == 'a')
    {
        GameObject obj;
        Object prefab = EditorUtility.GetPrefabParent(Selection.activeObject);

        if (prefab)
        {

我们也可以停止判断 Selection.activeObject了,因为如果它不存在,预设会为空,因此我们可以只检查预设引用是否存在就可以了.
现在让我们来实例化预设并设置它的位置.

void GridUpdate(SceneView sceneview)
{
    Event e = Event.current;

    if (e.isKey && e.character == 'a')
    {
        GameObject obj;
        Object prefab = EditorUtility.GetPrefabParent(Selection.activeObject);

        if (prefab)
        {
            obj = (GameObject)EditorUtility.InstantiatePrefab(prefab);
            obj.transform.position = new Vector3(0.0f, 0.0f, 0.0f);
        }
    }
}

看,现在克隆出来的有预设关联了.

11:转换屏幕上鼠标坐标到世界坐标系
Event类没有让我们获得鼠标在世界空间的位置,它只提供了屏幕空间坐标系的鼠标位置.下面我们将转换它们,让我们得到鼠标在世界空间里的位置.

void GridUpdate(SceneView sceneview)
{
    Event e = Event.current;

    Ray r = Camera.current.ScreenPointToRay(new Vector3(e.mousePosition.x, -e.mousePosition.y + Camera.current.pixelHeight));
    Vector3 mousePos = r.origin;

首先,我们使用编辑器相机的ScreenPointToRay从屏幕坐标系上获取射线,不过在这之前我们需要转换屏幕坐标供ScreenPointToRay()使用.

e.mousePosition的原点是从屏幕左上角(等于Camera.current.pixelWidth, -Camera.current.pixelHeight).我们需要转换为原点在屏幕左下角的坐标(Camera.current.pixelWidth, Camera.current.pixelHeight).
接下来我们要保存鼠标位置的向量.
现在我们可以指定克隆出来的对象到鼠标的位置.

if (prefab)
{
    obj = (GameObject)EditorUtility.InstantiatePrefab(prefab);
    obj.transform.position = new Vector3(mousePos.x, mousePos.y, 0.0f);
}

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 = aligned;
}

结果下图这样.

13:销毁从编辑器脚本创建的对象.

可以使用DestroyImmediate()来删除对象,本例中我们将通过按下”d”键来删除所有被选中的对象.

if (e.isKey && e.character == 'a')
{
    GameObject obj;
    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 = aligned;
    }
}
else if (e.isKey && e.character == 'd')
{
    foreach (GameObject obj in Selection.gameObjects)
        DestroyImmediate(obj);
}

当”D”键按下时,我们遍历所有被选中的对象,并逐一删除.当然Delete键依旧可用的.

14:撤消对象实例化

这一节我们将利用Undo类来实现撤消编辑器脚本的撤消.为了实现撤消,我们需要调用在编辑器中创建Undo.RegisterCreatedObjectUndo().它有两个参数,第一个是已经创建的对象,第二个是撤消的动作名称.会在”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 = aligned;
    Undo.RegisterCreatedObjectUndo(obj, "Create " + obj.name);
}

如果你按下按键,创建了多个立方体,然后尝试撤消,你会发现,刚才所有创建的立方体都被删除了.这事因为所有被创建的立方体放在了一个单一撤消事件中.

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 = aligned;
    Undo.RegisterCreatedObjectUndo(obj, "Create " + obj.name);
}

现在测试脚本你会看到会一个个的撤消被创建的立方体.

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键删除后.你可以用这个命令撤消删除.

17:创建编辑器窗口脚本

用一个新脚本创建一个新编辑器窗口,脚本名为GridWindow.cs.

using UnityEngine;
using UnityEditor;
using System.Collections;

public class GridWindow : EditorWindow
{
    public void Init()
    {
    }
}

通过栅格对象来访问这个窗口.

public class GridWindow : EditorWindow
{
    Grid grid;

    public void Init()
    {
        grid = (Grid)FindObjectOfType(typeof(Grid));
    }
}

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来创建按钮,并且设置按钮的名称和宽度.打开GridWindow窗口点下这个按钮,将返回真.

回到检视面板点击打开Grid Window按钮.

你会看到GridWindos弹出了.

19:在GridWindos中创建颜色字段.

可以在这个窗口中添加任何东西,现在让我们为栅格类添加一个颜色字段,以便后面我们来编辑它.

public class Grid : MonoBehaviour
{
    public float width = 32.0f;
    public float height = 32.0f;

    public Color color = Color.white;

在OnDrawGizmos()中赋给Gizmos.color.

void OnDrawGizmos()
{
    Vector3 pos = Camera.current.transform.position;
    Gizmos.color = color;

在GridWindos脚本里的OnGUI()添加一个颜色拾取器字段用来我们拾取颜色.

public class GridWindow : EditorWindow
{
    Grid grid;

    public void Init()
    {
        grid = (Grid)FindObjectOfType(typeof(Grid));
    }

    void OnGUI()
    {
        grid.color = EditorGUILayout.ColorField(grid.color, GUILayout.Width(200));
    }
}

点击测试一下改变栅格的颜色.

20:添加Delegate

我们设置了一个Delegate,用=号得到场景视图的输入事件,这样做不太好,因为它涵盖了所有其他回调.我们可以用+=符号代替=号.下面修改GridEditor.cs脚本.

public void OnEnable()
{
    grid = (Grid)target;
    SceneView.onSceneGUIDelegate += GridUpdate;
}

我们还需要创建一个OnDisable()的回调来替换掉GridUpdate(),否则会被执行很多次.

public void OnEnable()
{
    grid = (Grid)target;
    SceneView.onSceneGUIDelegate += GridUpdate;
}

public void OnDisable()
{
    SceneView.onSceneGUIDelegate -= GridUpdate;
}

结论:
这篇是编辑器编程的基础教程,如果你想扩展这方面的知识,可以查看脚本参考(http://unity3d.com/support/documentation/ScriptReference/index.html)中的Resources,和AssetDatabase和FileUtil类.

原文:http://active.tutsplus.com/tutorials/workflow/how-to-add-your-own-tools-to-unitys-editor/
由威阿翻译,转载请注明来自1Vr.Cn,否则MJJ.