今天我们用Arm-2D制作一款时下流行的小游戏flappy bird,由于版权问题,没有用原版素材,它的玩法也很简单,只需要一个按键控制拍翅膀就行。
软硬件配置如下:
【游戏简介】
这款floppy bird小游戏使用了Arm-2D图形引擎,使得制作很简单。为了祝愿玩Arm-2D的人越来越多——如星星之火燎原之势,势不可挡——我们游戏的主人公“小火星”就出来了,如下图所示:
外围是Arm-2D小火星,中间是一个虫字,
是的,这个小游戏刚开始是把下面的一个个蜡烛点燃,走着走着就会变成一只蝴蝶,蝴蝶飞过,下面的玫瑰花会开放。背景是一个大满月,也是花好月圆,美好圆满的寓意。
有了美好的寓意,那这款游戏的玩法也是很简单,小火星会自动前进,并在前进过程中自动下落,当按下按键后,会控制小火星向上移动,实现从障碍物中间的缝隙中穿过。
【界面设计】
首先设计障碍物,如下图:
从概念设计图可知,已知屏幕的宽度和高度都是240像素,障碍物的宽度为30,间距为140,因此只要确定了上边障碍物的(x,y)坐标和上下障碍物的间隙h就可以在屏幕中绘制障碍物了。
障碍物的位置确定了,可是随着y和h的不同,障碍物也会有长有短,这个怎么弄呢,不会都是图片吧?显然不是的,对障碍物的拆解如下图所示:
看到了吧,障碍物是由两张小图片和绘制一个矩形得到的。
这个是下边的障碍物,那上边的障碍物怎么绘制呢?是要用rotation功能把图片旋转180度吗?
这样做倒是也可以, 不过Arm-2D还提供了更简单的方法。
什么, 还有简单的方法?
是的,就是用“Y轴镜像拷贝”(Y-Mirroring)图片,效果和旋转180度是一样的,如下:
最后一个参数ARM_2D_CP_MODE_Y_MIRROR就是Y镜像拷贝。同样的,Arm-2D还提供了X镜像拷贝和XY镜像拷贝。接着就是绘制小火星了,这个很简单,就是一个图片拷贝,如下所示:
那那只蝴蝶煽动翅膀是怎么实现的呢?其实这个也简单,就是两张图片交替显示就可以了,如下所示:
背景的制作我们要着重讲一下,因为他使用了一个Arm-2D的高级功能:横向单线渐变蒙版(horizontal-line-mask) 来实现一个渐变效果。如下图所示:
是不是很酷,其实用Arm-2D实现也很简单,只需要给 target 这一端提供一个 mask就行了。需要注意的是,target的这个mask 它的 height 和 width 必须能完整覆盖 target(或者height 为 1时,width 能覆盖整个 target的宽度)。下面我以height 为 1时的target mask程序举例,如下:
定义一个mask Tile,iWidth为200,iHeight 为1
初始化mask为透明度渐变
绘制透明度渐变的圆,这个就是上面我们看到的透明度渐变的满月了。
有了这个target mask,我们很容易就做出百叶窗效果,啥都不用改,只需要改mask(即s_bmpFadeMask[]的值),百叶窗效果的程序如下:
实现效果如下图:
简单吧,其实有了这个利器,我们还可以做出各种千奇百怪的擦除效果,比如渐渐出现,渐渐消失的效果,只要动态修改mask,图还是那个图,但是mask一直在变,就可以做出一些非常绚丽的效果。
【游戏核心数据结构】
下面我们讲一下障碍物的数据结构,如下:
由于我们的屏幕只能显示两个障碍物,所以只需要 定义一个长度为2 的数组,如下
接下来就是游戏中物体的移动,需要一个坐标,结构也很简单,如下并定义了一个静态全局变量
【程序实现】
有了上面的数据类型,我们就来看看他是怎么前进的。其实前进很简单,因为我们的小火星是横向向右移动,所以只需要把横向坐标累加就可以了,如下:
tplay_X_Y.iX增加之后,小火星是怎么移动的呢?其实小火星向右移动就相当于障碍物向左移动(即减去tplay_X_Y.iX的值),如下以绘制红色矩形为例:
提示:我们也可以根据tplay_X_Y.iX的值在小火星走过一定的距离后,在背景出现一个动画效果,也可以在走过一段距离后速度提升增加难度。
那障碍物移出屏幕之外怎么办呢?移出屏幕就给他重新赋值新的play_obstacle.iX,准备下一次出现,如下图所示:
当判断障碍物的iX < -30时,就可以修改play_obstacle.iX的值,然后重新出现,这样就可以一直循环下去,如下:
play_obstacle.iGap使用了随机数,其他变量也可以是随机数,但要注意变量的范围。那小火星碰到障碍物怎么检测呢?其实也简单,就是判断两个矩形是否有重合,原理如下图:
程序如下:
那现在就剩下小火星的上下移动了,相信大家也已经想到了,其实就是 tplay_X_Y.iY增加或减小。有按键按下就向上移动,没有就自动向下移动。程序如下:
最后就是我们的play_game()函数,如下:
还是很简单的,是吧。按键驱动函数我就不贴了。(* ̄︶ ̄)其中还有一些不足之处希望大家能够完善。期待大家实现自己的小游戏并添加更多好玩的功能。
【帧率优化】
上面我们讲了控制速度用了一个变量MOVING_SPEED_iX,设置不同的值移动速度就会不同,这样也可以实现速度的控制了。不过,Arm-2D还有一种特有的提速方法,那就是dirty List脏矩阵(即局部刷新),这也是我们可以使用小PFB的奥秘所在。
dirty List我们前面提到过,不知道大家还有印象没,他的使用就是几个宏,如下:
<1> IMPL_ARM_2D_REGION_LIST
IMPL_ARM_2D_REGION_LIST就是定义一个arm_2d_region_list_item_t的数组:
宏展开为
<2> ADD_REGION_TO_LIST
ADD_REGION_TO_LIST就是初始化数组元素:
宏展开为
<3>ADD_LAST_REGION_TO_LIST
ADD_LAST_REGION_TO_LIST就是初始化最后一个元素
宏展开为
<4> END_IMPL_ARM_2D_REGION_LIST
END_IMPL_ARM_2D_REGION_LIST()就是数组初始化结束
<5>Dirty List脏矩阵
这几个宏就是定义一个arm_2d_region_list_item_t类型的数组,其中arm_2d_region_list_item_t 原型如下:
看到这里我们就清楚了,dirty List就是用数组封装了一个链表。
好,知道了这个,下面才是我们要讲的重点:动态修改dirty List的刷新区域。dirty List虽然是一个链表,但他也是一个数组,我们就用数组的形式对他进行修改,如下:
这段代码的刷新区域如下图白色框所示:
由于我们移动了MOVING_SPEED_iX的距离,所以刷新宽度要加上。小火星的刷新区域也是一样的,我就不写了。对了,dirty List也可以用链表的形式进行动态添加和删除操作哦!
前3个是用来确定障碍物的位置坐标,char_arm_2d是用来保存要显示的字符,如下图所示:
char_arm_2d = 'A',就显示字符A,会循环显示Arm-2D。
game_level是用来区分第几关,(即第一关为小火星,第二关为蝴蝶)。
MOVING_SPEED_iX为移动速度,可以自己设定。
play_flag为游戏开始标志,初始化为False,当按下按键置为True,游戏开始。
stop_flag为小火星撞到障碍物置为True,小火星停止前进。