基于MFC实现的赛车游戏

news2024/11/23 16:44:58

一、问题描述

游戏背景为一环形车道图,选择菜单选项“开始游戏”则可开始游戏。游戏的任务是使用键盘上的方向键操纵赛道上的蓝色赛车追赶红色赛车,红色赛车沿车道顺时针行驶,出发点和终点均位于车道左上方。任一赛车先达到终点则比赛结束。

二、编程要求

  • 矩形车道和赛车的图像可辨认,显示正确
  • 赛道内有红色和蓝色两辆赛车,红色赛车由程序控制,蓝色赛车用键盘操纵
  • 赛车在矩形车道上运行,方向应与赛车形状相匹配
  • 用光标键控制蓝色赛车追赶红色赛车,赛车不能偏离跑道
  • 当某一赛车到达终点时,显示消息框
  • 正确设置游戏菜单(开始,结束)

三、要点分析

本题主要涉及到的知识点有:定时器消息、位图显示、画笔/画刷的使用、显示文字等工作,难度适中。

该题的难点在于如何保证赛车的头部永远向前。这里使用了一幅位图,它由四部分组成,如图1所示。为了方便编程,假定赛车是正方形,即赛车位图的宽度和高度相同。在本题中每一辆赛车位图的宽度和高度均为48个像素点,整个位图的宽度为192个像素点,高度为48个像素点。根据赛车在运行时的方向,将赛车位图的一部分显示到屏幕。

四、解题步骤

首先用AppWizard生成一个名为Car的SDI程序框架,其选项均用缺省设置。

编辑项目的菜单资源,在框架窗口的主菜单(IDR_MAINFRAME)中添加一个下拉菜单选项“游戏”,添加两个菜单选项“开始”和“结束”,并删除不必要的菜单选项,如图2所示。菜单属性设置如表5所示。

IDCaptionPrompt
ID-BEGIN开始开始
ID_STOP结束结束

利用ClassWizrd为视图类添加与这些菜单选项对应的成员函数。进入ClassWizard的Message Maps选项卡,选择Class Name项为CCarView,在Object IDs列表框中分别选择新添加的菜单选项的ID,在Messages列表框中选择COMMAND,按下Add Function按钮添加成员函数。ClassWizard会为这些菜单选项添加相应的消息响应函数(目前尚没有具体内容,需要程序员自行加入有关的处理代码),并将其声明加入OCarView类定义,在消息映射宏中加入相应的消息映射。

使用Developer Studio菜单的Insert / Resource…选项调出Insert Source对话框,为项目添加两个位图资源,分别表示红车和蓝车的位图。相应位图的资源ID分别为IDB_BITMAP1和IDB_BITMAP2。

完成以上工作后,即可修改程序框架,添加必要的代码。

五、源程序清单

在视图类OCarView类头文件首部添加三个常量,在类中再添加若干数据成员:

const CRect rectRed = CRect (30, 30, 750, 490);      //红车位置
const CRect rectBlue = CRect (30, 90, 700, 430);     //蓝车位置
const CRect rectGreen = Crect (150, 150, 640, 340);  //草坪位置
class OCarView: public CView
{
    //此处略去若干行由系统生成的代码
    CBitmap m_bmpRed;                //红车的位图
    CBitmap m_bmpBlue;               //蓝车的位图
    int m_nWidth;                    //位图的宽度
    int m_nHeight;                   //位图的高度
    CRect m_rectRed;                 //红车的区域
    int m_nRed;                      //红车的方向,0:左;1:下;2:右;3:上
    CRect m_rectBlue;                //蓝车的区域
    int m_nBlue;                     //蓝车的方向,0:左;1:下;2:右;3:上
};

在视图类的构造函数中添加代码:

CCarView ::CCarView ( )
{
    m_bmpRed. LoadBitmap (IDB_BITMAP1);
    m_bmpBlue. LoadBitmap (IDB_BITMAP2);
    BITMAP BM;
    m_bmpRed. GetBitmap (&BM);
    m_nWidth = BM. bmWidth/4;
    m_nHeight = BM. bmHeight;
}

在视图类的虚函数OnInitialUpdate ( )中添加代码:

void CCarView :: OnInitialUpdate ( )
{
    CView :: OnInitialUpdate ( );
    //定义红车的赛道
    m_rectRed = CRect (rectRed. Left, rectRed. top, rectRed. left + m_nWidth, rectRed. top + m_nHeight);       
    //定义蓝车的赛道
    m_rectBlue = CRect (rectBlue. left, rectBlue. top,
rectBlue. left + m_nWidth, rectBlue. top + m_nHeight);   
    //赛车开始时方向均向右
    m_nRed = m_nBlue = 0;                                
}

