关闭 x
IT技术网
    技 采 号
    ITJS.cn - 技术改变世界
    • 实用工具
    • 菜鸟教程
    IT采购网 中国存储网 科技号 CIO智库

    IT技术网

    IT采购网
    • 首页
    • 行业资讯
    • 系统运维
      • 操作系统
        • Windows
        • Linux
        • Mac OS
      • 数据库
        • MySQL
        • Oracle
        • SQL Server
      • 网站建设
    • 人工智能
    • 半导体芯片
    • 笔记本电脑
    • 智能手机
    • 智能汽车
    • 编程语言
    IT技术网 - ITJS.CN
    首页 » C/C++ »MFC实现桌面版Flappy Bird

    MFC实现桌面版Flappy Bird

    2014-12-09 00:00:00 出处:developerWorks 中国
    分享

    目录

    开发背景 开发语言及运行环境 效果展示 游戏框架说明 游戏状态及逻辑说明 经典算法说明 重量级问题解决 开发感想

    一、开发背景:

    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 78环境,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函数这里就不再详细介绍)

     1 void CMainWindow::OnTimer(UINT nTimerID){
     2     switch(nTimerID){
     3     case bird_time:
     4         if(game_state==before_game)bird.logic(before_game,game_state);//开始前
     5         break;
     6     case land_time:
     7         if(game_state==before_game){//开始前
     8             land.logic();//路
     9         }else if(game_state==during_game){//游戏中
    10             if(bird.state!=bird_delay)land.logic();//路
    11             bird.logic(1,game_state);//鸟正常运动
    12             if(bird.state!=bird_delay)pipe.logic(goals,bird,game_state);//管道
    13         }else if(game_state==dying_game){//失败中
    14             bird.logic(2,game_state);//垂直下落
    15         }else if(game_state==end_game){//显示game-over+计分板+2个按钮
    16             if(panel.state==finish)button.logic(game_state);
    17             if(last_state>=10)panel.logic(goals,best_goals);
    18         }else if(game_state==start_game){//重新开始
    19             restart();
    20             game_state=before_game;
    21         }
    22         break;
    23     default:break;
    24     }
    25     draw();
    26 }

    2、 ON_WM_LEFTBUTTONDOWN:鼠标左键按下监听映射:

    每次单击鼠标左键相应该函数,然后该函数根据不同的游戏状态做出不同的逻辑操作:①、[当游戏处于0态,即:游戏开始之前时,点击鼠标,状态改为1态,柱子加入开始移动,鸟跃起开始飞翔][当处于游戏态时:每次点击鸟都会跃起];②、[当处于结束态时:按钮等待鼠标按动,并根据区域做出判断是否按了按钮,按了哪一个]

     1 void CMainWindow::OnLButtonDown(UINT nFlags, CPoint point){
     2     if(game_state==0){
     3         game_state=1;
     4         pipe.add();
     5         bird.jump();
     6     }else if(game_state==1){
     7         bird.jump();
     8     }else if(game_state==3){
     9         button.click(point);
    10     }
    11 }

    3、PipeList::logic柱子逻辑函数,包括碰撞检测!

    为了简化起见,我把音频播放的部分删去了:这里是遍历整个链表,对于每一个柱子,由上到下每一个if为:①、[判断鸟是否正好穿越一个柱子,如果是则分数加1];②、[判断柱子是否出界,超出就不把该柱子放回链表,相当于删除];③、[鸟与地面的碰撞检测];④、[鸟与柱子的碰撞检测]⑤、[最后一个if是判断最后一个柱子是否到达指定位置,如果到达就向链表尾部加入一个新的柱子,从而保证了柱子连续且间距统一]

     1 //---------------------------------------------------------------
     2 void PipeList::logic(int &goals,Bird &bird,int &game_state){//逻辑函数
     3     int count=pipe.GetCount();
     4     for(int i=0;i<count;i++){
     5         Pipe temp=pipe.GetHead();
     6         pipe.RemoveHead();
     7         temp.logic();
     8         if(temp.pos_x==64){
     9             goals+=1;
    10         }
    11         if(temp.pos_x>=-70)pipe.AddTail(temp);
    12         //碰撞检测
    13         if(23+bird.y+48-$d>400){//与地面
    14             bird.y=400-230-48+$d;
    15             bird.stop();
    16             game_state=2;
    17         }else if(!(65+48-$d < temp.pos_x || temp.pos_x+52<65+$d)){//与柱子
    18             if(!(230+bird.y+$d > temp.pos_y+320 && temp.pos_y+420 > 230+bird.y+48-$d)){
    19                 game_state=2;//表示碰撞,游戏结束;
    20             }
    21         }
    22     }
    23     if((pipe.GetTail()).pos_x<=140){
    24         Pipe temp;
    25         pipe.AddTail(temp);
    26     }
    27 }//---------------------------------------------------------------

    4、 Bird::logic鸟的运动逻辑,包括所有运动状态(绝密算法!!!)

    同样的为了简单我也把音频部分的代码删去了。此函数是分别将鸟的运动的各个状态做分别处理:①、[开始前:采用正弦函数波动飞行同时改变翅膀状态];②、[正常飞行时:又把鸟的运动状态划分为向上、向下、旋转、停留四个状态分别处理];③、[下落死亡状态:这里用了一个辅助时间变量,控制帧动画播放]

     1 //---------------------------------------------------------------
     2 void Bird::logic(int ID,int &game_state){
     3     if(ID==0){//开始前
     4         y=4*sin(Time*PI);
     5         Time+=0.25;
     6         fly_state=(fly_state+1)%3;
     7     }else if(ID==1){//正常
     8         switch(state){
     9         case state_up:
    10             v+=a;
    11             y+=v;
    12             dis_state--;
    13             if(dis_state==0){
    14                 state=state_turn;
    15                 Time=0;
    16             }
    17             break;
    18         case state_turn:
    19             v+=a;
    20             y+=v;
    21             if(230+y+48-$d>=400){
    22                 y=400-230-48+$d;
    23                 stop();
    24                 game_state=3;
    25             }
    26             dis_state++;
    27             if(dis_state==1 && Time<=0.4){
    28                 Time+=0.1;
    29                 dis_state=0;
    30             }
    31             if(dis_state==6){
    32                 state=state_down;
    33             }
    34             break;
    35         case state_down:
    36             v+=a;
    37             y+=v;
    38             if(delay==0 && 230+y+48-$d>=400){
    39                 y=400-230-48+$d;
    40                 stop();
    41                 state=state_delay;
    42             }
    43             break;
    44         case state_delay:
    45             delay++;
    46             if(delay==8){game_state=3;}
    47             break;
    48         default:break;
    49         }
    50         if(dis_state!=6)fly_state=(fly_state+1)%3;
    51     }else if(ID==2){//下落
    52         delay++;
    53         if(delay==8){//撞击声延时
    54         }
    55         if(delay<60){//下落运算
    56             y+=v;
    57             v+=a;
    58             if(dis_state!=6)dis_state++;
    59             if(230+y+48-$d>=400){//撞地检测
    60                 y=400-230-48+$d;
    61                 stop();
    62                 if(dis_state==6){delay=60;}
    63             }
    64         }else if(delay==66){//坠地后延时
    65             game_state=3;
    66         }
    67     }
    68 }//---------------------------------------------------------------

    5、Button::按钮识别和按钮动画逻辑实现:

    通过鼠标所在的点判断是否在按钮所在的矩形区域内来判断是否点了该按钮:

     1 //---------------------------------------------------------------
     2 void Button::click(CPoint &point){
     3     if(point.x>=25 && point.x<=25+116 && point.y>=340 && point.y<=340+70){
     4         kind=play;
     5         move=true;
     6     }else if(point.x>=155 && point.x<=155+116 && point.y>=340 && point.y<=340+70){
     7         kind=score;
     8         move=true;
     9         LoadFromResource(IDR_HTML1);
    10     }else kind=none;
    11 }//---------------------------------------------------------------
    12 //这里主要解决颤动效果实现及按钮状态复原:
    13 //---------------------------------------------------------------
    14 void Button::logic(int &game_state){
    15     if(kind==play){//颤动控制
    16         if(move==true){
    17             play_y=2;
    18             move=false;
    19         }else{
    20             play_y=0;
    21             kind=none;
    22             game_state=4;
    23         }
    24     }else if(kind==score){
    25         if(move==true){
    26             score_y=2;
    27             move=false;
    28         }else{
    29             score_y=0;
    30             kind=none;
    31         }
    32     }
    33 }//---------------------------------------------------------------

    七、重量级问题解决:

    1、 飞翔弧度、旋转状态难题:

    正如前面的鸟的飞翔逻辑代码所示:鸟的飞翔过程并不是简单的自由上抛就能解决的;我通过大量实验发现必须把这个过程分为上面介绍的4步,然后每一步用更加详细的数学公式计算鸟的运行逻辑[因为此处我们必须考虑鸟的旋转效果和鸟的速度同步,所以这才是难点所在]。因为上面已经详细说明了,这里就不再重复,但这是一大难点!

    2、 混音效果、完美封装处理:

    本来音乐播放只要用PlaySound函数一句话就能产生音乐播放效果,但是当全部节点都放好音乐时,发现当鸟正好越过柱子发出加分的铃声时和鸟飞翔的声音无法混合播放,而是出现了严重的打断效果!导致听起来很不舒服。难道只有重新用Direct-X来处理混音吗?想想就冒汗….毕竟游戏已经接近尾声了,没必要再推翻MFC框架而用Direct-X来吧!于是发现用用2个不同的函数可以解决这个问题,即:其他部分不变还是用PlaySound函数,而分数增加的音乐用mciSendString函数来播放可以解决问题。

    但是mciSendString只能加在特定路径下的音频,无法处理资源文件下的wav文件,这该怎么办呢?难道要放弃资源的全封装效果?那多不好,于是还是被我解决了!我采用的思路是:把资源文件读到一个中间虚拟文件,然后把该中间文件加载金mciSendString就可以啦!下面是如何读取资源文件并转为中间文件的函数:

    注:PlaySound(MAKEINTRESOURCE(ID),AfxGetResourceHandle(),SND_RESOURCE|SND_ASYNC);

     1 //---------------------------------------------------------------
     2 bool ExtractResource(LPCTSTR strDstFile, LPCTSTR strResType, LPCTSTR strResName)
     3 {
     4     // 创建文件
     5     HANDLE hFile = ::CreateFile(strDstFile, GENERIC_WRITE, NULL, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, NULL);
     6     if (hFile == INVALID_HANDLE_VALUE)
     7         return false;
     8 
     9     // 查找资源文件中、加载资源到内存、得到资源大小
    10     HRSRC    hRes    = ::FindResource(NULL, strResName, strResType);
    11     HGLOBAL    hMem    = ::LoadResource(NULL, hRes);
    12     DWORD    dwSize    = ::SizeofResource(NULL, hRes);
    13     
    14     // 写入文件
    15     DWORD dwWrite = 0;      // 返回写入字节
    16     ::WriteFile(hFile, hMem, dwSize, &dwWrite, NULL);
    17     ::CloseHandle(hFile);
    18 
    19     return true;
    20 }//--------------------------------------------------------------

    3、 创建分享、窗口截屏技术:

    其实已经解决上面几个问题已经仿的差不多啦,但是还不完美!于是开始着手解决那个分享按钮[要知道这对MFC来说难度不亚于不用引擎来做图像处理!]可是这并不代表问题不可解。先不说,先看看效果!

    知道难度了吧!这是分享按钮自动创建的网页,然后还有图片信息,下载链接[这样才会吸引更多的人玩]由于这里涉及到非基础MFC知识,这里只提示一下:用到的技术是HTML+JS技术[也就是网页编程+脚本设计]

    上一篇返回首页 下一篇

    声明: 此文观点不代表本站立场;转载务必保留本文链接;版权疑问请联系我们。

    别人在看

    Destoon 模板存放规则及语法参考

    Destoon系统常量与变量

    Destoon系统目录文件结构说明

    Destoon 系统安装指南

    Destoon会员公司主页模板风格添加方法

    Destoon 二次开发入门

    Microsoft 将于 2026 年 10 月终止对 Windows 11 SE 的支持

    Windows 11 存储感知如何设置?了解Windows 11 存储感知开启的好处

    Windows 11 24H2 更新灾难:系统升级了,SSD固态盘不见了...

    小米路由器买哪款?Miwifi热门路由器型号对比分析

    IT头条

    Synology 对 Office 套件进行重大 AI 更新,增强私有云的生产力和安全性

    01:43

    StorONE 的高效平台将 Storage Guardian 数据中心占用空间减少 80%

    11:03

    年赚千亿的印度能源巨头Nayara 云服务瘫痪,被微软卡了一下脖子

    12:54

    国产6nm GPU新突破!砺算科技官宣:自研TrueGPU架构7月26日发布

    01:57

    公安部:我国在售汽车搭载的“智驾”系统都不具备“自动驾驶”功能

    02:03

    技术热点

    如何删除自带的不常用应用为windows 7减负

    MySQL中多表删除方法

    改进的二值图像像素标记算法及程序实现

    windows 7 32位系统下手动修改磁盘属性例如M盘修改为F盘

    windows 7中怎么样在家庭组互传文件

    Linux应用集成MySQL数据库访问技巧

      友情链接:
    • IT采购网
    • 科技号
    • 中国存储网
    • 存储网
    • 半导体联盟
    • 医疗软件网
    • 软件中国
    • ITbrand
    • 采购中国
    • CIO智库
    • 考研题库
    • 法务网
    • AI工具网
    • 电子芯片网
    • 安全库
    • 隐私保护
    • 版权申明
    • 联系我们
    IT技术网 版权所有 © 2020-2025,京ICP备14047533号-20,Power by OK设计网

    在上方输入关键词后,回车键 开始搜索。Esc键 取消该搜索窗口。