项目里面一直在用Time.timeScale来做游戏的 1倍 2倍整体加速,今天我仔细看了一下Time.timeScale才发现之前我理解错了一些东西。
Time.timeScale可以控制Update 和LateUpdate 的执行速度,举个例子说明一下。
Time.timeScale=1时,Update、LateUpdate、FixedUpdate 都按正常的时间来执行。
Time.timeScale=2时,Update和 LateUpdate的执行速度是之前的2倍,而FixedUpdate还是按正常时间来执行。
Time.timeScale=100时,Update和 LateUpdate的执行速度是之前的100倍,而FixedUpdate还是按正常时间来执行。(有个前提是1秒内你的CPU允许你转100帧,这里说的数字是理想数字)
Time.timeScale=0时,Update和 LateUpdate的执行速度是按正常的Update时间执行,我算了一下时间执行速度大概和Time.timeScale=1时,Update、LateUpdate的速度一样。然而此时FixedUpdate就不在执行了。
Time.timeScale也误导了我很久,为什么这么说呢?我原先一直以为Time.timeScale = 0 的话所有的Update都不执行了。今天测试了一下发现原来不是这样的,无论Time.timeScale 等于多说Update和LateUpdate都会去执行,不信你可以自己做个实验看看。
Time.timeScale还会影响Time.time的时间,比如Time.timeScale = 2的话,那么Time.time的增长速度也会变成2倍速度。如果你想取到游戏的实际时间,那么使用Time.timeSinceLevelLoad就可以,前提是必须在Awake()方法以后再取,如果在Awake()方法里面取Time.timeSinceLevelLoad会取出一个错误的值,在Start方法里面取的话就正常了。
总之一句话Time.timeScale影响的是Unity的游戏时间缩放比例。Unity里面所有跟时间有关系的东西都是根据timeScale来演算的。仔细想想现在的手游就是个 动画 和 粒子技能特效 还有UI位移特效,所以改他们的速度直接用Time.timeScale就可以完成。还有一个重要的东西就是人物移动 或者 技能移动的速度了, 根据时间的公式,时间 = 路程\速度 ,比如角色从起点跑到中间的一个预期时间, 或者一个技能的火球从攻击到打中目标的预期时间。 凡是处理时间的东东全用Time.time 这样就可以完美让Time.timeScale控制你的游戏了。
下面说说两个大家伙比较关心的话题。
1.游戏暂停
设置 Time.timeScale = 0;即可让游戏暂停。 其实我们暂停的主要是 人物动画,还有技能特效,比如一个火球打了一半。UI方面往往我们不希望暂停,比如暂停界面 有一些UI位移动画或者帧动画, 或者最起码要有个“取消暂停的按钮” 吧。 总不能游戏暂停了我点击按钮 按钮的点击动画 或者特效也暂停了吧。
所有的动画都是基于时间来的,因为Time.timeScale = 0了,所以Time.time也就不会在变化了。换句话来说如果游戏暂停以后想在暂停界面上继续播放一些不受Time.timeScale 影响的动画,那么我们就需要用到Time.timeSinceLevelLoad
如果你的项目NGUI的版本还算比较新的话,你会发现在UITweener.cs处理UI动画的基类里面已经增加了一个属性叫public bool ignoreTimeScale = true; 它就是控制控制NGUI 的UI动画是否受到ignoreTimeScale影响。如下图所示,你可以看看NGUI在Update里面的实现,它也是根据时间来判断的。忽略timescale的话就用真实时间,不忽略的话就用Time.time 和Time.deltaTime。
2.如何让游戏中某个游戏对象不受Time.timeScale影响。
动画不受timeScale影响:
http://answers.unity3d.com/questions/217351/animations-ignore-timescale.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
static
IEnumerator
Play
(
Animation
animation
,
string
clipName
,
bool
useTimeScale
,
System
.
Action
onComplete
)
{
if
(
!
useTimeScale
)
{
AnimationState
_currState
=
animation
[
clipName
]
;
bool
isPlaying
=
true
;
float
_startTime
=
0F
;
float
_progressTime
=
0F
;
float
_timeAtLastFrame
=
0F
;
float
_timeAtCurrentFrame
=
0F
;
float
deltaTime
=
0F
;
animation
.
Play
(
clipName
)
;
_timeAtLastFrame
=
Time
.
realtimeSinceStartup
;
while
(
isPlaying
)
{
_timeAtCurrentFrame
=
Time
.
realtimeSinceStartup
;
deltaTime
=
_timeAtCurrentFrame
-
_timeAtLastFrame
;
_timeAtLastFrame
=
_timeAtCurrentFrame
;
_progressTime
+=
deltaTime
;
_currState
.
normalizedTime
=
_progressTime
/
_currState
.
length
;
animation
.
Sample
(
)
;
if
(
_progressTime
>=
_currState
.
length
)
{
if
(
_currState
.
wrapMode
!=
WrapMode
.
Loop
)
{
isPlaying
=
false
;
}
else
{
_progressTime
=
0.0f
;
}
}
yield
return
new
WaitForEndOfFrame
(
)
;
}
yield
return
null
;
if
(
onComplete
!=
null
)
{
onComplete
(
)
;
}
}
else
{
animation
.
Play
(
clipName
)
;
}
}
|
粒子特效不受timescale影响
https://gist.github.com/AlexTiTanium/5676482
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
using
UnityEngine
;
using
System
.
Collections
;
public
class
ParticaleAnimator
:
MonoBehaviour
{
private
void
Awake
(
)
{
particle
=
GetComponent
<
ParticleSystem
>
(
)
;
}
// Use this for initialization
void
Start
(
)
{
lastTime
=
Time
.
realtimeSinceStartup
;
}
// Update is called once per frame
void
Update
(
)
{
float
deltaTime
=
Time
.
realtimeSinceStartup
-
(
float
)
lastTime
;
particle
.
Simulate
(
deltaTime
,
true
,
false
)
;
//last must be false!!
lastTime
=
Time
.
realtimeSinceStartup
;
}
private
double
lastTime
;
private
ParticleSystem
particle
;
}
|
经过我的测试发现timeScale = 0 时, 播放放粒子特效,效率上有很大问题非常的卡。
举个例子啊,刀塔传奇大家都玩过吧?某个角色放技能的时候,其他所有人物动作全部暂停,并且打出去的技能也暂停。等着角色的技能全部放完,别人才恢复正常。 我觉得向做这类游戏,最好就不要 利用timeScale了,不然写起来太蛋疼了。 不过刀塔传奇也没有 1 倍 2倍 3倍速 的功能吧,呵呵。
3.timeScale变化时的声音。
当声音播放的同时去修改timescale的数值你会发现声音播放的很奇怪。如下代码所示,你可以封装一个方法,把IgnoreTimeScale作为参数传进去, 如果忽略timescale的话那么速度就应该是1否则就应该是Time.timeScale。
1
2
3
4
5
6
|
//添加一个声音组件
AudioSource
source
=
gameObject
.
AddComponent
<
AudioSource
>
(
)
;
//设置播放声音的速度。 默认速度是1 ,那如果我们加速了,那就应该是 Time.timeScale
source
.
pitch
=
IgnoreTimeScale
?
1
:
Time
.
timeScale
;
//播放声音
source
.
Play
(
)
;
|
此时如果修改了Time.timeScale的数值的话,那么正在播放中的声音会非常奇怪。所以当每次播放音频的时候我们需要记录一下这个音频的状态,它是否需要忽略timeScale当前的数值。
1
2
3
4
5
6
7
8
|
//创建一个音频的字典
private
static
Dictionary
<
AudioSource
,
bool
>
soundList
=
new
Dictionary
<
AudioSource
,
bool
>
(
)
;
//播放声音
source
.
Play
(
)
;
//把音频对象加入字典中,value 就是是否忽略timescale
soundList
.
Add
(
source
,
IgnoreTimeScale
)
;
|
每当timeScale变化的时候调用一下下面这个方法。就是遍历一下当前保存的所有音频对象,从新设置一下他们的播放频率。
1
2
3
4
5
6
7
8
9
10
|
public
static
void
TimeScaleChanged
(
)
{
foreach
(
AudioSource
source
in
soundList
.
Keys
)
{
if
(
source
!=
null
)
{
source
.
pitch
=
soundList
[
source
]
?
1
:
Time
.
timeScale
;
}
}
}
|
如果你有更好的建议,欢迎在下面给我留言,谢谢。