【发布时间】:2016-04-11 15:49:51
【问题描述】:
我看到我们可以在Awake() 或Start() 中初始化变量,并且Awake() 将在Start() 之前被调用。
我们应该在什么时候初始化Awake 和Start 以获得最佳性能?
【问题讨论】:
标签: unity3d
我看到我们可以在Awake() 或Start() 中初始化变量,并且Awake() 将在Start() 之前被调用。
我们应该在什么时候初始化Awake 和Start 以获得最佳性能?
【问题讨论】:
标签: unity3d
通常Awake() 用于在某些值或脚本相互依赖时进行初始化,如果其中一个值或脚本初始化得太晚(在游戏开始前唤醒运行)会导致错误。
每个脚本实例也只调用一次 Awake。
让我引用文档:
[...] 在所有对象初始化后调用 Awake,这样您就可以安全地与其他对象交谈或使用例如查询它们。游戏对象.FindWithTag。每个 GameObject 的 Awake 在对象之间以随机顺序调用。因此,您应该使用 Awake 设置脚本之间的引用,并使用 Start() 来回传递任何信息。 Awake 总是在任何 Start 函数之前调用。这允许您订购脚本的初始化。 Awake 不能充当协程。
关于Start():
当脚本在任何之前启用时,在框架上调用 Start 更新方法的第一次被调用。
与 Awake 函数一样,Start 在生命周期中只被调用一次 的脚本。但是,当脚本对象是 初始化,无论脚本是否启用。 开始 如果脚本不是,则不能在与 Awake 相同的帧上调用 在初始化时启用。
最后一部分有很大的不同
回答您的问题:
如果脚本在游戏开始时未启用,并且您不需要初始化变量,start 会节省性能 因为 awake() 无论如何都会被调用...
每个变量都会在一开始就被初始化。至少这是我做出的合乎逻辑的假设。
【讨论】:
Start() 中做所有事情将意味着没有任何东西被浪费(即初始化可能永远不会被使用的东西),但是它可能会在第一帧中导致一些延迟,也许?
Awake() 的延迟会稍微不那么明显。
【讨论】:
Start 链接对象的建议。我还要补充一点,通过利用Script Execution Order 来修改脚本排序以处理Awake 上的依赖关系链接具有额外的灵活性。
The difference between Awake and Start is that Start is called only when a script is enabled. 是的,只是想补充一点:请记住,如果 GameObject 处于非活动状态,则甚至不会调用 Awake
Awake 相当于 ctor。它在创建 MonoBehaviour 时调用,在任何其他方法之前调用。
在第一次激活 MonoBehaviour 时运行 Start。这可以在 Awake 之后或很久之后。这允许执行与应用或对象的当前状态相关的操作。
例如,您创建了一个敌人,在 Awake 中,您放置了所有基本初始化的东西。然后,敌人在觉醒结束时被停用。稍后,敌人即将被激活,但如果玩家有某种特定的武器,你想让它变成红色,然后你在开始时做。
OnEnable 与 Start 类似,但在每次 SetActive(true) 时发生,如果启用则在启动时发生。例如,这可能是您的敌人愿意根据玩家的魔力在关卡中改变颜色的候选对象。
【讨论】:
性能没有太大差异。但我可以告诉你它们之间的区别。
举个简单的例子。假设您想在控制台中打印“HELLO”,即使您没有在检查器中激活脚本,使用 Awake() 函数,您可以在控制台中打印它。但是如果你在 Start() 函数中写了同样的东西并且脚本没有被激活,你不会在控制台中得到任何输出。这就是区别。
Start() 函数中的代码仅在脚本被激活时才会执行,而 Awake() 函数中的代码即使在脚本未激活时也会被执行。试试吧!
【讨论】:
我声称根本没有真正的性能差异。
Minzkraut 声明了什么
如果在游戏开始时未启用脚本,并且您不需要初始化变量,则 start 将节省性能,因为无论如何都会调用 awake()... 每个变量都会在一开始就被初始化。至少这是我做出的合乎逻辑的假设。
只是半正确的。如果您只在Start 中执行所有操作,则用户体验可能会更糟,因为在启动应用程序时不会出现更大的延迟——在我看来这是完全可以接受的——它可能会导致游戏过程中出现更小但更多的延迟,我个人会避免这种情况尽可能的。
一般来说,其他答案中已经经常解释如何以及何时调用 Awake 和 Start,并且基本上它“只是”时间差异。
详情请咨询Execution Order of Events
将禁用的组件和非活动的游戏对象放在一边,我个人的经验法则是
将Awake 用于您不依赖任何其他组件和引用的所有内容。
例如设置默认字段值,使用GetComponent 等填充内容
将Start 用于您依赖其他组件的所有事情,例如访问GetComponent 的结果
这样,这些其他组件应该已经收到了他们的Awake 调用,所以他们自己已经链接起来并准备好被其他人使用。
这在大多数情况下解决了时间和依赖性问题。
如果这还不够,您将开始调整 Script execution order 或使用事件。
然后在使用Instantiate或AddComponent的用例上还有一个相当重要的区别
Awake(和OnEnable,除了使用Instantiate并且组件被禁用)将在实例化期间立即调用,Instantiate之后的任何代码行都将在之后执行结束了。
Start 但是会延迟到该帧结束(或者如果组件在Awake 中被禁用,则直到启用)。
这使您有足够的时间使用Instantiate 并在调用Start 之前修改某些字段值,并且现在可以根据修改后的字段值来执行其行为。
例如这个
public class Example : MonoBehaviour
{
private class X : MonoBehaviour
{
public int x = -1;
private void Awake()
{
Debug.Log($"Awake {x}");
x = 12;
}
private void OnEnable()
{
Debug.Log($"OnEnable {x}");
}
private void Start()
{
Debug.Log($"Start {x}");
}
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
Debug.Log("Instantiate");
var x = gameObject.AddComponent<X>();
Debug.Log("After instantiate");
x.x = 42;
}
}
}
将产生输出
Instantiate
Awake -1
OnEnable 12
After Instantiate
Start 42
【讨论】: