Overview: Performance Optimization 性能优化
1. Use Static Typing
使用静态类型
When using JavaScript the most important optimization is to use static typing instead of dynamic typing. Unity uses a technique called type inference to automatically convert JavaScript constructs to statically typed code without you having to do any work.
使用JavaScript很重要的优化是使用静态类型替代动态类型。Unity使用一种叫做类型推理的技术来自动转换JavaScript为静态类型而无需你做任何工作。
using UnityEngine;
using System.Collections;
public class example : MonoBehaviour {
public int foo = 5;
}
var foo = 5;
In the above example foo will automatically be inferred to be an integer value. Thus Unity can apply a lot of compile time optimizations, without costly dynamic name variable lookups etc. This is one of the reasons why Unity's JavaScript is on average around 20 times faster than other JavaScript implementations.
上面例子中的foo将自动推断为一个整数值。因此Unity能节约大量时间,而不使用动态名称变量查找等耗时的计算。这就是为什么Unity的执行平均速度比其他JavaScript快20倍的原因之一。
The only problem is that sometimes not everything can be type inferred, thus Unity will fall back to dynamic typing for those variables. By falling back to dynamic typing, writing JavaScript code is simpler. However it also makes the code run slower.
唯一的问题是,有时不是所有的东西都能被类型推断,因此Unity会对这些变量重新使用动态类型.通过这样,书写JavaScript代码会很简单。不过他也使代码执行变慢。
Let's see some examples.
我们来看一些例子.
using UnityEngine;
using System.Collections;
public class example : MonoBehaviour {
void Start() {
duck foo = GetComponent<MyScript>();
foo.DoSomething();
}
}
function Start () {
var foo = GetComponent(MyScript);
foo.DoSomething();
}
Here foo will be dynamically typed, thus calling the function DoSomething takes longer than necessary - because the type of foo is unknown, it has to figure out whether it supports DoSomething function, and if it does, invoke that function.
这里foo将是动态类型,因此调用函数DoSomething必须要更长时间 – 因为foo的类型未知,它必须弄明白是否支持DoSomething函数,如果支持,就调用那个函数。
using UnityEngine;
using System.Collections;
public class example : MonoBehaviour {
void Start() {
MyScript foo = GetComponent<MyScript>();
foo.DoSomething();
}
}
function Start () {
var foo : MyScript = GetComponent(MyScript);
foo.DoSomething();
}
Here we're forcing foo to be of specific type. You will get much better performance.
这里我们指定foo为静态类型.你将获得更好的性能.
2. Use #pragma strict
使用#pragma strict
Now the problem is of course, that you don't usually notice when you are using dynamic typing. #pragma strict to the rescue! Simply add #pragma strict at the top of a script and Unity will disable dynamic typing in that script, forcing you to use static typing. Wherever a type is not known, Unity will report compile errors. So in this case, foo will produce an error when compiling:
现在问题是。你通常意识不到你使用了动态类型。 #pragma strict可解决!只需在脚本开始处简单的添加#pragma strict. Unity将会禁用脚本的动态类型,强制你使用静态类型。当一个类型未知,Unity就会报告编译错误。所以,如下代码,foo编译时将会产生一个错误:
using UnityEngine;
using System.Collections;
public class example : MonoBehaviour {
void Start() {
MyScript foo = GetComponent<MyScript>() as MyScript;
foo.DoSomething();
}
}
#pragma strict
function Start ()
{
var foo = GetComponent(MyScript);
foo.DoSomething();
}
3. Cache component lookups
缓存组件查找
Another optimization is caching of components. This optimization unfortunately requires a bit of coding effort and is not always worth it. But if your script is really used a lot and you need to get the last bit of performance out of it, this can be a very good optimization.
另一个优化是组件缓存.不过它需要一些代码,而且不一定都有必要。但是如果你的代码真是很大,且你需要一定量的性能提升,那么它可以提供很好的优化。
Whenever you access a component through GetComponent or an accessor variable, Unity has to find the right component from the game object. This time can easily be saved by caching a reference to the component in a private variable.
当你通过GetComponent访问一个组件或一个变量,Unity必须从游戏对象里找到正确的组件。这时你可以将参数缓存致一个私有的组件。
Simply turn this: 只要把这个:
using UnityEngine;
using System.Collections;
public class example : MonoBehaviour {
void Update() {
transform.Translate(0, 0, 5);
}
}
function Update () {
transform.Translate(0, 0, 5);
}
Into this: 变成这样
using UnityEngine;
using System.Collections;
public class example : MonoBehaviour {
private Transform myTransform;
void Awake() {
myTransform = transform;
}
void Update() {
myTransform.Translate(0, 0, 5);
}
}
private var myTransform : Transform ;
function Awake () {
myTransform = transform;
}
function Update () {
myTransform.Translate(0, 0, 5);
}
The latter code will run a lot faster since Unity doesn't have to find the transform component in the game object each frame. The same applies for scripted components, where you use GetComponent instead of the transform or other shorthand property.
后者运行得快一些,因为Unity不再在每帧查找transform组件。它同样适用于脚本组件,在你用GetComponent替换transform或其他快捷属性。
4. Use Builtin arrays
使用Builtin arrays
Builtin arrays are FAST, very fast, so use them. While the ArrayList or Array classes are easier to use since you can easily add elements they don't have nearly the same speed. Builtin arrays have a fixed size but most of the time you know the maximum size in advance and can just fill it out later. The best thing about builtin arrays is that they directly embed struct data types in one tightly packed buffer, without any extra type information or other overhead. Thus iterating through is very easy on the cache as everything is aligned.
Builtin arrays运行速度很快。虽然ArrayList或 Array 类很容易使用,你能轻易添加组件,但是他们有完全不同的速度。Builtin arrays有一个固定的长度,而且多数情况下你事先知道最大长度,且能扩展它。builtin arrays最大好处是它直接嵌入struct数据类型在一个缓冲区里,不需要额外类型信息或其他。因此更易于做缓存遍历。
using UnityEngine;
using System.Collections;
public class example : MonoBehaviour {
private Vector3[] positions;
void Awake() {
positions = new Vector3[100];
int i = 0;
while (i < 100) {
positions[i] = Vector3.zero;
i++;
}
}
}
private var positions : Vector3 [];
function Awake () {
positions = new Vector3 [100];
for (var i=0;i<100;i++)
positions[i] = Vector3.zero ;
}
5. Don't call a function if you don't have to
如果不是必须,不要调用函数
The simplest and best of all optimizations is to perform less work. For example , when an enemy is far away it is most of the time perfectly acceptable to have the enemy fall asleep. That is do nothing until the player comes close. The slow way of handling this situation would be:
最简单和最好的优化是执行最少的工作。例如,当一个敌人足够远,很多时候可以理想的认为敌人已沉睡。他什么都不做,直到角色接近。慢方法处理这种情况将是这样:
using UnityEngine;
using System.Collections;
public class example : MonoBehaviour {
public Transform target;
void Update() {
if (Vector3.Distance(transform.position, target.position) > 100)
return;
}
}
function Update ()
{
// Early out if the player is too far away.
// 尽早离开如果玩家太远
if ( Vector3.Distance (transform.position, target.position) > 100)
return;
//perform real work work...
}
This is not a good idea since Unity has to invoke the update function and you are performing work every frame. A better solution is to disabling the behaviour until the player comes closer. There are 3 ways to do this: 1. Use OnBecameVisible and OnBecameInvisible. These call backs are tied into the rendering system. As soon as any camera can see the object, OnBecameVisible will be called, when no camera sees it anymore OnBecameInvisible will be called. This is useful in some cases, but often for AI it is not useful because enemies would become disabled as soon as you turn the camera away from them.
这并不是很好的方法,因为Unity不得不访问update函数,且你得在每一帧执行它。更好的方法是禁用该行为直到玩家靠近。有3种方法实现它:
1.使用OnBecameVisible和OnBecameInvisible。这些回调函数绑定在渲染系统,一旦摄像机能看到对象,OnBecameVisible将被调用,当没有摄像机看他OnBecameInvisible被调用。这有时很有用,但是对AI来讲就不能用了,因为你将摄像机移开敌人后,敌人就失效了,(敌人的AI就失效了)
using UnityEngine;
using System.Collections;
public class example : MonoBehaviour {
void OnBecameVisible() {
enabled = true;
}
void OnBecameInvisible() {
enabled = false;
}
}
function OnBecameVisible () {
enabled = true;
}
function OnBecameInvisible ()
{
enabled = false;
}
2. Use triggers. A simple sphere trigger can work wonders though. You get OnTriggerEnter/Exit calls when exiting the sphere of influence you want
使用触发。一个简单的范围触发能获得惊人的效果。 当你要使物体在你期望的作用范围触发事件的时候,你可以调用OnTriggerEnter/Exit
using UnityEngine;
using System.Collections;
public class example : MonoBehaviour {
void OnTriggerEnter(Collider c) {
if (c.CompareTag("Player"))
enabled = true;
}
void OnTriggerExit(Collider c) {
if (c.CompareTag("Player"))
enabled = false;
}
}
function OnTriggerEnter (c : Collider )
{
if (c.CompareTag("Player"))
enabled = true;
}
function OnTriggerExit (c : Collider )
{
if (c.CompareTag("Player"))
enabled = false;
}
3. Use Coroutines. The problem with Update calls is that they happen every frame. Quite possibly checking the distance to the player could be performed only every 5 seconds. This would save a lot of processing power.
使用协同程序。 Update的问题是他在每帧都调用。完全可以让它每5秒检查一次玩家的距离,这将节约大量的处理能力。