Windows编程大作业报告

大连民族学院

计算机科学与工程学院大作业

作业题目: 俄罗斯方块小游戏 课程名称: Windows编程

实验类型:□演示性 □验证性 □操作性 □设计性 ■综合性

专业:计算计科学与技术 班级: 计科094

学生姓名: 李玥 学号:2009081408

实验日期:20xx年11月12日 实验地点:多媒体机房

实验学时:4 学时 实验成绩:

指导教师签字: 年 月 日

目录

1. 问题描述 ..................................................................................... 3

2. 基本要求 ...................................................................................... 3

3. 概要设计 ...................................................................................... 3

4. 详细设计 ...................................................................................... 3

4.1 添加资源 .................................................................................................... 3

4.2 设置应用程序外观 .................................................................................... 4

4.3 实现客户区函操作 .................................................................................... 5

4.4 设计COptionDlg类 ................................................................................ 15

5. 编码与调试分析 ......................................................................... 16

6. 使用说明 .................................................................................... 16

7. 测试 ............................................................................................ 17

8. 心得体会 .................................................................................... 18

9. 附录(源代码) ......................................................................... 18

2

1. 问题描述

实现了一个简单的俄罗斯方块游戏,用键盘进行游戏操作,可以设置游戏级别、播放背景音乐、选择方块样式。

2. 基本要求

开始菜单栏和工具栏可以进行“开始”“暂停”“结束”“设置”等操作;菜单栏中的“选项”可以选择游戏级别和是否播放音乐;游戏设置可以对游戏区域大小、游戏级别选择、方块样式选择进行选择,还可以选择是否添加网格背景和是否播放音乐。

3. 概要设计

3.1 添加菜单栏、位图、对话框等资源。

3.2在MainFrame下 OnCreate函数中实现自定义的工具栏外貌,在

PreCreateWindow函数中设置窗口大小。

3.3 在View下添加所需成员变量、成员函数、常用消息响应函数和菜单对应的

消息响应函数。

3.4 创建COptionDlg类并添加成员变量与成员函数。

4. 详细设计

4.1 添加资源

4.1.1 菜单栏添加顶层菜单“游戏(G)”及下拉菜单“S.开始游戏”“P.游

戏暂停”“E.游戏结束”“O.游戏设计”“X.退出游戏”;添加顶层菜

单“选项(O)”及下拉选项“I.游戏级别”“M.音乐”,“I.游戏级别”属性选择“弹出”再添加如下几个级别“第一级:入门级”“第二级:初级”“第三级:中级”“第四级:高级”

4.1.2 添加位图rect.bmp,ID为默认值

4.1.3 添加对话框“游戏设置”,修改ID为“IDD_DLG_OPTION”,并添加静

3

态文本“游戏区域大小”与对应ID为IDC_COMBO_AREA的组合框(级别与样式与之雷同),添加“网格背景”“背景音乐”复选框

4.2 设置应用程序外观

4.2.1 在OnCreate函数中修改工具栏外观

(1) 定义CImageList类型变量 img和CString类型变量 str

(2) 修改创建工具栏代码如下:

if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP

| CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC)) {

return -1;

}

(3) 修改工具栏属性

//设置按钮的宽度和长度 m_wndToolBar.GetToolBarCtrl().SetButtonWidth(40, 100); //改变属性 m_wndToolBar.ModifyStyle(0, TBSTYLE_FLAT |CBRS_TOOLTIPS | TBSTYLE_TRANSPARENT|TBBS_CHECKBOX ); //设置按钮数为4 m_wndToolBar.SetButtons(NULL,4);

(4) 添加图像

//设置"热/hot"状态的的位图

img.Create(28, 28, ILC_COLOR8|ILC_MASK,2,2);

img.SetBkColor(RGB(0, 0, 102));

//顺序增添按键的图标

img.Add(AfxGetApp()->LoadIcon(IDI_ICON_START));

img.Add(AfxGetApp()->LoadIcon(IDI_ICON_PAUSE));

img.Add(AfxGetApp()->LoadIcon(IDI_ICON_END));

