很久没有更新BLOG了,最近在CSDN上看到一个关于如何平滑移动图片的问题,因为此类问题之前也回答过很多次,因此觉得有必要将我的经验放在BLOG上让大家分享一下,当然,以后在回答此类问题的时候更可以大笔一挥,贴上个链接,自己看去,免得一次又一次打字了。
好了言归正传,首先看一下问题是什么:平滑移动,这里包含了两个部分,“移动”和“平滑”
恩,这不是废话,既然我要分开这两个词,并不是用来骗稿费的,当然,也没有人给。
大家一定玩过飞行射击的游戏吧?对了,什么小蜜蜂啊,雷电啊等等都是。什么?没玩过?去下个雷电玩玩补一下课先。
屏幕上飞来飞去的飞机也好子弹也好,有快的也有慢的,作为一个编程爱好者来说,你是否有想过他们的移动是如何实现的呢?
贴图!回答正确,但是等于没说。
大家都知道显示器的屏幕有一个最小的单位,就是象素,也就是说上面飞来飞去的飞机也好子弹也好,它们每一次移动的距离一定是这个单位的整倍数,恩,想想也是,移动半个象素,显示器它也没这个能力啊。
×××这里作个标记,后面说明为什么
好了,假设我们现在开始写个小小的飞行射击游戏。
要定义很多数据吧,比如我方飞船的飞行速度,敌方飞船的飞行速度,子弹的飞行速度....
如何定义呢?
恩,我方飞船的速度可以慢一点,假设是3,为什么慢呢?因为有我们玩家控制呀,速度慢点才显示出控制能力高么。
敌方飞船速度么,当然比我们的高一点,那就4吧。
子弹速度呢?恩,当然要比地方飞船快吧,子弹追不上飞船那还打什么呀?好,那就5吧
MySpeed=3
EnemySpeed=4
BulletSpeed=5
其他,什么按键检测啊(除非你想看DEMO),什么碰撞检测(判断子弹是否击中,我方飞船是否和敌方相撞),什么精灵贴图啊(就是光贴飞船和子弹的图,去掉背景的那种),什么爆炸画面呀,背景音乐呀...我就当它已经做好了,或者直接不考虑,那不是重要的,至少对于我们这个话题来说不是重点。
开始运行一下你的程序。恩,发现很好,你的程序能运行,说明你的编程能力好,没有硬伤。
但是,你有没有觉得飞船和子弹的飞行有点象是在“瞬移”呢?可不是么,程序循环一次就移动了3,4个象素,能不“瞬移”么。
那怎么办?
回答:降低速度!
好办法,开始改代码(其实也就是改了几个数字)
MySpeed=2
EnemySpeed=3
BulletSpeed=4
运行,恩,似乎好点了,但是还是在“瞬移”
这可难不倒我们,继续改吧:
MySpeed=1
EnemySpeed=2
BulletSpeed=3
运行,恩,很好,几乎感觉不到“瞬移”了,但是,这个控制怎么有点别扭呢,老是被敌人的飞船撞死。也对,现在敌方飞船的速度是我们的一倍,控制再好也难躲呀。
恩,现在可把我们的程序员给难住了,难道把敌方飞船速度改成和我们一样?那大家是同样的速度,敌方飞船要撞到我们就很难咯,太简单的游戏可不好玩。可是它移动要么就是1个象素,要么就是2个象素,总不能来个1.5吧?
怎么办呢?难住了,好好想,仔细想想如何解决这个大难题。
...
经过一整夜的思考(恩,没错,真正的程序员都喜欢在夜里开工,思路比较清晰也没有人打扰,多好)
在太阳快要升起的时候,他终于有了一个天才般的灵感(自我感觉良好):谁说速度一定就要是个整数呢?显示器固然只能在整数的位置上显示,可没说速度也一定要跟着这个来呀。
恩,或许看着篇文章的朋友有点糊涂了。是啊,难道这个程序员想出了显示半个象素的方法?这这这可能么?
当然不可能,因此请大家接下来仔细听我细细道来。
对了,从上面标注的地方开始,全部推倒作废,因为事实证明那不是个好办法。
首先,把速度定义为一个浮点数,没错,就是带小数的那种,偶们要玩大的了,就用几个比较特殊的数字来举例子:
MySpeed=0.9
EnemySpeed=1.6
BulletSpeed=2.3
翻开初中物理书,上面是如何定义“速度”这两个字的?
物体在单位时间内经过的距离叫做速度。
假设我们的代码在一秒钟内可以循环40次(40FPS)
那么我们的飞船移动的距离为:40*0.9=36
地方飞船移动的距离为:40*1.6=64
子弹飞行的距离为:40*2.3=92
那到底是如何解决了半个象素的问题呢?
说起来也简单,就是用了一个累加器:
因为屏幕是2维的,因此我们用X和Y两个坐标来解释。
每一个飞行对象都建立两个变量来记录它们的位置,这里我们用敌方飞船来据例子:
定义两个浮点型的变量:EnemyX 和 EnemyY
先来一个初始位置,假定敌方飞船的出场位置是在屏幕上方的正中间吧:
EnemyX=300.0
EnemyY=0.0
地方飞船的飞行是按照某种规律(可以是你代码预先设定好的,当然也可以给它加点“智慧”自主一点)它在不停改变位置,简单起见我们就当它是45度向右下方飞吧:
第一次循环:
EnemyX = EnemyX + EnemySpeed = 300 + 1.6 = 301.6
EnemyY = EnemyY + EnemySpeed = 0 + 1.6 = 1.6
然后在显示的时候呢,稍微处理一下,可以直接取整去掉小数(当然也可以四舍五入)
此时地方飞船出现在位置(301,1)处。
第二次循环
EnemyX = EnemyX + EnemySpeed = 301.6 + 1.6 = 303.2
EnemyY = EnemyY + EnemySpeed = 1.6 + 1.6 = 3.2
此时地方飞船出现在位置(303,3)处
第三次循环:(304,4)
第四次循环:(3.6,6)
...
光从坐标上来看,似乎飞船的速度是不稳定的,一会移动1个象素,一会移动2个象素。
但是如果你的程序一秒钟能运行10次循环以上(如果不到10次,我觉得你的程序应该好好精简和优化了)那你的眼睛是绝对不会觉得别扭的。
而且飞船也确实在10次循环中移动了10*1.6=16个象素。(316,16)
好了,我们成功的实现了浮点数的速度,并且成功的在显示器上显示了出来。
因此也可以把这种方法看成是在时间上的插值。(因为在我之前写的图像缩放程序中就是用的类似的方法解决了非整倍数的缩放,有兴趣的朋友可以去仔细研究一下我之前帖子里的代码吧)
主要问题解决了,其他问题就简单了,双缓冲上去,闪烁也解决了。
到目前为止,我们的程序员朋友不但解决了一个很大的问题,也在解决问题的方法上提高了自己档次。
大家鼓掌!!!!
哦,对了,天还没亮,那就自己鼓掌吧。