【Unity Tips】备忘录(扫盲篇)
写在前面
Unity3D虽然是个非常方便的游戏引擎,但还是有一些地方会产生一些让人莫名其妙的问题,而且debug半天也不知道到底哪里错了。往往在经过了大量的log之后,也许我们才顿悟,原来Unity内部是这样做的啊。这里旨在总结这些容易被忽略、但是又经常会给开发造成麻烦的问题,欢迎补充。
随时更新。
备忘录
1. StartCoroutine和StopCoroutine
2. Awake、Start、Update、LateUpdate、FixedUpdate
上述函数都是MonoBehavior几个最常见的基本函数。但很少有人真正知道它们具体的调用顺序。
首先是Awake。当该脚本实例被加载时,Unity会调用它的Awake函数,在该脚本实例的整个生命周期中,它只会被调用一次。具体来说,Unity首先会加载场景中的所有对象,完成后,去扫描这些对象上的MonoBehavior,然后调用它的Awake函数。因此,我们通常使用Awake来初始化各种变量,在其中使用FindWithTag等函数也是没有问题的。Awake函数将早于任何Start函数被调用,但是Awake本身的调用顺序是不定的。
然后是Start函数。很多人认为Awake和Start的区别只在调用顺序上,实际上这是错误的。Start函数真正的调用时间,是在该脚本第一次被enable的那一帧,而且早于它的任何Update调用。那么什么是enable呢?从视觉上来说,就是Inspector面板里该脚本前面的那个勾选框是不是被选中了。我们可以通过*.enable去控制脚本的激活状态。和Awake一样,Start也只会被调用一次,但是和Awake不同的是,除了Awake先于Start外,Awake只要对象被加载就会被调用,而Start需要等到该脚本实例被enable之后才会调用。举例来说,我们没有场景中某对象上的的脚本A前面的勾选框,点击开始后,在某该脚本的Awake还是会被立刻调用,但是Start却没有被调用。这时,不暂停游戏,激活脚本A,就会看到Start函数在勾选框被勾选的那一帧被调用了。
接下来是Update函数。Update函数会在每一帧时被调用,但前提是该脚本是enable的。还是上面的例子,如果脚本A一直没有被激活,那么Update函数一直不会被调用。在它被激活的时刻,Update函数在Start函数后面被调用了。
下面是LateUpdate。从字面上就说明了它的调用顺序:在Update之后,前提是该脚本是enable的。也就是说,它也是在每一帧时被调用,但是是在所有的Update函数执行完后,再调用的。这种顺序有什么用的?举例来说,我们在Update函数里移动了某个对象的位置,而摄像机是要跟随它的位置的,这时我们就可以在LateUpdate函数里,根据该对象的位置去改变摄像机的位置。和Update类似,如果我们需要知道距离上一次LateUpdate函数的时间,可以使用Time.deltatime。
下面是FixedUpdate函数。文档中解释,该函数会在一个固定帧率的帧时被调用,但前提是该脚本是enable。固定帧率说明了它和Update函数的不同,也就是说,在每秒内它调用的次数是固定的,这个帧率可以在Project Setting/Time/Fixed Timestep中查看和修改。文档中还指出,当我们需要对刚体(Rigdbody)操作时,如施加力时,应该使用FixedUpdate,而不是Update函数,原因也是因为Update函数调用的时间间隔是不定的,而FixedUpdate是固定的。
但是!!!在实际编程中,有一个特例。举一个例子,我们在脚本A里定义了自己的函数function0,脚本A被添加到一个prefab上。当我们在脚本B中使用Instantiate函数实例化了该prefab,并且立刻调用了function0,那么是Awake、Start、Update、function0调用顺序是什么?按我最初的想法,从先到后分别是Awake、Start、Update、function0。
GameObject obj = Instantiate(prefab) as GameObject;
obj.GetComponent<A>().function0();
脚本A如下:
public int a = 0;
void Awake() {
Debug.Log("Awake " + a);
a = 1;
}
void Start() {
Debug.Log("Start " + a);
a = 2;
}
void Update() {
Debug.Log("Update " + a);
}
public void function0() {
Debug.Log("function0 " + a);
a = 3;
}
结果如下:
从上图可以看出,我们自定义的函数是先于Start和Update函数的。这有什么影响呢?如果你在function0中修改了某些变量,在Start函数里对它们进行了赋值,那么function0所做的更改就会消失;又或者某些变量在Start函数里初始化,那么调用funciton0的时候就会错空指针错误!因此,由于Unity的脚本仅自动生成了Start函数,而没有Awake函数,有些人就喜欢在Start函数里对GameObject等类型的对象初始化,这往往会造成错误。因此,建议就是,请尽量在Awake函数里进行对象初始化!当然,也建议Unity可以自动生成Awake函数,这样也许偷懒的人就会更少啦~
3. Time.timeScale和Update、FixedUpdate
基本大家知道,一般实现游戏暂停的方法就是令Time.timeScale = 0。但是我们经常会发现一些匪夷所思的问题,而很多时候是何Update函数有关。
根据官方文档可知,设置Time.timeScale为0将回暂停所有和帧率无关的事情。这些主要是指所有的物理事件和依赖时间的函数、刚体力和速度等,而且FixedUpdate会被暂停(不是Update)。但是,动画(Animations)和任何你放到Update中的通过Time.deltaTime来控制的位移也会属于和帧率无关的一类:不管你的帧率具体是多少,它们总是在相同的时间旋转或平移。
但是,Update函数本身的执行是不会受Time.timeScale的影响的。根据上一点我们知道,Update是依赖你的机器的,它的调用次数和你的机器渲染一样快慢(一些特殊情况除外);性能高的机器,帧率高,Update函数执行次数也就多。因此,当使用Time.timeScale = 0时,游戏看起来是被冻结了,这是因为所有和时间有关的事情都被暂停了。但是,我们的游戏仍在渲染,也就是说Update函数仍在执行。
还有一点,Time.timeScale为0时,Time.deltaTime将为0。这意味着,如果你使用Time.deltaTime来控制旋转和位移等,那么Time.timeScale = 0也将使这些物体停止运动。
参考:http://answers.unity3d.com/questions/270116/timetimescale-seems-doesnt-work-with-update.html
那么,正确的暂停游戏的方法是什么呢?详见《Unity技巧篇》。
4. 让Input能够正确工作
一些人总觉得自己按照文档里去读取Input,但总是没有响应似的,苦苦找不到原因。其实,大部分这类原因是因为这里面涉及到了物理运算。官网上写到,物理运算要放到FixedUpdate里面进行处理,那么如果需要用Input控制物理运动,就把Input也放到FixedUpdate里面不就可以了吗?这是错的!这是因为,Update是真正和我们机器相关,帧率受不同机器性能影响的函数,而FixedUpdate则是一个Unity自行封装定义的函数,它的默认值一般是0.02m,也就是50fps,我们可以通过Edit -> Project Setting -> Physics来改变调用间隔时间。但要注意,这个值只是一个请求,你的机子可能不能达到这个速度。
因此,Update和FixedUpdate其实是完全独立的两个函数。当Update执行了一次时,FixedUpdate可能已经执行了两次、三次甚至一次都没有执行过。下面的例子就说明了为什么把Input放到FixedUpdate中可能会无法响应。在这个例子里,Update的速率是100fps,而FixedUpdate是50fps。(来源Unity Gems)
也就是说,下面的写法是错误的:
using UnityEngine;
public class Test:MonoBehaviour{
void FixedUpdate(){
if(Input.GetKeyDown(KeyCode.Space)){
//Action
}
}
}
正确的写法是:
using UnityEngine;
public class Test:MonoBehaviour{
bool action = false;
void Update(){
if(Input.GetKeyDown(KeyCode.Space)){
action = true;
}
}
void FixedUpdate(){
if(action){
//Action
action = false;
}
}
}
上面,我们用一个boolean值来控制开关。由于Update每一帧都会调用,因此可以保证所有的输入都可以被响应。
5. 使用Quaternions来修改物体的rotation
你不应该修改四元数的x、y、z、w,除非你知道你正在干什么。如果你想要使用度数来改变物体的rotation,那么你应该使用 .eulerAngles。
如果你想知道四元数应该怎么用,你可以看这篇更详细的教程。
想要在代码里设置物体面板中的rotation,你应该像下面这样:
transform.rotation.eulerAngles = new Vector3(100,0,100);
6. 从Javascript里访问C#,或者从C#里访问Javascript
Javascript和C#不会被编译到同一个汇编中,因此不能只简单地从一个里面访问另一个。而任何在Plugins, Standard Assets 或者 Pro Standard Assets将会首先被编译,因此不在这些文件夹下的代码可以访问这些代码。
记住,如果Javascript已经在其中一个文件夹中,那么它就不可以再访问任何C#代码,即便这些代码在上述特殊的文件夹中。反之亦然。
7. Unity中的Javascript是什么
8. 为什么Coroutine在wait和yield后没有执行完
大多数原因是因为你disable掉了它所在的脚本,或者销毁了所在的对象。比如,你想要在通过Coroutine播放一个死亡动画后再销毁,或者更新完分数后销毁。
下面是写coroutine一个错误的写法:
void Update() {
if(health < 0) {
StartCoroutine(Die());
Destroy(gameObject); //or enabled = false;
}
}
IEnumerator Die() {
animation.Play("wobble");
yield return new WaitForSeconds(3);
//This will never be called
animation.Play("die");
}
上面的代码会造成,在执行完播放“wobble”动画后,该对象立即被销毁,因此不会再播放“die”动画。
正确的写法是:
bool dying;
void Update() {
if(dying) return;
if(health < 0) {
StartCoroutine(Die());
}
} IEnumerator Die() {
dying = true;
animation.Play("wobble");
yield return new WaitForSeconds(3);
animation.Play("die");
yield return new WaitForSeconds(3);
Destroy(gameObject);
}
boolean值dying将保证只会执行一次Die()函数。
9. Unity的坐标系问题
Unity使用的是左手坐标系,因此在进行旋转等工作时需要注意它的方向问题。
10. Shader中的向量和矩阵
Unity Shader使用的是CG语言,CG中的vector是row vector,因此,在定义类似float3x3这种矩阵的时候,初始化列表是按行来填充的。例如:
float3x3 m = float3x3(
1.1, 1.2, 1.3, // first row (not column as in GLSL!)
2.1, 2.2, 2.3, // second row
3.1, 3.2, 3.3 // third row
);
参考:http://en.wikibooks.org/wiki/Cg_Programming/Vector_and_Matrix_Operations
【Unity Tips】备忘录(扫盲篇)的更多相关文章
- Httpd服务入门知识-http协议版本,工作机制及http服务器应用扫盲篇
Httpd服务入门知识-http协议版本,工作机制及http服务器应用扫盲篇 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.Internet与中国 Internet最早来源于美 ...
- 转摘 MySQL扫盲篇
一下文章摘自:http://www.jellythink.com/archives/636 MySQL扫盲篇 2014-09-15 分类:MySQL / 数据库 阅读(1412) 评论(1) 为什么 ...
- Unity的NGUI插件篇——入场效果
Unity的NGUI插件篇--入场效果 入场效果 入场效果须要借助于NGUI提供的TweenPosition类来完毕.为了说明此类的用法.本节将使会解说两个演示样例.本文选自 大学霸 <NGU ...
- 分布式协调服务Zookeeper扫盲篇
分布式协调服务Zookeeper扫盲篇 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 身为运维工程师对kubernetes(k8s)可能比较熟,那么etcd(go语言实现)分布式协 ...
- HTTP/2协议–特性扫盲篇
HTTP/2协议–特性扫盲篇 随着web技术的飞速发展,1999年制定的HTTP 1.1已经无法满足大家对性能的要求,Google推出协议SPDY,旨在解决HTTP 1.1中广为人知的性能问题.SPD ...
- 高级Linux运维工程师必备技能(扫盲篇)
高级Linux运维工程师必备技能(扫盲篇) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 在了解文件系统之前,我们要学习一下磁盘存储数据的方式,大家都知道文件从内存若要持久化存储的 ...
- C语言扫盲篇
C语言扫盲篇 作者:尹正杰 版权声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接. 一.什么是C语言 C 语言是一种通用的高级语言,最初是由 ...
- 01--Qt扫盲篇
Qt扫盲篇 1.What is Qt 一个跨平台应用程序和UI开发框架,主要偏向于UI框架方面,由诺基亚公司开发维护. 使用 Qt 只需一次性开发应用程序,无须重新编写源代码,便可跨不同桌面和嵌入式操 ...
- MySQL数据库扫盲篇
MySQL数据库扫盲篇 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.MySQL概述 1>.什么是MySQL MySQL是瑞典的MySQL AB公司开发的一个可用于各 ...
- C#扫盲篇(三):Action和Func委托--实话实说
一.基础定义 老王想找老张的老婆出去耍,但是一看,老张还在厨房煮饭.于是老王就对老张隔壁的淑芬说:"等下老张吃完饭出去喝茶,你就把前门晒的苞谷收了,老张从左门出,你就收右边的苞谷,我就知道从 ...
随机推荐
- css修改浏览器默认的滚动条样式
//滚动条样式 ::-webkit-scrollbar { width: 10px; } /* 垂直滚动条的滑动块 */ ::-webkit-scrollbar-thumb:vertical { bo ...
- 55. Jump Game(中等)
Given an array of non-negative integers, you are initially positioned at the first index of the arra ...
- geotrellis使用(四十)优雅的处理请求超过最大层级数据
前言 要说清楚这个题目对我来说可能都不是一件简单的事情,我简单尝试. 研究 GIS 的人应该都清楚在 GIS 中最常用的技术是瓦片技术,无论是传统的栅格瓦片还是比较新颖的矢量瓦片,一旦将数据切好瓦片就 ...
- Java内存泄漏分析系列之三:jstat命令的使用及VM Thread分析
原文地址:http://www.javatang.com 使用jstat命令 当服务器CPU100%的时候,通过定位占用资源最大的线程定位到 VM Thread: "VM Thread&qu ...
- 六星经典CSAPP-笔记(7)加载与链接(上)
六星经典CSAPP-笔记(7)加载与链接 1.对象文件(Object File) 1.1 文件类型 对象文件有三种形式: 可重定位对象文件(Relocatable object file):包含二进制 ...
- Redis之(四)事务
5.1开始事务 MULTI 命令的执行标记着事务的开始: 当客户端处于非事务状态下时, 所有发送给服务器端的命令都会立即被服务器执行. Redis 的事务不可嵌套, 当客户端已经处于事务状态, 而客户 ...
- AsyncTask还要知道的一些知识
在之前的博客中,对AsyncTask做过详细分析,而且也以小案例的形式,介绍如何基本的使用它.今天再来探讨它,更多的认识,尤其在面试中以下面方式回答,可能印象分更多一些. 面试题:讲一讲您对Async ...
- solr多集合配置
1.1 多SolrCore配置 一个solr工程中可以配置多个SolrCore实例. 分享牛原创(尊重原创 转载对的时候第一行请注明,转载出处来自分享牛http://blog.csdn.net/qq_ ...
- Android 6.0出现的init: cannot execve(‘XXX’):Permission denied问题:禁止SELINUX的权限设置
最近在开发MTK的相关项目,需要将一些可执行文件添加到init.rc文件里去,但是开机后发现,这个bin文件没有权限不能执行,于是我就在init.rc中对相应的bin文件增加了权限.后来发现,改了也没 ...
- Apache shiro集群实现 (一) shiro入门介绍
近期在ITOO项目中研究使用Apache shiro集群中要解决的两个问题,一个是Session的共享问题,一个是授权信息的cache共享问题,官网上给的例子是Ehcache的实现,在配置说明上不算很 ...