在OnDraw ( )函数中添加代码:

void CCarView :: OnDraw (CDC * pDC)
{
    CCarDoc * pDoc = GetDocument ( );
    ASSERT_VALID (pDoc);
    CBrush brushgreen, * pubrushOld;
    CPen penBlue, * ppenOld;
    penBlue. CreatePen (PS_SOLID, 1, RGB (0, 0, 128) );
    brushgreen. CreateSolidBrush (RGB (0, 255, 0) );
    ppenOld = pDC - > SelectObject (&penBlue);
    pbrushOld = pDC - > SelectObject (&brushgreen);
    pDC - > Rectangle (&rectRed);              //画赛道
    pDC - > SelectStockObject (WHITE_BRUSH);
    pDC - > Rectangle (&rectGreen)//画中间区域
    CDC MemDC;
    MemDC. CreateCompatibleDC (NULL);          //建立虚拟设备环境
    MemDC. SelectObject (&m_bmpRed);
    pDC - > BitBlt (m_rectRed. left + 1, m_rectRed. top + 1, m_nWidth, m_nHeight, &MemDC, m_nWidth * m_nRed, 0, SRCCOPY);     //画红车
    MemDC. SelectObject (&m_bmpBlue);
    pDC - > BitBlt (m_rectBlue. left + 1, m_rectBlue. top + 1, m_nWidth, m_nHeight, &MemDC, m_nWidth * m_rectBlue, 0, SRCCOPY);     //画蓝车
    pDC - > SelectObject (pbrushOld);
    pDC - > SelectObject (ppenOld);
}

在视图类中添加定时器消息处理函数OnTimer ( ),并在其中添加代码:

void CCarView :: OnTimer (UINT nIDEvent)
{
    InvalidateRect (m_rectRed);
    switch (m_nRed)                           //红车的方向
    {
    case 0:                                    //向右
        if (m_rectRed. right + 10 < rectRed. right)
            m_rectRed + = CSize (10, 0);
        else
            m_nRed = 1;
        break;
    case 1:                          //向下
        if (m_rectRed. bottom + 10 < rectRed. bottom)
            m_rectRed + = CSize (0, 10);
        else
            m_nRed = 2break;
    case 2:                          //向左
        if (m_rectRed. left > rectRed. left)
            m_rectRed + = CSize (-10, 0);
        else
            m_nRed = 3;
        break;
    case 3:                        //向上
        if (m_rectRed. top > rectRed. top)
            m_rectRed + = CSize (0, -10);
        else
        {
            KillTimer (1);                   //游戏结束
            MessageBox (〃You Lose.);
        }
    }
    InvalidateRect (m_rectRed);
    CView :: OnTimer (nIDEvent);
}

在视图类中添加键盘按下消息处理函数OnKeyDown ( ),并在其中添加代码:

void CCarView :: OnKeyDown (UINT nchar, UINT nRepCnt, UINT nFlags)
{
    InvalidateRect (m_rectBlue);
    switch (nChar)
    {
    case 39:                      //向左光标键
        m_nBlue = 0;
        if (m_rectBlue. right + 10 < rectBlue. right&&m_rectBlue. bottom < rectGreen. top)
            m_rectBlue + = CSize (10, 0);
        break;
    case 40:                   //向下光标键
        m_nBlue = 1;
        if (m_rectBlue. bottom + 10 < rectBlue. bottom&&m_rectBlue. left > rectGreen. right)
            m_rectBlue + = CSize (0, 10);
        break;
    case 37:                  //向右光标键
        m_nBlue = 2;
        if (m_rectBlue.left > rectBlue. left&&m_rectBlue. top > rectGreen. bottom)
            m_rectBlue + = CSize (-10, 0);
        break;
    case 38:                      //向上光标键
        m_nBlue = 3;
        if (m_rectBlue. top > rectRed. top&&m_rectBlue. right < rectGreen. left)
            m_rectBlue + = CSize (0, -10);
        if (m_rectBlue. Bottom< rectBlue. top)
        {
            KillTimer (1);         / /游戏结束
            MessageBox (〃You Win.);
        }
    }
    InvalidateRect (m_rectBlue);
    CView :: OnKeyDown (nChar, nRepCnt, nFlags);
}

在视图类中新增菜单对应的消息处理函数中添加代码:

