http://dong2008hong.blog.163.com/blog/static/4696882720140302323886/
Unity3D脚本中文系列教程(二) 示,属性不被序列化或显示在检视面板中。
.避免使用构造函数
不要在构造函数中初始化任何变量,使用Awake或Start实现这个目的。即使是在编辑模式中Unity也自动调用构造函数,这通常发生在一个脚本被编译之后,因为需要调用构造函数来取向一个脚本的默认值。构造函数不仅会在无法预料的时刻被调用,它也会为预设或未激活的游戏物体调用。
单件模式使用构造函数可能会导致严重的后果,带来类似随机null引用异常。
因此如果你想实现,如,一个单件模式,不要使用构造函数,而是使用Awake。其实上,没有理由一定要在继续自MononBehaviour类的构造函数中写任何代码。
概览:最重要的类
Javascript中可访问的全局函数或C#中的基类
移动/旋转物体
动画系统
刚体
FPS或第二人称角色控制器
概览:性能优化
.使用静态类型
在使用Javascript时最重要的优化是使用静态类型而不是动态类型,Unity使用一种叫做类型推理的技术来自自动转换Javascript为静态类型编码而不需要你做任何工作。
var foo=;
在上面的例子里foo会自动被推断为一个整型值。因此,Unity可能使用大量的编译时间来优化。而不使用耗时的动态名称变量查找等。这就是为什么Unity比其他在JavaScript的实现平均快20倍的原因之一。
唯一的问题是,有时并非一切都可以做类型推断。Unity将会为这些变量重新使用动态类型。通过回到动态类型,编写JavaScript代码很简单。但是这也使得代码运行速度较慢。
让我们看一些例子:
function Start ()
{
var foo = GetComponent(MyScript);
foo.DoSomething();
}
这里foo将是动态类型,因此调用DoSomething函数将使用较长时间,因为foo的类型是未知的,它必须找出它是否支持DoSomething函数,如果支持, 调用它。
function Start ()
{
var foo : MyScript = GetComponent(MyScript);
foo.DoSomething();
}
这里我们强制foo为指定类型,你将获得更好的性能。
.使用#pragma strict
当然现在问题是,你通常没有意识到你在使用动态类型。#pragma strict解决了这个!简单的在脚本顶部添加#pragma strict。然后, unity将在脚本中禁用动态类型,强制使用静态类型,如果一个类型未知。Unity将报告编译错误。那么在这种情况下foo将在编译时产生一个错误:
#pragma strict
function Start ()
{
var foo = GetComponent(MyScript);
foo.DoSomething();
}
.缓存组件查找
另一个优化是组件缓存。不幸的是该优化需要一点编码,并且不一定是值得的,但是如
果你的脚本是真的用了很长时间了,你需要把最后一点表现出来,这是一个很好的优化。
当你访问一个组件通过GetComponent或访问变量,Unity会通过游戏对象找到正确的组件。这一次可以很容易地通过缓存保存在一个私有变量里引用该组件。
简单地把这个:
function Update ()
{
transform.Translate(, , );
}
变成:
private var myTransform : Transform;
function Awake ()
{
myTransform = transform;
}
function Update ()
{
myTransform.Translate(, , );
}
后者的代码将运行快得多,因为Unity没有找到变换在每一帧游戏组件中的对象。这同样适用于脚本组件,在你使用GetComponent代替变换或者其它的东西。
.使用内置数组
内置数组的速度非常快,所以请使用它们。
而整列或者数组类更容易使用,因为你可以很容易地添加元素,他们几乎没有相同的速度。内置数组有一个固定的尺寸,但大多数时候你事先知道了最大的大小在可以只填写了以后。关于内置数组最好的事情是,他们直接嵌入在一个结构紧凑的缓冲区的数据类型没有任何额外的类型信息或其他开销。因此,遍历是非常容易的,作为一切缓存在内存中的线性关系。
private var positions : Vector3[];
function Awake ()
{
positions = new Vector3[];
for (var i=;i<;i++)
positions[i] = Vector3.zero;
}
.如果你不需要就不要调用函数
最简单的和所有优化最好的是少工作量的执行。例如,当一个敌人很远最完美的时间就是敌人入睡时可以接受。直到玩家靠近时什么都没有做。这是种缓慢的处理方式的情况:
function Update ()
{
// 早期进行如果玩家实在是太遥远。
if (Vector3.Distance(transform.position, target.position) > )
return;
perform real work work...
}
这不是一个好主意,因为Unity必须调用更新功能,而你正在执行工作的每一个帧。一个比较好的解决办法是禁用行为直到玩家靠近。有3种方法来做到这一点:
.使用OnBecameVisible和OnBecameInvisible。这些回调都是绑到渲染系统的。只要任何相机可以看到物体,OnBecameVisible将被调用,当没有相机看到任何一个,OnBecameInvisible将被调用。这种方法在很多情况下非常有用,但通常在AI中并不是特别有用,因为只要你把相机离开他们敌人将不
可用。
function OnBecameVisible () {
enabled = true;
}
function OnBecameInvisible ()
{
enabled = false;
}
.使用触发器。一个简单的球形触发器会工作的非常好。一旦你离开这个影响球你将得到OnTriggerEnter/Exit调用。
function OnTriggerEnter (c : Collider)
{
if (c.CompareTag("Player"))
enabled = true;
}
function OnTriggerExit (c : Collider)
{
if (c.CompareTag("Player"))
enabled = false;
}
.使用协同程序。Update调用的问题是它们每帧中都发生。很可能会只需要每5秒检检查一次到玩家的距离。这应该会节省大量的处理周期。
概览:脚本编译(高级)
Unity编译所有的脚本为.NET dll文件,.dll将在运行时编译执行。
这允许脚本以惊人的速度执行。这比传统的javascript快约20倍。比原始的C++代码慢大约50%。在保存的时候,Unity将花费一点时间来编译所有脚本,如果Unity还在编译。你可以在Unity主窗口的右下角看到一个小的旋转进度图标。
脚本编译在4个步骤中执行:
.所有在"Standard Assets", "Pro Standard Assets" 或 "Plugins"的脚本被首先编译。
在这些文件夹之内的脚本不能直接访问这些文件夹之外脚本。
不能直接引用或它的 变量,但是可以使用GameObject.SentMessage与它们通信。
.所有在"Standard Assets/Editor", "Pro Standard Assets/Editor" 或 "Plugins/Editor"的脚本被首先编译。
如果你想使用UnityEditor命名空间你必须放置你的脚本在这些文件夹中,例如添加菜单项或自定义的向导,你都需要放置脚本到这些文件夹。
这些脚本可以访问前一组中的脚本。
.然后所有在"Editor"中的脚本被编译。
如果你想使用UnityEditor命名空间你必须放置你的脚本在这些文件夹中。例如添加菜单单项或自定义的向导,你都需要放置脚本到这些文件夹。
这些脚本可以访问所有前面组中的脚本,然而它们不能访问后面组中的脚本。
这可能会是一个问题,当编写编辑器代码编辑那些在后面组中的脚本时。有两个解决方法:、移动其他脚本到"Plugins"文件夹 、利用JavaScript的动态类型,在javascript中你不需要知道类的类型。在使用GetComponent时你可以使用字符串而不是类型。你也可以使用SendMessage,它使用一个字符串。
.所有其他的脚本被最后编译
所有那些没有在上面文件夹中的脚本被最后编译。
所有在这里编译的脚本可以访问第一个组中的所有脚本("Standard Assets","Pro
Standard Assets" or "Plugins")。这允许你让不同的脚本语言互操作。例如,如果你想创建一个JavaScript。它使用一个C#脚本;放置C#脚本到"Standard Assets"文件夹并且JavaScript放置在"Standard Assets"文件夹之外。现在JavaScript可以直接引用c#脚本。
放置在第一个组中的脚本,将需要较长的编译时间,因为当他们被编译后,第三组需要被重新编译。因此如果你想减少编译时间,移动那些不常改变 的到第一组。经常改变的到第四组。