C语言高级demo Flappy Bird游戏
Demo目录
- 导语
- 序章
- 游戏总体设计
- 游戏流程图
- 游戏中数据与函数介绍
- 游戏实现
- 开始界面实现
- bird控制
- 游戏绘图
- 碰撞与得分实现
- 结语
导语
这是C语言高级demo。用C语言实现Flappy Bird这款经典的游戏,最终的效果要达到与原著有一定的相似度。示意图如下:(完整的代码在结语部分)
本demo的要求有:
- 游戏画面精美,尽力达到原汁原味。
- 在游戏中没有图像的闪烁,画质良好且稳定。
- 对游戏的数据以文件形式储存。
- 实现积分。
- 游戏有玩法提示,良好的交互。
- 合适的音效。
序章
首先介绍我用到了那些技术。
- C语言
- EasyX图形库。EasyX 是针对 C++ 的图形库,可以帮助 C++语言初学者快速上手图形和游戏编程。它的内部调用Windows API 兼容性良好,对新手友好,使用简单,支持鼠标,批量绘图技术,支持真彩色。在项目开发中熟练使用图像库中对图片操作的函数,理解批量绘图函数并运用。
- Window.h中的一些函数的使用:
- 键盘处理函数GetAsyncKeyState(),它是一个用来判断函数调用时指定虚拟键的状态,确定用户当前是否按下了键盘上的一个键的函数。如果按下,则返回值最高位为1;
- mciSendString(),它是用来播放多媒体文件的API指令,可以播放MPEG,AVI,WAV,MP3,等等。
总的来说,运用的技术没有太多,但是我在实际编码时发现了不少问题,比如图片的黑边如何处理,按键灵敏度等,在后面我会解释如何处理它们的。
游戏总体设计
游戏流程图
(并不标准的流程图,但基本额能够表达清楚整个游戏的运行过程)
游戏中数据与函数介绍
以下内容全部定义在一个头文件中:FB_headfile.h
- 一, bird数据
struct Bird
{
int birdhigh;
int birdpicture;
int number;
float flow;
float up;
};
- 游戏中的图片数据
typedef struct Picture
{
IMAGE birdpt[3][2];//bird图片
IMAGE bandpt[5][2];//障碍物图片
IMAGE backgroundpt;//背景图片
IMAGE landpt;//下方可以移动土地
IMAGE big_num[10][2];//大号的数字
IMAGE mid_num[10][2];//中号数字
IMAGE sm_num[10];//小号数字
IMAGE gametitle[2];//游戏的标题
IMAGE getready[2];//准备阶段标题
/*所有的按钮和得分面板
0:button_play; 1:button_score;
2:tutorial.jpg; 3;score_panel*/
IMAGE button[4][2];
IMAGE copyright[2];//开始界面的版权文字
IMAGE medals[4][2];//奖牌
IMAGE gameover[2];//游戏结束
IMAGE newscore;//最新的高分
};
- 游戏其他数据
typedef struct Data
{
int landdata;//下方移动土地
//障碍物 0是障碍物的x坐标,1是上障碍物的y坐标 2是下方障碍物y坐标
int banddata[5][3];
int nowscore;//本次得分
int speed;//移动速度
int oldscore[3];//0第一,1第二,2第三
int scoreblock;//分数锁
bool life;//bird是否存活
Bird bird;//bird数据
};
- 用到的函数
void initdata(Data &data);//初始化游戏数据
void birdct(Data &data);// bird控制
bool play();//开始游戏
void databack(Data &data);//数据处理
void gamestartmenu(Picture picture, Data data);//绘制开始界面
void getread(Picture picture,Data data);//绘制getready界面
void picturedeal(Picture &picture,int n);//游戏图片处理(加载与旋转) n=1 是加载图片
void drowpicture(Picture picture, Data data);//绘制游戏内容
void collision(Data &data);//碰撞
void scorepanl(Picture picture,Data &data);//得分面板
void changeband(Data &data, int n);//改变障碍物的Y轴数据
using namespace std;
游戏实现
开始界面实现
实现前的讲解:(之后会默认你已经明白这些知识了)
- 黑边处理:仔细的你可能已经发现了,几乎所有的图片数据都是二维,并且第二维的元素为2。我简单解释一下:因为素材图片都是网上找到,如果直接画上去会有很难看的黑边,我用的图形库又不能使用透明背景的图片,但它支持绘图时,将要绘制的图像像素与他要覆盖范围内的原像素进行位运算并显示运算后的结果图像,基于这个功能,我们可以自己实现去黑边,首先每张图片需要制作它的掩码图,比如:
有了它的掩码图,就可以去掉黑边了。具体的原理可以自行百度,我提供一份参考资料: 点这里 - 闪烁处理: 大多数情况下,我们直接绘制在屏幕上的图片会有严重的闪烁!这十分影响游戏的效果,发生闪烁的原因可以自行百度,这里不再赘述。好在图形库位我们实现了批量绘图的技术,简单提及一下它是如何实现的:在开始批量绘图时(调用:BeginBatchDraw()),他就另外创建了一个画板,你的只有所有绘制的图像都会这这个画板上,而不是直接显示在你的显示器上,当你本次绘画完成时(调用FlushBatchDraw();),它会一次性把你之前画的内容显示出来。最后记得关闭批量绘图。
tips:不同的图形库对于批量绘图有不同实现。 - 这一部分编码难度在分数排名的绘制,它涉及到文件操作、界面的跳转等。
代码示例
- 掩码图的运用与批量绘图技术:
BeginBatchDraw();//开始批量绘图
putimage(0, 0, &picture.backgroundpt);
putimage(50, 100, &picture.gametitle[0], SRCAND);
putimage(50, 100, &picture.gametitle[1], SRCPAINT);
putimage(115, 180, &picture.birdpt[number][0], SRCAND);
putimage(115, 180, &picture.birdpt[number][1], SRCPAINT);
putimage(20, 345, &picture.button[0][0], SRCAND);
putimage(20, 345, &picture.button[0][1], SRCPAINT);
putimage(150, 340, &picture.button[1][0], SRCAND);
putimage(150, 340, &picture.button[1][1], SRCPAINT);
putimage(n, 400, &picture.landpt);
putimage(70, 425, &picture.copyright[0], SRCAND);
putimage(70, 425, &picture.copyright[1], SRCPAINT);
FlushBatchDraw();//将已经绘制的图像一次性显示出来
Sleep(50);
putimage(0, 0, &picture.backgroundpt);
putimage(50, 100, &picture.gametitle[0], SRCAND);
putimage(50, 100, &picture.gametitle[1], SRCPAINT);
putimage(115, 180, &picture.birdpt[number][0], SRCAND);
putimage(115, 180, &picture.birdpt[number][1], SRCPAINT);
putimage(20, 340, &picture.button[0][0], SRCAND);
putimage(20, 340, &picture.button[0][1], SRCPAINT);
putimage(150, 340, &picture.button[1][0], SRCAND);
putimage(150, 340, &picture.button[1][1], SRCPAINT);
putimage(n, 400, &picture.landpt);
putimage(70, 425, &picture.copyright[0], SRCAND);
putimage(70, 425, &picture.copyright[1], SRCPAINT);
FlushBatchDraw();//将已经绘制的图像一次性显示出来
EndBatchDraw();//结束批量绘图
bird控制
实现前的讲解:
- 这个部分实现的难点在于捕获键盘信息。我使用了window的api(GetAsyncKeyState)来获取按键信息。
- 设置bird的下降速度。
流程图:
实现函数:birdct()
游戏绘图
流程图:
实现函数:drowpicture()
碰撞与得分实现
这是游戏中最复杂的部分。
- 碰撞的设计思路:
实现函数:collision()
结语
感谢你的参阅,游戏还有很多不完善的地方,有兴趣的话你可以添加自己的玩法。最后附上项目的地址: 点这里 。
Fraps_: https://github.com/jmiller656/EDSR-Tensorflow/blob/master/utils.py#L30 参考这里的实现。
weixin_47102310: X = tf.reshape(I, (bsize, a, b, c/(r*r), r, r))总是在这一行提示错误。TypeError: Expected int32, got 8.0 of type 'float' instead.
weixin_47102310: 博主您好,我想把sub-pixel插入到卷积网络中,代替上采样,但是总是报错,说格式不对,应该如何处理
Fraps_ 回复 qq_44796499: 不好意思这才回你,对于第一个问题,我现在认为是作者自己强塞的,到目前,神经网络也是凭经验、实验性的,没有太多理论上的东西,提出的模型有效果后,再反过来解释;对于第二个问题,要深究的话要去看第一个提出“反卷积”这个概念的文章,对于本文提到的sub-pixel,再github上有实现,而且再以后很多超分模型都在用这功能,附地址:https://github.com/tetrachrome/subpixel
qq_44796499: 博主,您好,我最近在学习有关图像超分辨的知识,感谢您写了这篇博客,我看完您这篇博客后有两个问题:第一个是该神经网络为什么可以依赖神经网络的学习能力,在网络的第一层,让它去自适应的习得一个灵活的滤波器。在别的博客中也写到什么插值函数被隐含地包含在前面的卷积层中,我也不太理解这种解释。第二个是最后一层网络,也就是sub-pixel卷积层为什么是属于反卷积。望能得到博主的解答,感激不尽!