[MFC] 高仿Flappy bird 桌面版
这是今年年初做的东西,一直没有时间整理,现在拿出来分享下~
目录
- 开发背景
- 开发语言及运行环境
- 效果展示
- 游戏框架说明
- 游戏状态及逻辑说明
- 经典算法说明
- 重量级问题解决
- 开发感想
一、开发背景:
flappy bird由一位来自越南河内的独立游戏开发者阮哈东开发,是一款形式简易但难度极高的休闲游戏。简单但不粗糙的8比特像素画面、超级马里奥游戏中的水管、眼神有点呆滞的小鸟和几朵白云便构成了游戏的一切。你需要不断控制点击屏幕的频率来调节小鸟的飞行高度和降落速度,让小鸟顺利地通过画面右端的通道,如果你不小心擦碰到了通道的话,游戏便宣告结束。
这款虐心的小游戏一经推出,便引起火爆的下载。然后先后出现了各种平台的移植开发:IOS平台PC和手机版、采用HTML5+Canvas及Javascript技术来实现的Flappy Bird电脑版、以网页html5+JS技术完全克隆了原版native app的Web App版、实现了在微信朋友圈和QQ空间中的无缝运行的微信/QQ空间版、WindowsPhone版….但是唯一没有的是直接可在windows操作系统下的单机版,于是当时突发奇想,不如我来填补这个漏洞吧!
二、开发语言及运行环境:
此PC版采用C++的MFC技术在VS2012开发平台下写成,支持windows 7\8环境,XP不知道为啥不行~
三、效果展示:
四、游戏框架说明:
整个游戏除了由MFC游戏基本框架CMyApp和CMainWindow外,这里特别封装了以下几个类:
- 1、 Bird类[专门处理鸟的飞行逻辑、碰撞检测、音乐播放、贴图]
- 2、 PipeList类[内嵌Pipe类,并用CList创建一个Pipe链表,用来处理游戏中管道的移动逻辑、碰撞检测等]
- 3、 Panel类[主要是计分板的动画效果逻辑和计分板的计分逻辑,数字贴图,金币种类运算等]
- 4、 Land类[主要处理陆地运动逻辑及贴图]
- 5、 Button类[主要处理按钮的动画效果、贴图及响应]
- 6、 Pic类[是图片资源类,主要负责存储、加载、全局调用游戏的图片资源]
五、游戏状态及逻辑说明:
这款游戏本身操作简单、逻辑分明,大致可分为以下几种状态:
1、 初始态:基本上为静态贴图,只有鸟和陆地为简单运动。由上往下依次为:
[数字:0]
[标志:Get Ready!]
[图标:操作方法]
[鸟:上下飞行]
[陆地:向左移动]
[背景:随机昼夜]
2、 游戏进行态:当点击一下屏幕,鸟、柱子被解封,陆地依然保持原来运动状态,背景不变,这里采用相对运动效果,其实背景是没有运动的,而鸟也只是上下运动,根本就没有向前飞一点!
[鸟:向上跃起,然后以竖直上抛的逻辑使鸟运动;同时,还要专门为鸟的姿态设计合理的旋转函数]
[柱子:向柱子链表里加入新的柱子,并使链表里的所有柱子开始向左移动,当柱子完全超出最左边界时,将该柱子删除;同样的,当最后一个柱子到达某一特定距离时,向链表里加入一个新的柱子,这样既保证了刚开始的柱子出现效果的真实、有趣性,又保证了资源的合理回收,提高算法高效性]
[分数:当柱子到达鸟所在的位置时就要进行碰撞检测,如果没有碰撞且鸟跨过柱子,就让分数+1,并响铃]
[陆地:保持匀速运动逻辑,采用循环贴图技术,产生无缝效果]
3、 死亡状态:鸟的死亡状态看似简单,但是仔细分析并非如此。各种细节都要分别考虑:
[直接撞地态:中止所有运动逻辑,同时留一定的时间间隔,产生画面转换的质感]
[高撞柱子态:旋转为垂直态,然后自由落体;撞击时发出声音,然后发出坠落的声音,同时进行碰撞检测,碰到陆地中止一切运动,进行时间停留]
[低撞柱子态:和高撞柱子态的区别是,坠落的时间少了,音乐没有完页面就跳转了,所以要控制时间停留长度,产生高仿的效果]
4、 死亡之后态:鸟撞地之后要有一定的时间逗留防止页面跳转过快不舒服的感觉。接下来首先贴上game_over的图标,然后计分板从下往上飞来,接着开始计分并张贴是否为新纪录和是否获得金牌之类的,最后贴上两个按钮等待响应。
[陆地:停止运动]
[图标:展示Game_Over]
[计分板:动画效果,从下往上飞来并带有音效,当飞到指定位置时开始从0累计得分,并统计是否为新纪录和是否获得相应的奖牌]
[按钮:静态贴图,但是相应的时候有上下振动的效果]
六、经典算法说明:
1、 ON_WM_TIMER:时间消息映射:
主要控制全局逻辑运算的时间进程,根据当前的状态做相应的逻辑运算;同时逻辑运算也会对全局的游戏状态进行改变,实现全局操控逻辑实现:(与此相同的draw函数这里就不再详细介绍)
void CMainWindow::OnTimer(UINT nTimerID){
switch(nTimerID){
case bird_time:
if(game_state==before_game)bird.logic(before_game,game_state);//开始前
break;
case land_time:
if(game_state==before_game){//开始前
land.logic();//路
}else if(game_state==during_game){//游戏中
if(bird.state!=bird_delay)land.logic();//路
bird.logic(,game_state);//鸟正常运动
if(bird.state!=bird_delay)pipe.logic(goals,bird,game_state);//管道
}else if(game_state==dying_game){//失败中
bird.logic(,game_state);//垂直下落
}else if(game_state==end_game){//显示game-over+计分板+2个按钮
if(panel.state==finish)button.logic(game_state);
if(last_state>=)panel.logic(goals,best_goals);
}else if(game_state==start_game){//重新开始
restart();
game_state=before_game;
}
break;
default:break;
}
draw();
}
2、 ON_WM_LEFTBUTTONDOWN:鼠标左键按下监听映射:
每次单击鼠标左键相应该函数,然后该函数根据不同的游戏状态做出不同的逻辑操作:①、[当游戏处于0态,即:游戏开始之前时,点击鼠标,状态改为1态,柱子加入开始移动,鸟跃起开始飞翔][当处于游戏态时:每次点击鸟都会跃起];②、[当处于结束态时:按钮等待鼠标按动,并根据区域做出判断是否按了按钮,按了哪一个]
void CMainWindow::OnLButtonDown(UINT nFlags, CPoint point){
if(game_state==){
game_state=;
pipe.add();
bird.jump();
}else if(game_state==){
bird.jump();
}else if(game_state==){
button.click(point);
}
}
3、PipeList::logic柱子逻辑函数,包括碰撞检测!
为了简化起见,我把音频播放的部分删去了:这里是遍历整个链表,对于每一个柱子,由上到下每一个if为:①、[判断鸟是否正好穿越一个柱子,如果是则分数加1];②、[判断柱子是否出界,超出就不把该柱子放回链表,相当于删除];③、[鸟与地面的碰撞检测];④、[鸟与柱子的碰撞检测]⑤、[最后一个if是判断最后一个柱子是否到达指定位置,如果到达就向链表尾部加入一个新的柱子,从而保证了柱子连续且间距统一]
//---------------------------------------------------------------
void PipeList::logic(int &goals,Bird &bird,int &game_state){//逻辑函数
int count=pipe.GetCount();
for(int i=;i<count;i++){
Pipe temp=pipe.GetHead();
pipe.RemoveHead();
temp.logic();
if(temp.pos_x==){
goals+=;
}
if(temp.pos_x>=-)pipe.AddTail(temp);
//碰撞检测
if(+bird.y+-$d>){//与地面
bird.y=--+$d;
bird.stop();
game_state=;
}else if(!(+-$d < temp.pos_x || temp.pos_x+<+$d)){//与柱子
if(!(+bird.y+$d > temp.pos_y+ && temp.pos_y+ > +bird.y+-$d)){
game_state=;//表示碰撞,游戏结束;
}
}
}
if((pipe.GetTail()).pos_x<=){
Pipe temp;
pipe.AddTail(temp);
}
}//---------------------------------------------------------------
4、 Bird::logic鸟的运动逻辑,包括所有运动状态(绝密算法!!!)
同样的为了简单我也把音频部分的代码删去了。此函数是分别将鸟的运动的各个状态做分别处理:①、[开始前:采用正弦函数波动飞行同时改变翅膀状态];②、[正常飞行时:又把鸟的运动状态划分为向上、向下、旋转、停留四个状态分别处理];③、[下落死亡状态:这里用了一个辅助时间变量,控制帧动画播放]
//---------------------------------------------------------------
void Bird::logic(int ID,int &game_state){
if(ID==){//开始前
y=*sin(Time*PI);
Time+=0.25;
fly_state=(fly_state+)%;
}else if(ID==){//正常
switch(state){
case state_up:
v+=a;
y+=v;
dis_state--;
if(dis_state==){
state=state_turn;
Time=;
}
break;
case state_turn:
v+=a;
y+=v;
if(+y+-$d>=){
y=--+$d;
stop();
game_state=;
}
dis_state++;
if(dis_state== && Time<=0.4){
Time+=0.1;
dis_state=;
}
if(dis_state==){
state=state_down;
}
break;
case state_down:
v+=a;
y+=v;
if(delay== && +y+-$d>=){
y=--+$d;
stop();
state=state_delay;
}
break;
case state_delay:
delay++;
if(delay==){game_state=;}
break;
default:break;
}
if(dis_state!=)fly_state=(fly_state+)%;
}else if(ID==){//下落
delay++;
if(delay==){//撞击声延时
}
if(delay<){//下落运算
y+=v;
v+=a;
if(dis_state!=)dis_state++;
if(+y+-$d>=){//撞地检测
y=--+$d;
stop();
if(dis_state==){delay=;}
}
}else if(delay==){//坠地后延时
game_state=;
}
}
}//---------------------------------------------------------------
5、Button::按钮识别和按钮动画逻辑实现:
通过鼠标所在的点判断是否在按钮所在的矩形区域内来判断是否点了该按钮:
//---------------------------------------------------------------
void Button::click(CPoint &point){
if(point.x>= && point.x<=+ && point.y>= && point.y<=+){
kind=play;
move=true;
}else if(point.x>= && point.x<=+ && point.y>= && point.y<=+){
kind=score;
move=true;
LoadFromResource(IDR_HTML1);
}else kind=none;
}//---------------------------------------------------------------
//这里主要解决颤动效果实现及按钮状态复原:
//---------------------------------------------------------------
void Button::logic(int &game_state){
if(kind==play){//颤动控制
if(move==true){
play_y=;
move=false;
}else{
play_y=;
kind=none;
game_state=;
}
}else if(kind==score){
if(move==true){
score_y=;
move=false;
}else{
score_y=;
kind=none;
}
}
}//---------------------------------------------------------------
七、重量级问题解决:
1、 飞翔弧度、旋转状态难题:
正如前面的鸟的飞翔逻辑代码所示:鸟的飞翔过程并不是简单的自由上抛就能解决的;我通过大量实验发现必须把这个过程分为上面介绍的4步,然后每一步用更加详细的数学公式计算鸟的运行逻辑[因为此处我们必须考虑鸟的旋转效果和鸟的速度同步,所以这才是难点所在]。因为上面已经详细说明了,这里就不再重复,但这是一大难点!
2、 混音效果、完美封装处理:
本来音乐播放只要用PlaySound函数一句话就能产生音乐播放效果,但是当全部节点都放好音乐时,发现当鸟正好越过柱子发出加分的铃声时和鸟飞翔的声音无法混合播放,而是出现了严重的打断效果!导致听起来很不舒服。难道只有重新用Direct-X来处理混音吗?想想就冒汗….毕竟游戏已经接近尾声了,没必要再推翻MFC框架而用Direct-X来吧!于是发现用用2个不同的函数可以解决这个问题,即:其他部分不变还是用PlaySound函数,而分数增加的音乐用mciSendString函数来播放可以解决问题。
但是mciSendString只能加在特定路径下的音频,无法处理资源文件下的wav文件,这该怎么办呢?难道要放弃资源的全封装效果?那多不好,于是还是被我解决了!我采用的思路是:把资源文件读到一个中间虚拟文件,然后把该中间文件加载金mciSendString就可以啦!下面是如何读取资源文件并转为中间文件的函数:
注:PlaySound(MAKEINTRESOURCE(ID),AfxGetResourceHandle(),SND_RESOURCE|SND_ASYNC);
//---------------------------------------------------------------
bool ExtractResource(LPCTSTR strDstFile, LPCTSTR strResType, LPCTSTR strResName)
{
// 创建文件
HANDLE hFile = ::CreateFile(strDstFile, GENERIC_WRITE, NULL, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, NULL);
if (hFile == INVALID_HANDLE_VALUE)
return false; // 查找资源文件中、加载资源到内存、得到资源大小
HRSRC hRes = ::FindResource(NULL, strResName, strResType);
HGLOBAL hMem = ::LoadResource(NULL, hRes);
DWORD dwSize = ::SizeofResource(NULL, hRes); // 写入文件
DWORD dwWrite = ; // 返回写入字节
::WriteFile(hFile, hMem, dwSize, &dwWrite, NULL);
::CloseHandle(hFile); return true;
}//--------------------------------------------------------------
3、 创建分享、窗口截屏技术:
其实已经解决上面几个问题已经仿的差不多啦,但是还不完美!于是开始着手解决那个分享按钮[要知道这对MFC来说难度不亚于不用引擎来做图像处理!]可是这并不代表问题不可解。先不说,先看看效果!
知道难度了吧!这是分享按钮自动创建的网页,然后还有图片信息,下载链接[这样才会吸引更多的人玩]由于这里涉及到非基础MFC知识,这里只提示一下:用到的技术是HTML+JS技术[也就是网页编程+脚本设计]
八、开发感想:
实践出真知,通过开发这款简单的像素游戏,遇到了很多问题,也学到了很多,如今将近一年后拿出来还觉得当时做的这个是一个小奇迹~虽然这一年里也做了不少好玩的软件、神奇的硬件、以及一些软硬结合的小东西,但是都没有这个让人感觉充实。仔细想想,我觉得之所以它能让开发它的人如此留念,很大一部分原因是因为对它的反复斟酌修改与追求完美的过程中所积淀的解决问题、享受成果的乐趣吧!如今大三上也快GAME OVER了。这一年可能太过于浮躁,一方面想施展下身手、另一方面又技艺不精,会的挺多但都浅尝辄止,好的想法要很长时间才能实现,遇到优化又不能静下心来,整天忙忙碌碌基本1~2点休息,可是很少出这种精致的作品~时间很快,暑假实习过后留在大学里的日子就不多啦,且行且珍惜~
相关链接
博主主页(转载请注明出处哦):http://www.cnblogs.com/zjutlitao/
上述工程资料:http://pan.baidu.com/s/18i2dS
[MFC] 高仿Flappy bird 桌面版的更多相关文章
- [MFC] MFC 仿 Flappy bird PC桌面版
http://www.cr173.com/ 前些日子发现朋友都在玩flappy bird这款虐心的小游戏,网上也炒得很火,于是俺也想下一个玩玩.可是矮穷挫至今还没配上高端的智能机,于是去网上搜了一下, ...
- 通通制作Html5小游戏——第二弹(仿flappy bird像素鸟)
亲爱的博友们,我又回来啦~因为我们技术宅的思想只有技术宅懂得,好不容易写了点好玩的东西发QQ空间,结果只有11的UV,0回复....10分钟ps一个女神的素描效果发QQ空间朋友圈,一大堆回复加赞,作为 ...
- 飞翔的圆(Flappy Bird)游戏源码
这个源码是一个不错的休闲类的游戏源码,飞翔的圆(Flappy Bird)游戏源码V1.0,本项目是一个仿Flappy Bird的小游戏,只不过是把Flappy Bird里面的鸟替换成了简单的圆.感兴趣 ...
- canvas 制作flappy bird(像素小鸟)全流程
flappy bird制作全流程: 一.前言 像素小鸟这个简单的游戏于2014年在网络上爆红,游戏上线一段时间内appleStore上的下载量一度达到5000万次,风靡一时, 近年来移动web的普及为 ...
- 自己动手写游戏:Flappy Bird
START:最近闲来无事,看了看一下<C#开发Flappy Bird游戏>的教程,自己也试着做了一下,实现了一个超级简单版(十分简陋)的Flappy Bird,使用的语言是C#,技术采用了 ...
- 65行 JavaScript 代码实现 Flappy Bird 游戏
飞扬的小鸟(Flappy Bird)无疑是2014年全世界最受关注的一款游戏.这款游戏是一位来自越南河内的独立游戏开发者阮哈东开发,形式简易但难度极高的休闲游戏,很容易让人上瘾. 这里给大家分享一篇这 ...
- 用Phaser来制作一个html5游戏——flappy bird (一)
Phaser是一个简单易用且功能强大的html5游戏框架,利用它可以很轻松的开发出一个html5游戏.在这篇文章中我就教大家如何用Phaser来制作一个前段时间很火爆的游戏:Flappy Bird,希 ...
- cocos2dx-html5 实现网页版flappy bird游戏
我也是第一次使用cocos2d_html5,对js和html5也不熟,看引擎自带的例子和引擎源码,边学边做,如果使用过cocos2d-x的话,完成这个游戏还是十分简单的.游戏体验地址: http:// ...
- Flappy bird源代码(略吊)
#include<stdio.h> #include<stdlib.h> #include<conio.h> #include<time.h> #inc ...
随机推荐
- EmptyRecycle() 清空回收站
//在uses下面引用 function SHEmptyRecycleBinA(Wnd:HWND;str:PChar;WRD:DWORD):Integer;stdcall; external 'SHe ...
- format when printing
http://msdn.microsoft.com/en-us/library/vstudio/56e442dc.aspx %[flags] [width] [.precision] [{h | l ...
- 动态生成dropdownlist
<td colspan=" id="td_ddl" runat="server"> </td> 后台代码: #region 动 ...
- VC++ CTreeCtrl 使用NM_CLICK和TVN_SELCHANGED
//这是当CTREECTRL控件点击时NM_CLICK的处理函数 void CDriverSelCtrl::OnNMClick(NMHDR *pNMHDR, LRESULT *pResult) { C ...
- Jquery Mobile 动态添加元素然后刷新 转
Jquery Mobile 动态添加元素然后刷新 (2013-05-09 12:39:05) 转载▼ 标签: it 分类: Mobile jquery 表单元素每一个元素都有自己刷新的方法,每当改变它 ...
- BigDecimal最基础用法
BigDecimal最基础用法 用字符串生成的BigDecimal是不会丢精度的. 简单除法. public class DemoBigDecimal { public static void mai ...
- MATLAB - 运算符
1.关系运算符用来比较两个数之间的大小关系,在Matlab中的关系运算符包括: < 小于 <= 小于或等于 > 大于 >= 大于或等于 == ...
- Selenium2+python自动化14-iframe
前言 有很多小伙伴在拿163作为登录案例的时候,发现不管怎么定位都无法定位到,到底是什么鬼呢,本篇详细介绍iframe相关的切换 以http://mail.163.com/登录页面10为案例,详细介绍 ...
- 新一代IDE Light Table开源:让编程工作更简单
近日,Light Table项目创始人Chris Granger在其博客上宣布Light Table开源,将代码全部托管在GitHub上,遵循GNU开源许可.与此同时,还发布了0.6版本,该版本添加了 ...
- 加载form表单
var row = $('#dg').datagrid('getData').rows[rowIndex]; $('#moneyff').form('load', row);//row 可以 ...