img.Add(AfxGetApp()->LoadIcon(IDI_ICON_SET));

m_wndToolBar.GetToolBarCtrl().SetHotImageList(&img);

img.Detach();

//设置"冷/cold"状态的位图

img.Create(32, 32, ILC_COLOR8|ILC_MASK, 2,2);

img.SetBkColor(::GetSysColor(COLOR_BTNFACE));

//顺序增添按键的图标

img.Add(AfxGetApp()->LoadIcon(IDI_ICON_START));

img.Add(AfxGetApp()->LoadIcon(IDI_ICON_PAUSE));

img.Add(AfxGetApp()->LoadIcon(IDI_ICON_END));

img.Add(AfxGetApp()->LoadIcon(IDI_ICON_SET));

m_wndToolBar.GetToolBarCtrl().SetImageList(&img);

img.Detach();

4

(5) 设置文字

m_wndToolBar.SetButtonInfo(0, ID_GAME_START, TBSTYLE_BUTTON, 0); m_wndToolBar.SetButtonText(0, "开始");

m_wndToolBar.SetButtonInfo(1, ID_GAME_PAUSH, TBSTYLE_BUTTON, 1); m_wndToolBar.SetButtonText(1, "暂停");

m_wndToolBar.SetButtonInfo(2, ID_GAME_END, TBSTYLE_BUTTON,2); m_wndToolBar.SetButtonText(2, "结束");

m_wndToolBar.SetButtonInfo(3, ID_GAME_OPTION, TBSTYLE_BUTTON,3); m_wndToolBar.SetButtonText(3, "设置");

CRect rectToolBar;

//得到按钮的大小

m_wndToolBar.GetItemRect(0, &rectToolBar);

//设置按钮的大小

m_wndToolBar.SetSizes(rectToolBar.Size(), CSize(32,32));

4.2.2 在PreCreateWindow函数中设置窗口大小

cs.cx = 500;//设置窗口大小

cs.cy = 500;

4.3 实现客户区函操作

4.3.1 定义宏变量

在RussianRect1408View.h中添加如下宏变量:

// 宏变量:白色和黑色

#define WHITE RGB(255,255,255)

#define BLACK RGB(0,0,0)

#define BLUE RGB(0,0,255)

//中断操作的运动趋势

#define LEFT 0 //向左移动

#define RIGHT 1 //向左移动

#define UP 2 //向上(变形)

#define DOWN 3 //向下移动(加速)

//游戏区域地图最大限制

#define MAX_ROW 100 //地图的最大行数

#define MAX_COL 100 //地图的最大列数

//地图状态

#define MAP_STATE_EMPTY 0 //空(未被占据)

5

#define MAP_STATE_NOT_EMPTY 1 //被占据

4.3.2 添加头文件

在RussianRect1408View.h中添加游戏设置对话框头文件和音乐播放相关头文件:

#include "OptionDlg.h"

//音乐播放

#include "mmsystem.h"

#pragma comment(lib,"Winmm.lib")

4.3.3 添加成员变量

在View类下添加如下成员变量并设置为protected:

//用于记录当前区域大小与当前级别的字符串,用于显示在屏幕上提示用户当前状态 CString m_strLevel; CString m_strArea; CPen * m_pBlackPen;//黑色画笔 CBrush *m_pGrayBrush;//灰色画刷指针 CBrush * m_pBlackBrush;//黑色画刷指针 int m_nWidth;//子窗口的宽度 int m_nHeight;//子窗口的高度 //内存绘图设备的处理 CBitmap m_memBmp;//内存位图 CDC m_memRectDC; //方块内存设备环境 CDC m_memDC; //内存设备环境 int m_iStartY; //游戏区域左上角的坐标 int m_iStartX; bool m_isBottom; //标识方块已到底的变量,到底为TRUE int m_iPerformance;//游戏总成绩 int m_iLevel; //当前的级别,换算成速度的算法为:1500 - m_iLevel*200 int m_iLarge; //小方块的大小,它会随着行与列的不同而不同,具 体为: // 12行10列,30个象素的正方形 // 18行15列,20 // 24行20列,15 // 30行25列,12 int m_inextStatus; //下一个将会出现的样式 int m_icurrentStatus;//当前的样式 6