void CCarView :: OnBegin ( )
{
    OnInitialUpdate ( );
    SetTimer (1, 100, NULL);                  / /设置定时器
}
void CCarView :: OnStop ( )
{
    KillTimer (1);                           / /删除定时器
}

六、输入输出

用户可选择框架窗口菜单“演示”中的选项“开始”,运行结果如图3所示。

七、小结

在视图类中重载了键盘按下消息处理函数OnKeyDown ( ),在该函数中,仅响应用户按下的四个方向键,用以修改赛车的位置。

在OnKeyDown ( )中调用了两次InvalidateRect ( ),两者都只有一个参数,表明擦除时应重画背景。

八、进一步工作

本程序还可以添加“信号灯”功能,在中间白色区域画一信号灯,当用户选择了“开始”后不能马上比赛,而要等到信号灯从红变黄,再从黄变绿后,才能开始比赛。

另外还可以添加难度菜单,难度越高,红车速度越快。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2237270.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

实验三 JDBC数据库操作编程(设计性)

实验三 JDBC数据库操作编程&#xff08;设计性&#xff09; 实验目的 掌握JDBC的数据库编程方法。掌握采用JDBC完成数据库链接、增删改查&#xff0c;以及操作封装的综合应用。实验要求 本实验要求每个同学单独完成&#xff1b;调试程序要记录调试过程中出现的问题及解决办法…

Java期末复习暨学校第二次上机课作业

Java期末复习暨学校第二次上机课作业&#xff1a;了解程序的控制结构&#xff0c;掌握顺序结构程序的设计方法&#xff0c;掌握分支程序设计方法。 第一题&#xff1a; 闰年有两种判断方式&#xff1a; &#xff08;1&#xff09;&#xff1a;能被4整除但不能被100整除 &…

Windows配置NTP时间同步

Windows下实现NTP时间同步 1、Windows时间服务(W32Time)2、Windows 时间同步的工作原理3、配置和管理 Windows 时间同步3.1 命令行工具&#xff1a;w32tm3.2 控制面板中的设置 4. 高级设置&#xff08;Windows Server 环境&#xff09;5.调整时间同步的间隔5.1 通过组策略调整时…

Go八股(Ⅳ)***slice,string,defer***

***slice&#xff0c;string&#xff0c;defer*** 1.slice和arry的区别 arry&#xff1a; Go语言中arry即为数据的一种集合&#xff0c;需要在声明时指定容量和初值&#xff0c;且一旦声明就长度固定&#xff0c;访问时按照索引访问。通过内置函数len可以获取数组中的元素个…

STM32H503开发(2)----STM32CubeProgrammer烧录

STM32H503开发----2.STM32CubeProgrammer烧录 概述硬件准备视频教学样品申请源码下载参考程序自举模式BOOT0设置UART烧录USB烧录 概述 STM32CubeProgrammer (STM32CubeProg) 是一款用于编程STM32产品的全功能多操作系统软件工具。 它提供了一个易用高效的环境&#xff0c;通过…

ubuntu下aarch64-linux-gnu(交叉编译) gdb/gdbserver(二)

ubuntu下aarch64-linux-gnu(交叉编译) gdb/gdbserver&#xff08;二&#xff09; 本教程作为gdb/gdbserver编译安装教程的一个补充&#xff0c;教会大家如何使用gdb/gdbserver进行远程调试。 如上图所示&#xff0c;我们需要将编译后的gdbserver上传至目标设备&#xff0c;其上…

架构零散知识点

1 数据库 1.1 数据库范式 有一个学生表&#xff0c;主键是学号&#xff0c;含有学生号、学生名、班级、班级名&#xff0c;违反了数据库第几范式&#xff1f; --非主属性不依赖于主键&#xff0c;不满足第二范式 有一个订单表&#xff0c;包含以下字段&#xff1a;订单ID&…

【时间之外】IT人求职和创业应知【31】

目录 新闻一&#xff1a;2024年“秦创原沣东杯”陕西省科技工作者创新创业大赛颁奖仪式暨沣东新城机器人产业发展大会盛大启幕 新闻二&#xff1a;声网CEO赵斌&#xff1a;RTE将成为生成式AI时代AI Infra的关键部分 新闻三&#xff1a;“5G工业互联网”融合应用试点城市名单…

移动开发(七):.NET MAUI使用RESTAPI实现查询天气笔记

