首先看看MonoBehaviour的生命周期

先上个图(来源 http://blog.csdn.net/qitian67/article/details/18516503):

1.Awake 和 Start的区别

相信很多人都有个类似的疑惑: 在MonoBehaviour中,为什么会有Awake 和 Start 函数? 他们又有何区别?

这个在我初学U3D时也有过的疑惑,但是通过实践得出结论,Awake 会在MonoBehaviour 创建时候即被调用,相当于构造函数。
而Start 函数 会在下一帧更新时 才会被调用到,并仅只调用一次,而Update 函数会在 Start 函数的下一帧开始被调用。

所以有时稍微不注意, 就会出现如下的Bug:

 public class MonoTest{
public int testNum = ;
public void Awake(){
testNum = ;
print("AWAKE :"+testNumber);
}
public void Start(){
testNum = ;
print("START :"+testNumber);
}
} //-----------------------
// Create and Invoke
public class InvokeTest{
MonoTest mt;
bool inited = false;
void Update(){
if(!mt){
mt = gameObject.AddCompoment<MonoTest>();
//AWAKE: 2
//如果在此处修改 mt.testNum 的值, 则将在下一帧会调用MonoTest.Start 被覆盖为3
}
//mt 刚创建时:
// false 2
// Update 在以后调用的输出结果
// true 3
print(inited+" "+mt.testNum); inited = true;
}
}

2. Destroy 与 DestroyImmediate

Destroy 与 DestroyImmediate 和 Start 与 Awake 其实也一样,不过我是到今天才知道,也是我写下此文的主因。

我第一次用 DestroyImmediate 的时候 是在做编辑器插件(学习 iTweenPath的源码 来做编辑器),当时需要在编辑器下点击一下button 立即删除,代码大概如下:

using UnityEngine;
using System.Collections; [ExecuteInEditMode]
public class DestroyInEditor : MonoBehaviour { public GameObject destroyTarget;
public bool destroy = false; void Update () {
if(destroy && destroyTarget)
{
Destroy(destroyTarget);
destroy = false;
}
}
}

当在Inspector 上将 destroy 设置为true,便会将destroyTarget 销毁。

但是在编辑器模式下会抛出一下异常:

Destroy may not be called from edit mode! Use DestroyImmediate instead.
Also think twice if you really want to destroy something in edit mode. Since this will destroy objects permanently.
UnityEngine.Object:Destroy(Object)

编辑模式下 不能调用Destroy,请使用 DestroyImmediate 。 当时因为英语不好,不明白Immediate 是什么意思,虽然知道是立即,但是并不了解 Destroy 和 DestroyImmediate的区别。

知道今天 使用NGUI.Table ,NGUI.Table 可以将所有子对象进行排版,但是我有一个要求是当点击next 的时候,将Table所有子对象清空,并添加新的子对象s再进行排版。

问题来了,要清空所有子对象,调用Table.RemoveChild()并没有用,原因可以自己查看 NGUI 的源码。

所以我自己写了个函数 将所有子对象清空:

//伪代码
public void Clear(){
foreach(var c in children){
Destroy(c.gameObject);
}
}

并新增 新的子对象

//伪代码
public void Reset(List<Transform> newList){
Clear();
foreach(var c in newList){
// TODO: reset localScale
c.parent = transform;
}
table.Reposition();
}

新问题出现了,排版功能有BUG了。 但是经过我手动 Reposition(Inspector上右击Table 点击Execute) 排版正常了。

而问题就是处在 Destroy 函数.

下面我们进行模拟测试一下,简化这个问题
测试环境

测试代码

using UnityEngine;
using System.Collections; public class DestroyTest : MonoBehaviour { public bool destroy = false; void Update () {
if (!destroy)
return; var childCount = transform.childCount;
print("Destroy Before:" + childCount); for(var i = ; i < childCount; ++i)
{
Destroy(transform.GetChild(i).gameObject);
} print("Destroy After:" + transform.childCount); destroy = false; }
}

测试结果:

Destroy Before:
Destroy After:

也就是说,当我们调用Destroy 时,Unity3D并没有真的将gameObject销毁,而是将gameObject 设置为Destroy标记。待到更新下一帧的间隙时,才真的将gameObject销毁。至于这个间隙,一般是在Render(GPU工作中)时 利用相对空闲CPU 将这些gameObject处理,当然除了处理Destroy Unity3D 还很多其他事。

答案呼之欲出了,将Destroy改为 DestroyImmediate 即可,现在终于明白立即原来是这个意思!

3.碰撞检测

说到碰撞,先了解下U3D的碰撞组件,看这里:http://www.cnblogs.com/neverdie/p/Unity3D_RigidBody2D_Collider2D.html

我这里先copy两张比较重要的图作备用

如果对一个碰撞器勾选了Is Trigger选项,它就不会与其他没有勾选Is Trigger的碰撞器发生刚体碰撞,而会发生“Trigger 碰撞”,也就是说,这时碰撞时发送的消息是Trigger消息,而不是Collision消息,相应地在脚本中我们要对OnTriggerEnter进行重载,而不是对OnCollisionEnter进行重载。

下图对Collision和Trigger进行了总结,在分别勾选某些属性时,都会发送哪些消息:

这里并不是要说碰撞,而是说 FixedUpdate 和 Update, 根据上图我们都知道FixedUpdate 有可能因为更新物理的原因而在一帧内被调用多次,而Update 一帧最多只调用一次。

最初我以为 FixedUpdate 和Update 是多线程同步进行,但其实不是。凡是实际到脚本的代码都只能单线程处理!注:除了WaitForEndOfFrame.

所以, 有时候移植其他游戏的时候发现一些代码会进行比较底层的碰撞检测,例:

public class Player
{
// player 的当前位置
int x;
int y; //将player 移动到 y+offsetY 的位置 (
// 返回值: player 实际移动的位置
// 如果没有碰撞 则 返回值=offsetY
// 如果中途产生碰撞 则返回player 下落的位置
public int MoveToY(int offsetY)
{
if(HitTest("block"))
throw new Exception("已经产生碰撞,不能移动");
for(var i=;i<=offsetY;i+=offsetY/)//有什么余数的暂时不考虑
{
y += i;
if(HitTest("block"))
{
y -= offsetY/;
return i - offsetY/; //返回上一次的移动结果
}
y -= i;
}
y += offsetY;
return offsetY;
} }

但是如果移植到U3D 并使用 U3D自带的物理引擎碰撞系统,就不一定work了。因为你移动的过程中其实并没有将实际的移动位置更新到物理引擎,只是做了个缓存而已,只有在调用FixedUpdate的内部函数(物理引擎处理)时,才会将最新的位置设置到物理引擎上,甚至是渲染引擎也使用最新的位置。

测试代码:

using UnityEngine;
using System.Collections; public class CollidingTest : MonoBehaviour { void Update () {
var p = transform.localPosition;
//p.x = 1;
//transform.localPosition = p;
//p.x = 0;
//transform.localPosition = p;
for(var i=0.0f;i<;i+=0.02f)
{
p.x = i;
transform.localPosition = p;
}
p.x = ;
transform.localPosition = p;
} private void OnTriggerEnter2D(Collider2D other)
{
print("ENTER:"+other);
} //private void OnTriggerStay2D(Collider2D other)
//{
// print("STAY:"+other);
//} private void OnTriggerExit2D(Collider2D other)
{
print("EXIT:"+other);
} }

整个过程中并没有发生碰撞callback,这个是我们需要注意的,至于怎么解决,我还在思考当中。以后会给个答案!

4.WaitForEndOfFrame

刚才第3点已经提到了WaitForEndOfFrame了,一般是这样使用

public class WaitForEndFrameTest{
void Awake(){
StartCoroutine(CallPluginAtEndOfFrames());
} IEnumerator CallPluginAtEndOfFrames(){
while (true){
// Wait until all frame rendering is done
//TODO: Render Plugin but not component
yield return new WaitForEndOfFrame();
GL.IssuePluginEvent();
}
} }

这是我在写渲染插件的带马上摘抄下来的,。。。

这里我也懵圈了,我印象中 在TODO上是不能修改有关U3D的任何东西的,因为此时是GPU渲染时候,如果中途修改了transform的信息会出BUG。

但是经过测试发现,在 CallPluginAtEndOfFrames中修改 transform的信息并没有真的改变了gameObject 的位置。

using UnityEngine;
using System.Collections;
using System.Threading;
using System.Collections.Generic; public class WaitForEndFrameTest : MonoBehaviour
{
//List<int> list = new List<int>();
void Awake()
{
StartCoroutine(CallPluginAtEndOfFrames());
} private void Update()
{
var pos = transform.localPosition;
print("UPDATE " + pos +" "+Thread.CurrentThread.ManagedThreadId);
} IEnumerator CallPluginAtEndOfFrames()
{
while (true)
{
// Wait until all frame rendering is done
//TODO: Render but not component
var pos = transform.localPosition;
pos.x = ;
transform.localPosition = pos;
yield return new WaitForEndOfFrame();
pos = transform.localPosition;
pos.x = ;
transform.localPosition = pos;
GL.IssuePluginEvent(); print("HELLO "+pos+" "+Thread.CurrentThread.ManagedThreadId);
//list.Add(3);
}
}
}

GameObject 加上测试脚本后运行,刚开始 HELLO 和 UPDATE 都有输出,但是一旦在编辑器中 修改了 GameObject的位置, CallPluginAtEndOfFrames 就再也没有迭代下去了?

有待更详细的测试和验证。

Unity3D 中 脚本(MonoBehaviour) 生命周期WaitForEndOfFrame需要注意的地方的更多相关文章

  1. Unity3D脚本(MonoBehaviour)生命周期

    场景中有2个物体:A,B 每一个物体上绑定2个脚本:A,B 初始化log: Object : A , Script : B , Message : Awake Object : A , Script ...

  2. unity3d中脚本生命周期(MonoBehaviour lifecycle)

    最近在做一个小示例,发现类继承于MonoBehaviour的类,有很多个方法,于是乎必然要问出一个问题:这么多个方法,执行先后顺序是如何的呢?内部是如何进行管理的呢?于是在网上找了许多资料,发现了Ri ...

  3. (转)unity3d中脚本生命周期(MonoBehaviour lifecycle)

    自:http://blog.csdn.net/qitian67/article/details/18516503 最近在做一个小示例,发现类继承于MonoBehaviour的类,有很多个方法,于是乎必 ...

  4. Unity3d脚本的生命周期

    接下来,做出一下讲解:最先执行的方法是Awake,这是生命周期的开始,用于进行激活时的初始化代码,一般可以在这个地方将当前脚本禁用:this.enable=false,如果这样做了,则会直接跳转到On ...

  5. Unity脚本的生命周期中几个重要的方法

    1.function Update () {} 正常更新,用于更新逻辑.此方法每帧都会由系统自动调用一次.2.function LateUpdate () {} 推迟更新,此方法在Update() 方 ...

  6. 【转】Unity3D中脚本的执行顺序和编译顺序

    支持原文,原文请戳: Unity3D中脚本的执行顺序和编译顺序 在Unity中可以同时创建很多脚本,并且可以分别绑定到不同的游戏对象上,它们各自都在自己的生命周期中运行.与脚本有关的也就是编译和执行啦 ...

  7. Unity3D笔记八 Unity生命周期及动画学习

    Unity脚本从唤醒到销毁有着一套比较完善的生命周期,添加任何脚本都必须遵守自身生命周期法则.下面介绍一下生命周期中由系统自身调用的几个比较重要的方法.  Update(){}.正常更新,用于更新逻 ...

  8. MonoBehaviour生命周期

    MonoBehaviour生命周期 上图中重要的信息点很多,需要特别注意的是所有脚本的Awake方法都执行完才会执行Start,但是如果在Awake 中开启了一个协程这个协程中每一帧执行一些操作然后等 ...

  9. spring IOC 容器中 Bean 的生命周期

    IOC 容器中 Bean 的生命周期: 1.通过构造器或工厂方法创建 Bean 实例 2.为 Bean 的属性设置值和对其他 Bean 的引用 3.调用 Bean 后置处理器接口(BeanPostPr ...

随机推荐

  1. [WSGI]WSGI的服务端怎么实现?

    WSGI ( Python Web Server GateWay Interface) 是一个用在python web server 跟web application 之间的协议,遵从WSGI协议设计 ...

  2. python大法好——Python SMTP发送邮件

    Python SMTP发送邮件 SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件的规则,由它来控制信件的中转方式. py ...

  3. php进阶--菜鸟之路

    希望有所帮助! 第一阶段:基础阶段(基础PHP程序员) 重点:把LNMP搞熟练(核心是安装配置基本操作) 目标:能够完成基本的LNMP系统安装,简单配置维护:能够做基本的简单系统的PHP开发:能够在P ...

  4. linux io的cfq代码理解

    内核版本: 3.10内核. CFQ,即Completely Fair Queueing绝对公平调度器,原理是基于时间片的角度去保证公平,其实如果一台设备既有单队列,又有多队列,既有快速的NVME,又有 ...

  5. logback kafkaAppender输出日志到kafka

    官网地址https://github.com/danielwegener/logback-kafka-appender 本文以spring boot项目为基础,更多的信息,请参考官网 https:// ...

  6. c#实现文件写入数据表/以二进制流保存到数据库,并实现下载

    上传: 1.上传文件先保存到服务器   File.SaveAs(path) 2.sql(文件和sql在一个服务器上)进行保存操作: insert into File(filename,filebody ...

  7. Win10系统总是提示"在商店中查找应用"的关闭方法

    Win10系统总是提示"在商店中查找应用"该怎么关闭?win10中打开文件的时候总是提示在商店中查找应用,但是自己的电脑中有程序可以打开这个文件,不需要去商店中下载,该怎么取消这个 ...

  8. pyautogui 文档(三):键盘控制

    typewrite()函数 主要的键盘功能是typewrite().此函数将键入字符串中传递的字符.要在按下每个字符键之间添加延迟间隔,请为interval关键字参数传递int float . > ...

  9. CP IPS功能测试

    测试环境:CP 15000硬件 测试拓扑: Step1:重新安装Check_Point_R80.10_T479_Gaia并且打补丁 Step2:初始化CP并且部署模式为Management和Gatew ...

  10. formValidator 插件 使用总结

    1. 大小写的问题, formvalidator 的属性 大小写问题一定要注意, 在踩过的坑里就包括这个, 把所有属性的第二个字母的开头全部写成大写 ,导致提示信息不能用,这个问题纠结了好久 2.er ...