int m_iRow; //列数 int m_iCol; //行数 int m_iBlockSytle; //当前选择的方块显示样式 HBITMAP m_hMemRectBmp;//方块内存位图句柄 int m_currentRect; //当前的方块形状 BOOL m_bMusic; //是否插放背景音乐 bool m_bGamePaush; //游戏是否暂停 bool m_bGameEnd; //游戏是否已结束,为FALSE表示开始,否则为结束 int m_bFistPlay; //是否是第一次开始游戏 BOOL m_bDrawGrid;//是否画网格线 int InterFace[74][4];//接触面二维数组,记录1~7种下坠物的1~4种形态的接触面信息 //我们把该下坠物的某种形态种的4个方块,有接触面则记录方位,无则为-1标识。 int GameStatus[MAX_ROW][MAX_COL];//用于标志方块状态的数组,大小由横格

与竖格数据决定,为1表示该方块显示,或者不显示,画形状只需要修改数组中相应元素的状态值即可

//由时钟控制,定时在OnPaint函数中根据数组画方块

int ActiveStatus[4][2]; //用于保存当前方块的动态位置,4个小方块分别在大数组中的位置

//存入下一次要出来的方块的模样的数组

int NextStatus[4][2];//存放位置为先左后右,每一列又遵循先上后下的原则

4.2.4 在构造函数中初始化

m_bFistPlay = TRUE; //第一次开始游戏 m_bGamePaush = FALSE; //缺省为不是游戏暂停状态 m_bMusic = FALSE; //缺省为不插放背景音乐 7

m_bDrawGrid = TRUE; //缺省为画网格线

m_iPerformance = 0; //总分值清零

//缺省值:为12行,10列

m_iRow = 12;

m_iCol = 10;

//左上角X,Y坐标

m_iStartX = 10;

m_iStartY = 10;

m_iLevel = 3; //缺省级别为3级

m_iBlockSytle = 0; //第一种样式

//缺省方块大小为m_iLarge个象素

m_iLarge = 30;

//缺省游戏是结束的

m_bGameEnd = TRUE;

int i,j;

//赋初值

for (i=0;i<100;i++)

for (j=0;j<100;j++)

GameStatus[i][j]=0;

//如果某种形状的方块没有4个接触面,则后面的数据填-1

for (i=0;i<74;i++)

for (j=0;j<4;j++)

InterFace[i][j] = -1;

/*1 */

InterFace[1][0] = 3;

InterFace[11][0] = 0; InterFace[11][1] = 1; InterFace[11][2] = 2;

InterFace[11][3] = 3;

/*2 */

InterFace[2][0] = 1; InterFace[2][1] = 3;

/*3 */

InterFace[3][0] = 0; InterFace[3][1] = 2; InterFace[3][2] = 3;

InterFace[31][0] = 2; InterFace[31][1] = 3;

8

InterFace[32][0] = 0; InterFace[32][1] = 2;

3;

InterFace[33][0] = 0; InterFace[33][1] = 3;

/*4 */

InterFace[4][0] = 1; InterFace[4][1] = 3;

InterFace[41][0] = 0; InterFace[41][1] = 2;

3;

/*5 */

InterFace[5][0] = 1; InterFace[5][1] = 3;

InterFace[51][0] = 0; InterFace[51][1] = 2;

3;

/*6 */

InterFace[6][0] = 0; InterFace[6][1] = 3;

InterFace[61][0] = 1; InterFace[61][1] = 2;

3;

InterFace[62][0] = 2; InterFace[62][1] = 3;

InterFace[63][0] = 0; InterFace[63][1] = 1;

3;

/*7 */

InterFace[7][0] = 2; InterFace[7][1] = 3;

InterFace[71][0] = 1; InterFace[71][1] = 2;

3;

InterFace[72][0] = 0; InterFace[72][1] = 3;

InterFace[73][0] = 0; InterFace[73][1] = 1;