目录 一、接口准备 二、实体部分 三、页面部分 四、后台代码逻辑 五、总结 在移动开发过程中,第三方对接是非常常见的。今天给大家分享.NET MAUI如何使用REST API实现输入城市名称查询天气的示例,希望对大家学习.NET MAUI可以提供一些帮助! 一、接口准备 首先我们需要…

Javascript中如何实现函数缓存?函数缓存有哪些应用场景?

#一、是什么 函数缓存&#xff0c;就是将函数运算过的结果进行缓存 本质上就是用空间&#xff08;缓存存储&#xff09;换时间&#xff08;计算过程&#xff09; 常用于缓存数据计算结果和缓存对象 解释 const add (a,b) > ab; const calc memoize(add); // 函数缓存…

【网络安全 | 并发问题】Nginx重试机制与幂等性问题分析

未经许可,不得转载。 文章目录 业务背景Nginx的错误重试机制proxy_next_upstream指令配置重试500状态码非幂等请求的重试问题幂等性和非幂等性请求non_idempotent选项的使用解决方案业务背景 在现代互联网应用中,高可用性(HA)是确保系统稳定性的关键要求之一。为了应对服务…

利用游戏引擎的优势

大家好&#xff0c;我是小蜗牛。 在当今快速发展的游戏产业中&#xff0c;选择合适的游戏引擎对开发者来说至关重要。Cocos Creator作为一款功能强大且灵活的游戏引擎&#xff0c;为开发者提供了丰富的工具和资源&#xff0c;使他们能够高效地开发出优秀的游戏。本文将探讨如何…

财务源码 财务软件 SaaS 云财务

&#x1f50d; 专业财务源码&#xff0c;助您快速开展财务管理&#xff01;&#x1f4c8; &#x1f3af; 我们提供一系列高质量、可定制、易于使用的财务源码&#xff0c;帮助您快速搭建强大的财务管理系统。无论是小型企业、中型企业&#xff0c;还是个人用户&#xff0c;我们…

数据流图,学习笔记

目录 一、数据流图的基本元素 外部实体&#xff08;External Entity&#xff09; 加工&#xff08;Process&#xff09; 数据存储&#xff08;Data Store&#xff09; 数据流&#xff08;Data Flow&#xff09; 二、数据流图的层次结构 顶层数据流图 中层数据流图 底层…

docker镜像文件导出导入

1. 导出容器&#xff08;包含内部服务&#xff09;为镜像文件&#xff08;docker commit方法&#xff09; 原理&#xff1a;docker commit命令允许你将一个容器的当前状态保存为一个新的镜像。这个新镜像将包含容器内所有的文件系统更改&#xff0c;包括安装的软件、配置文件等…

Android中桌面小部件framework层使用到的设计模式

在Android中&#xff0c;桌面小部件&#xff08;App Widget&#xff09;的Framework层采用了多种设计模式&#xff0c;以实现模块化、可维护性和高效的交互。 以下是Android桌面小部件Framework层中常用的设计模式及其具体应用&#xff1a; 1. 观察者模式&#xff08;Observe…

7.《双指针篇》---⑦三数之和(中等偏难)

题目传送门 方法一&#xff1a;双指针 1.新建一个顺序表用来返回结果。并排序数组。 2.for循环 i 从第一个数组元素遍历到倒数第三个数。 3.如果遍历过程中有值大于0的则break&#xff1b; 4.定义左右指针,以及target。int left i 1, right n - 1; int target -nums[i];…

DBeaver工具连接Hive

DBeaver工具连接Hive 首先解压安装包dbeaver-ce-latest-x86_64-setup.zip,并安装dbeaver-ce-latest-x86_64-setup.exe; 安装Kerberos客户端4.1-amd64.msi; 查看集群节点/etc/hosts文件内容,并追加到C:\Windows\System32\drivers\etc\hosts; 下载集群用户keytab文件,并解压…

从零开始 blender插件开发

blender 插件开发 文章目录 blender 插件开发环境配置1. 偏好设置中开启相关功能2. 命令行打开运行脚本 API学习专有名词1. bpy.data 从当前打开的blend file中&#xff0c;加载数据。2. bpy.context 可用于获取活动对象、场景、工具设置以及许多其他属性。3. bpy.ops 用户通常…

深入理解JVM

文章目录 1. JVM内存区域划分2. JVM中类加载过程类加载&#xff08;1&#xff09;类加载的基本流程&#xff08;2&#xff09;双亲委派模型 《深入理解java虚拟机》 在这本书前&#xff0c;面试官对于JVM也不是很了解。 这本书主要还是写个一下开发 JVM 的人。 1. JVM内存区域…