每一桢用的时间,叫做SPF (Second per frame)
倒过来,每秒运行的桢数,叫FPS (Frame per second)
打CS的时候,有的版本可以看到屏幕上画着 FPS:
后面一个数字,不停的变。
这个东西越高越好,表示游戏速度越快。
怎么计算FPS:
在开始的时候记录一下系统时间,在结束的时候又记录一下系统时间,
两个时间一减,就知道这一桢用的时间了(SPF)。 1/SPF
就是FPS.
限桢的概念:
有时候一个循环奇快,有时候一个循环奇慢.
打游戏的时候, 你看着画面时快时慢, 肯定不中.
就要限制一下.
先规定好一个FPS,假如是每秒20桢,那么一桢用的时间就是50毫秒。
在 结束
的地方,判断,如果这一桢用的时间小于50毫秒,记录下还差多少时间到50毫秒,
然后就让程序睡觉,睡够这个差值,使这一桢整体使用的时间达到50毫秒。
这样平均下来游戏的FPS就是20,很稳定。
如果一桢的时间超出了50毫秒,那就没办法了。这就是游戏运行慢的原因,这时要想办法看看是做了什么事,导致程序运行缓慢。
魔兽肯定采用了比较高级的控制FPS的方法,如果卡,他就使用很低的FPS,不卡了之后,他就用很快的FPS补上。
这样看起来游戏
在网络速度不好的情况下,很迟钝的样子,然后再网络速度好了以后,刷刷刷运行的飞快。
神奇的是,他们能计算出桢的速度变化
播放动画为什么还要有个系统待我慢慢讲来。
状态机大家都知道了,就是无限循环的东西。
动画是由一桢一桢组成的,这个桢和游戏的桢概念不同。但含义都差不多。
每一桢就是组成动画的一幅画,上一桢和下一桢可能相同也可能不同,连续播放就可看到动画的效果 比如一个动画由12桢组成:(假设 | \—/都是小棍形状)
|, |, |, \, \, \, —, —, —, /, /, /
那么连续播放的话,就是播完从头再拨,就可以看到一个小棍儿在不停的转。
那么在一个不断循环的系统中播放动画,播完一遍需要12次循环,也就是12桢。
为什么相同的桢要播放三次,那是因为你要控制它播放的速度。
如果每桢播一次,机器又很快,就是FPS很快,你就会看到一个小棍儿在疯转。
相同的一个动画,如果你的机器快,他的机器慢,在上面看起来效果就不一样。
但是之前我们讲过FPS,讲过限桢的概念。
就是把一桢的时间控制在相同的范围。
如果程序限了桢,不管在哪个机器上,绘制一桢的时间都是确定的。
那么这个动画在哪看起来速度都一样。
那么现在看看代码怎么写呢?
一个动画有12桢意味着要播12次。
int frameIndex = 0; //用来记录一个动画的当前桢。
while (true) {
if (frameIndex == 0) {
// 画 |
} else if (frameIndex == 1) {
// 画 |
} else if (frameIndex == 2) {
// 画 |
} else if (frameIndex == 3) {
// 画 \
} else ………..
………………..
frameIndex ++; //上面的代码确保每次只画一桢,这句话在画完了之后把frame往下推一个。
}
这样的代码很麻烦,也肯定不是一个成熟的游戏需要的。
更简单些,这样:
while (true) {
playAnimation();
这个playAnimation() 这样写:
int frameIndex = 0;
Image[] animationFrames;
//定义一个图像数组来存储一个动画的每一桢图像。假设已经被装载好了。假设是上面那幅动画,这个数组里面就存了12幅画。
(实际不是这样,还要更仔细一些,例如只存二进制数据而不是一个图像,到了播放时才即时创建一幅出来。而且相同的桢数据不会重复放在里面。
现在只是举个比上面的代码好点儿的例子。)
public void playAnimation() {
Image currentFrameImage = animationFrames[frameIndex]; //把当前桢的那幅图给取出来
if (playAnimation) { //现在假设没有这个变量,往后看。
drawImag(currentFrameImage,…);//画到屏幕上。(代码没写全,drawImage还有很多参数)
frameIndex ++; // 桢往后推个1。下次画就是画下一桢了。
}
if (frameIndex > animationFrames.length) {// 如果播完了12桢怎么办?自己决定,是重头画还是停止。
//如果重头画
//frameIndex = 0;
//如果让这个动画画一遍就停下.
// playAnimation = false;
//现在知道有什么用了吧?虽然playAnimation还在被一直调用,但已经没有东西画出来了。
}
恩, 动画的播放原理差不多就这样。
然后再说说详细说说动画的组成。
动画是由一桢一桢的图像组成。
而每一桢的图像又是一块一块的小图拼成。
为什么要这样做?
一方面,在j2me程序中,程序+资源包的大小十分受限。
假如做一个小人儿的动画,就算10桢,就需要10幅画。
如果再多做几个小人儿动画,就是10xN幅画,太大了。
这样,就可以把各种各样小人儿的头,不同形状,不同动作,专门做成一整套,一个一个的,或者说看起来一块一块的,拼到一个大图上。
小人儿们的腿,身子,胳膊等等,也都按不同形状,不同动作等分别做成一整套。
这样,就可以从这些胳膊腿儿里面挑一个形状,挑一个动作,组成一个小人,这就形成了一桢。
不同的形状,不同的动作的组合可以做很多个不同的动画,但是资源大小并没有改变。(不能贴图,google应该改进.)
另外一方面,碰撞检测。
在播放动画的时候,往往需要检测一个动画和另一个动画是否碰撞在一起。
例如,街霸。
那么检测碰撞,不是检测整个一桢的碰撞,而是这一桢中,人的胳膊腿儿等的碰撞。
这样,把一大块桢分开成小的模块,也更合理。
这个组成桢的基本单位,术语称作"Module"。
在这里不多说,其实我也说不出啥东西。还是要自己做,自己研究。
还有动画的应用。
例如我们要播放一个片头动画,这很常见。展示我们的logo之类的。
就这样。
while (true) {
playAnimation(mylogo);
我们要让主角行走。
while (true) {
player.setAction(walk); //把动画封装在一个player类中。
player.update(); // 之前玩家已经把动作设置成为walk了。现在来更新这个动作。其实就是动画的播放
public class Player {
int action;
public void update() {
switch (action) {
case walk:
playAnimation(walkAnimation); //呵呵,怎么样?这就差不多是AI了,只不过这个AI只是会播放动画
//再随便瞎写几句
if (keyPressed(Num4)) {
//如果按了四键,就让玩家的位置发生变化。相应的,playAnimation里绘制桢的坐标就发生了变化。
//这样看起来,主角就是一边做walk的动作一边移动了。呵呵。像真实世界中的。
posX -= 20; //往左移动。
}
break;
}
}