3; InterFace[32][2] = InterFace[41][2] = InterFace[51][2] = InterFace[61][2] = InterFace[63][2] = InterFace[71][2] = InterFace[73][2] =

4.2.5 添加成员函数

在View类下添加如下成员变量并设置为protected(具体代码参见附录): void ActiveStatusToGameStatus();//将当前下坠物的位置映射到游戏区域

地图数组中去

void RectStatusToNextStatus(int m_which); //初始掉落时,将根据方块的样式决定下一次将要掉下来的动态数组的值 void RectStatusToActiveStatus(int m_which); //初始掉落时,将根据方块的样式决定当前动态数组的值 void RectDown();//方块下降处理函数 9

void RectChange();//方块变形处理函数 void RectArrow(int m_Type);// 当前方块下降加速,左移,右移 int Random(int MaxNumber); //:产生随机数(Random) 函数,参数:MaxNumber : 随机数的上限 ,返回值: 产生的随机数 bool IsRightLitmit();//方块是否还可以右移 bool IsLeftLimit();//方块是否还可以左移 void IsBottom();//判断当前方块是否已到底,并且销行等相关的工作 void InvalidateCurrent();//刷新当前的区域,只刷新需要刷新的四个小方块区域,防止屏幕抖动情况发生 void DrawGame(CDC *pDC);//绘制游戏背景、小方块游戏显示区域 void DcEnvInitial(void);//绘图设备环境的初始化 void DCEnvClear(void);// 绘图设备环境的清理 void CurrentAreaAndLevel();//用于生成当前区域大小与级别所对应的汉字描述 void StopMid();//终止插放背景音乐 void PlayMid();//插放背景音乐, 所有错误忽略

4.2.6 添加常用消息响应函数

(1)在OnCreate函数里创建一些设备,添加如下语句: //决定第一次掉下来的方块的样式

m_inextStatus = Random(7); DcEnvInitial();//函数调用 DrawGame(&m_memDC); //在内存位图的游戏区域绘制 pDC->BitBlt(0,0,m_nWidth,m_nHeight,&m_memDC,0,0,SRCCOPY);//贴图 switch(nChar) { case VK_LEFT: RectArrow(LEFT); break; case VK_RIGHT: RectArrow(RIGHT); break; case VK_UP: RectChange(); break; case VK_DOWN: RectArrow(DOWN); break; } //如果原来的方块已到底或游戏刚开始,则掉下一个新的方块 int i,j,k; 10 (2)在OnDraw函数中绘图 (3)在OnKeyDown函数中处理用户的输入,方块的左,右移,加速及变形 (4)在OnTimer函数中设置驱动

