一、设计目的掌握定义函数,并熟练使用各函数
实现各函数模块的调用
学会将静态库导入工程,熟练使用库函数
掌握使用graphics.h的IMAG的数据类型
学会调用API
学会编写合适的算法来解决问题
二、设计内容
为了更好地掌握各数据类型、数据结构的使用以及C语言程序设计的思想,以C语言平日课堂知识为主,复刻了前几年在移动端很火的一款游戏Flappy bird。
成品Flappy bird截图如下:
游戏开始
游戏画面
游戏结束
三、概要设计
3.1 功能模块图
3.2 各个模块详细的功能描述
Custom Function list 自定义函数
voidprint();
voidbegin();//载入music、背景、First stone
voidprintstone();
voidbird();//控制鸟的下降和上升
voidjudgement();
voidscoleprint();
voidendorretry();
使用到的头文件和lib
#include
#include
#include
#include
#include
#include
#include"mmsystem.h"
#includeWINMM.lib(VC6.0自带的静态库)
四、详细设计
首先,需要安装EasyX图形库,将.exe呈现出更好的图形界面,然后,利用graphics.h绘制图像。
熟悉graphics头文件中的各种库函数,比如
initgraph();// 界面初始化
circle();// 画圆
RGB();// 颜色设置
IMAGE// 定义IMAGE对象
closegraphics();// 关闭绘图界面等等
还需要寻找一些库函数,识别键盘的敲击,比如while(!kbhit())等等。熟悉相关库函数后,开始本次flappy bird程序编写。
实现思路如下所示:
背景图片的显示
加入小鸟图片
小鸟自由下落,按键后上升
加入静态的障碍物
障碍物向左移动
判断小鸟和障碍物的碰撞
障碍物移动出左边界后,在右边重新出现
加入记分模块
加入开始界面、结束界面
加入背景音乐
分阶段编写实现上述10个模块。
首先,由于我需要设置图形界面,所以调用了VC6.0里头对窗口句柄属性操作的函数,实现对窗口句柄属性的修改,这里由于游戏简单,仅修改了窗口名字,下面贴代码:
// 获取窗口句柄
HWND hwnd=GetHWnd();
// 设置窗口标题文字
SetWindowText(hwnd,"大峰子的Flappy bird!");
而后便是游戏窗口的设置,这里为了尽最大限度地复刻移动端,给玩家带来熟悉的感觉,我初始化成了350X600,大概是6寸的手机屏幕大小。这一步的实现是:
initgraph(350,600);
之后,需明确游戏的两个对象,鸟,管道;
由于小鸟的属性本质是取其需打印位置的左上角坐标(X’, Y’),然后通过库函数
loadimage();
putimage();
将准备好的图片素材输出到该位置上,这样小鸟就呈现在了屏幕上,所以我们需要定义两个全局变量bird_x、bird_y去实现存储小鸟坐标的功能。
之前提到,本游戏主要是基于C语言graphics.h中各类绘图库函数实现的,所以要定义IMAGE对象,代码说话:
IMAGE background,bird1[4],bird2[4],scole1[10],scole2[10],stone_up1,stone_up2,stone_down1,stone_down2,stone_up3,stone_up4,stone_down3,stone_down4;
以便于后面的loadimage和putimage这一系列操作。
随后,便是各个功能模块的技术实现了,我简述一下我的技术实现:
背景图片的显示。这个比较简单,定左上角为(0,0)点,只要使用函数putimage()就能实现该模块
加入小鸟图片。首先我们观察游戏会发现,该游戏中小鸟只会上下移动,并不会左右移动,造成小鸟向前飞的错觉是管道相对于小鸟的相对位移造成的,因此,我们只需要putimage到(150,300)即屏幕中央偏左的位置即可。另外,这里有一个小技术实现,就是三光栅操作,使得位图能透明显示。其原理是XOR,即异步运算,掩码图XOR精灵图=能透明显示到背景图上的位图。
举个例子:
黑XOR黑=原颜色。通过XOR运算将两张图像先后重叠打印,就能够实现图像的显示而不造成图像闪烁现象。
加入静态的障碍物管道。这个原理与2是一致的。不再过度阐述。
开始时播放背景音乐。这里需要调用媒体播放API,双线程进行,不然就会造成“播放.wav时控制台黑屏”的现象。这里用到的是头文件windows.h下的库函数Playsound,下面贴代码:
PlaySound("000.wav",NULL,SND_FILENAME|SND_ASYNC|SND_LOOP);
通过这段代码,实现多线程、循环播放。此外实现该功能需要添加WINMM.lib到project当中。
加入封面、结束界面。封面的加入较为简单,制作一个350X600的图片,在开始时putimage,并利用getch函数实现停顿直至键盘输入。结束界面的“GAME OVER”图像,显示方法如上述描述的一样,不再过多阐述。
小鸟的自动下落,键盘输入空格or鼠标左键时上升。首先,自动下落可以通过循环解决,每一次循环都进行一次按键检测 if(kbhit()),若按下空格键or鼠标左键,bird_y减小,小鸟图像打印时将上移,造成一种小鸟向上飞跃的感觉,若没有按按键,则bird_y+=3,缓慢下降。
障碍物向左移动。这个操作是在自定义函数printstone中实现的。关键点在于游戏一开始时柱子的初始化、柱子的随机长度、及柱子移动到屏幕左边框后右侧柱子重新出现…这三种情况的技术实现。
柱子的随机长度怎么设置呢?需要用rand函数,其位于stdlib.h中,用rand()%310-550,将得到的数值赋给上方的柱子坐标stone_y,保持x不变,y-750,就得到了下柱子的位置坐标,再putimage()即可。
为了让每一次游戏时管道间的间距位置都不一致,我使用了srand随机种子,由于随机种子函数值决定了rand()得到的数字序列,所以可以利用时间的多样性实现随机种子的多样性,下面贴代码:
srand((unsigned)time(NULL));
这样每次rand();得到的数字都是不定的,达到伪随机的效果。
如何实现柱子的左移呢?在printstone中添加stone_x— 这一行,这样每次while循环就能实现柱子的左移。
如何才能在一开始游戏时只出现一个柱子呢?需把stone_x2赋一个极小值,让putimage();到350*600的屏幕之外即可。
同理,每次循环检测柱子坐标变量stone_x的值是否等于0,等于0的话,将stone_x重新赋值为350即可。
碰撞判断模块。我自定义了一个judgement();进行判断,如果鸟和管道的素材重叠,就判断游戏失败了,执行endorretry();。由于我的bird图像素材是34X24,所以区间就是stone_y+-24,同时要考虑管道的素材是140*600,首先考虑碰撞上界,上界即是与上管道重叠,因此有以下代码实现:
if((stone_x1>10&&stone_x1<20)||(stone_x1>174&&stone_x1<184))
if((bird_y>(stone_y1+576)&&bird_y(stone_y1+726))&&bird_y
同时,掉出下界也需要判断,下界分为与下管道重叠,与窗口下边缘重叠,所以有了以下的代码实现:
elseif(stone_x1>=20&&stone_x1<=174)
if(!(bird_y>(stone_y1+600)&&bird_y
if(bird_y>576)
// 判断结果为ture后,即进入endorretry();或者main while(1)主循环
计分模块。当stone_x==150时,scole++,即柱子过屏幕一半的位置时,默认小鸟通过了柱子。当碰撞后,初始化,即执行endorretry();,scole=0,计分归零。
暂停模块。比较简单,键盘检测鼠标右键、Esc键,即在bird函数中检测键盘输入是否等于27(ASCII码对应Esc键),或者是否等于’ ’(对应空格),然后再使用getch();函数停顿即可。
游戏失败后的初始化模块。当 judgement();中判断成功并进入endorretry();,将打印Game Over图像并初始化。技术实现上,需要消除图像闪烁问题,这里需要用到BeginBatchDraw();和EndBatchDraw();批量绘图函数来消除Sleep();函数造成的闪烁问题。
功能函数的调用关系图
各功能函数的数据流程图
五、测试数据及运行结果
六、调试情况,设计技巧及体会
6.1 调试改进
像素小鸟翅膀的扇动
改进办法:当bird_y上升/下降时,利用IMAGE数组控制输出不同类型的图案。
像素小鸟位移感严重,跳跃不顺畅
改进办法:按键检测到空格键or鼠标左键时,将bird_y-=80调整至bird_y-=40,减小相对位移的长度。
柱子移动偏慢
改进办法:多设置一个难度模块,通过改变柱子移动速度达到难度的改变,例如定义全局变量n,将stone_x—修改为stone_x=stone_x-n,然后通过scanf(“%d”,&n);去控制每次循环时柱子向左移动的位移即可。
缺乏最高分模块,记录玩家最高分
改进办法:后续加入该模块。
6.2 体会
通过这次C语言课程设计,让我很好地掌握了C语言数据类型的使用,学会了用结构化、模块化的思想去编写程序,增强程序的健壮性,可读性,同时增强了对于添加并引用lib的熟练度,让程序功能更好地达到目的。同时增强了个人对计算机、程序编写的自信心,有利于未来的职业发展,衷心地感谢任课老师的指导与栽培。