if (m_isBottom) { //产生下一个随机下坠物 m_icurrentStatus = m_inextStatus; m_inextStatus = Random(7); //得到下一次的方块样式 //修改新的“下一下坠物” RectStatusToNextStatus( m_inextStatus ); //将旧的“下一下坠物”用作当前使用 m_currentRect = m_icurrentStatus; //根据当前下坠物的形状去初始化激活状态下的下坠物坐标 RectStatusToActiveStatus( m_icurrentStatus ); //将当前动态数组中的数据反映到大数组中 ActiveStatusToGameStatus(); m_isBottom = FALSE; IsBottom(); //判断当前方块是否已到底 //判断游戏是否已结束: 碰了底,且第1行有小方块 if (m_isBottom) for (i=0;i<m_iCol;i++) if (GameStatus[0][i]) { KillTimer(1); AfxMessageBox("游戏已结束!"); for (j=0;j<m_iRow;j++) for (k=0;k<m_iCol;k++) GameStatus[j][k]=0; Invalidate(FALSE); m_bGameEnd = TRUE; break; } } else //当前方块下降 { RectDown(); }

4.2.7 添加菜单对应的消息响应函数

(1) 添加“S.开始游戏”菜单项的命令响应函数OnGameStart() 并在内部添

加如下代码:

if (!m_bGamePaush) //如果不是游戏暂停状态,则必须作些初始工作

{ 11

m_bGameEnd = FALSE; //总分值清零, 并显示总分记分牌 m_iPerformance = 0; //显示当前的区域及游戏级别的汉字描述 CurrentAreaAndLevel(); CRect rect(m_iStartY, m_iStartX, m_iStartY+440, m_iStartX+370); InvalidateRect(&rect); } m_bGamePaush = FALSE; SetTimer(1,1500-250*m_iLevel,NULL);

同理添加“P.游戏暂停”菜单项的命令响应函数,内部添加如下语句:

m_bGamePaush = TRUE; KillTimer(1); m_bGameEnd = TRUE; int i,j; for (i=0;i<m_iRow;i++) for (j=0;j<m_iCol;j++) GameStatus[i][j]=0; CRect rect(m_iStartY, m_iStartX, m_iStartY+440, m_iStartX+370); InvalidateRect(&rect); m_bGamePaush = FALSE; //清除游戏暂停状态 KillTimer(1);

//参数顺序: 区域大小代码:0-3,分别为:12X10,18X15,24X20,m_iLargeX25 //背景音乐:TRUE 或者 FALSE

int m_lsArea;//区域大小对象

switch(m_iRow)

{

case 12:

m_lsArea = 0;

break;

case 18:

m_lsArea = 1;

break;

case 24:

m_lsArea = 2;

break;

case 30:

12

同理添加“E.游戏结束”菜单项的命令响应函数,内部代码如下: 同理添加“O.游戏设计”菜单项的命令响应函数,内部代码如下:

m_lsArea = 3; break; }

COptionDlg dlg;//创建COptionDlg类对象dlg

if (dlg.DoModal()==IDOK)

{

//确定区域的大小

switch(dlg.m_iArea)

{

case 0:

m_iRow = 12;

m_iCol = 10;

m_iLarge = 30;

break;

case 1:

m_iRow = 18;

m_iCol = 15;

m_iLarge = 20;

break;

case 2:

m_iRow = 24;

m_iCol = 20;

m_iLarge = 15;

break;

case 3:

m_iRow = 30;

m_iCol = 25;

m_iLarge = 12;

break;

}

m_iLevel = dlg.m_iLevel; //确定级别

m_iBlockSytle = dlg.m_iBlockStyle; //选择的样式

m_bDrawGrid = dlg.m_bDrawGrid; //确定是否绘网格背景 m_bMusic = dlg.m_bMusic; //检查是否插放音乐 if (m_bMusic)

PlayMid();

else

StopMid();

13

Invalidate();//重绘窗口 } //清理内存设备环境,释放资源 DCEnvClear(); 同理添加“X.退出游戏”菜单项的命令响应函数,内部添加如下语句:

(2) 添加(1)中5个菜单项的Update响应函数,进行菜单屏蔽与开启控制: 如果游戏开始,则 [开始游戏]按钮被屏蔽

在OnUpdateGameStart在内部添加如下代码:

if (m_bGameEnd || m_bGamePaush)

pCmdUI->Enable(TRUE); else pCmdUI->Enable(FALSE);

其他菜单项的语句与之雷同

(3) 添加“I.游戏级别”的命令响应函数和Update响应函数,以第一级为

例:

在OnOptionLevel1函数中添加以下语句:

m_iLevel = 0;

在OnUpdateOptionLevel1中添加如下代码:

if (m_bGameEnd)

pCmdUI -> Enable(TRUE); else pCmdUI -> Enable(FALSE); if (m_iLevel == 0) pCmdUI -> SetCheck(TRUE); else pCmdUI -> SetCheck(FALSE);

(4) 添加“M.音乐”的命令响应函数和Update响应函数

在OnOptionMusic函数中添加如下代码:

if (m_bMusic) { m_bMusic = FALSE; StopMid(); } else { m_bMusic = TRUE; PlayMid(); }

在OnUpdateOptionMusic函数中添加如下代码:

if (m_bGameEnd)

pCmdUI -> Enable(TRUE); else pCmdUI -> Enable(FALSE); 14

if (m_bMusic) pCmdUI -> SetCheck(TRUE); else pCmdUI -> SetCheck(FALSE);

4.4 设计COptionDlg类

4.4.1 在“网格背景”“背景音乐”复选框管理BOOL型变量m_bMusic和

m_bDrawGrid;

4.4.2 添加成员变量

int m_iArea,m_iLevel, m_iBlockStyle; //用户选择的区域大小代码,及级

别代码,方块样式。 //原先设置的区域、级别、样式 int m_oldArea; int m_oldLevel; int m_oldBlockSytle; BOOL m_oldMusic; BOOL m_oldDrawGrid;

4.4.3 添加OnInitDialog函数和OnOK函数

在OnInitDialog中初始对话框,代码如下:

//参传来的参数赋初值 CComboBox *m_comboArea; CComboBox *m_comboLevel; CComboBox *m_comboBlockStyle; CButton *m_btnDrawGrid; CButton *m_btnMusic; m_comboArea = (CComboBox *)GetDlgItem(IDC_COMBO_AREA); m_comboArea -> SetCurSel( m_oldArea ); m_comboLevel = (CComboBox *)GetDlgItem(IDC_COMBO_LEVEL); m_comboLevel -> SetCurSel( m_oldLevel ); m_comboBlockStyle = (CComboBox *)GetDlgItem(IDC_COMBO_BLOCK_SYTLE); m_comboBlockStyle -> SetCurSel( m_oldBlockSytle ); m_btnDrawGrid = (CButton *)GetDlgItem(IDC_CHECK_GRID); m_btnDrawGrid -> SetCheck(m_oldDrawGrid); m_btnMusic = (CButton *)GetDlgItem(IDC_CHECK_MUSIC); m_btnMusic -> SetCheck(m_oldMusic);

15

在OnOK()中添加如下代码确定更改后的信息:

CComboBox *m_comboArea; CComboBox *m_comboLevel; CComboBox *m_comboBlockStyle; m_comboArea = (CComboBox *)GetDlgItem(IDC_COMBO_AREA); m_comboLevel = (CComboBox *)GetDlgItem(IDC_COMBO_LEVEL); m_comboBlockStyle = (CComboBox *)GetDlgItem(IDC_COMBO_BLOCK_SYTLE); m_iArea = m_comboArea -> GetCurSel(); if (m_iArea<0 || m_iArea>3) m_iArea = 0; m_iLevel = m_comboLevel -> GetCurSel(); if (m_iLevel<0 || m_iLevel>5) m_iLevel = 2; m_iBlockStyle = m_comboBlockStyle -> GetCurSel(); if (m_iBlockStyle<0 || m_iBlockStyle>5) m_iBlockStyle = 0;

5. 编码与调试分析

(1) 由于程序涉及函数颇多,考虑全所有函数和逐一设计是一个难点

(2) 设置BOOL型变量与bool型没有考虑到二者的差别,传参时出现错误

(3) 网格和音乐复选框应该从ClassWizard中关联变量,而不是自己定义

(4) 涉及音乐操作的函数应该先加载头文件#include "mmsystem.h"和#pragma comment(lib,"Winmm.lib")

(5) 更改绘图区域时,右侧显示的下一个方块形状,四个方格之间的距离与“12行10列”的设置位置相同,这个问题还没有解决

(6) 游戏设置缺省值没有显示

6. 使用说明

(1) 在开始游戏之前可以点击“设置”按钮或菜单中“游戏设置”及“选项”对游戏进行设置

(2) 点击“开始”按钮或菜单中的“开始游戏”即可对游戏进行操作

(3) 按键盘左、右按钮可以调整方块左右移动,向下键可加速,向上键可改变形状

(4) 画面右侧显示下一方块形状、得分、区域大小和游戏级别

(5) 点击“暂停”“结束”按钮或菜单中对应项可暂停或结束游戏

16

7. 测试

游戏可以正常操作,运行效果图如下:

Windows编程大作业报告

更改游戏区域大小后的显示:

Windows编程大作业报告

17

游戏设置显示:

Windows编程大作业报告

8. 心得体会

参考别人的代码完成的这个程序,很多东西自己理解得并不深,还有一些功能没有实现,有点遗憾。

9. 附录(源代码)

自定义的成员函数代码参见源程序

18

相关推荐