计算机网络技术 计算机信息管理系统 其它计算机 课程设计 网络工程 信息化工程学 电子工程学毕业论文 计算机应用毕业论文 数据库毕业论文 计算机硬件 计算机 电子商务 计算机通信计算机软件
返回首页

用VC++设计与实现俄罗斯方块游戏

时间:2010-09-27 10:07来源:未知 作者:admin 点击:
目 录 1 引 言6 1.1 游戏的历史6 1.2 Windows编程特点7 1.3 Visual C++ 6.0编程特点 8 1.4 课程设计目的8 2 需求分析9 2.1 游戏需求9 2.2 软硬件需求9 2.3 接口控制10 3 概要设计10 3.1 定义方块的数据结构10 3.2 游戏设计分析11 3.3游戏流程图13 4 详细设计14 4.1 总

目  录
1  引  言 6
1.1 游戏的历史 6
1.2 Windows编程特点 7
1.3 Visual C++ 6.0编程特点  8
1.4  课程设计目的 8
2  需求分析 9
2.1 游戏需求 9
2.2 软硬件需求 9
2.3 接口控制 10
3  概要设计 10
3.1 定义方块的数据结构 10
3.2 游戏设计分析 11
3.3游戏流程图 13
4  详细设计 14
4.1 总体设计 14
4.2 创建界面的主框架 15
4.3 正常流程的设计                                           16
4.4 中断操作流程的实现 23
4.5 游戏区域绘图的实现 27
4.6 功能的完善 27
4.7 游戏演示  29
参考文献 30

摘要  本课程设计主要解决利用Visual C++设计与实现俄罗斯方块游戏的算法、游戏内部功能、游戏区域的绘图的程序设计。在课程设计中,系统开发平台为Windows 2000,程序设计设计语言采用Visual C++ 6.0,程序运行平台为Windows 98/2000/XP。对于每个俄罗斯方块各自的形态变换改变到下一个形态利用数组来实现,在程序设计中,采用MFC机制来实现,并且直接从架构的其中一个类对象中继承下来,将它完善而变成有某功能特性的物件对象。

关键词  俄罗斯方块;MFC;算法;数组
1  引  言
俄罗斯方块是个老幼皆宜的小游戏,它实现由四块正方形的色块组成,然后存储在一个数组的四个元素中,计算机随机产生不同七种类型的方块,根据计算机时钟控制它在一定的时间不停的产生,用户根据键盘的四个方向键控制翻转、向左、向右和向下操作,(控制键的实现是由键盘的方向键的消息函数实现)。然后程序根据这七种方块堆叠成各种不同的模型。
1.1 游戏的历史
俄罗斯方块是一款风靡全球的电视游戏机和掌上游戏机游戏,它曾经造成的轰动与造成的经济价值可以说是游戏史上的一件大事。这款游戏最初是由苏联的游戏制作人Alex Pajitnov制作的,它看似简单但却变化无穷,令人上瘾。相信大多数用户都还记得为它痴迷得茶不思饭不想的那个俄罗斯方块时代。
    究其历史,俄罗斯方块最早还是出现在PC机上,而我国的用户都是通过红白机了解、喜欢上它的。现在联众又将重新掀起这股让人沉迷的俄罗斯方块风潮。对一般用户来说,它的规则简单,容易上手,且游戏过程变化无穷,而在"联众俄罗斯方块"中,更有一些联众网络游戏所独有的魅力――有单机作战与两人在线对战两种模式,用户可任选一种进行游戏。网络模式还增加了积分制,使用户既能感受到游戏中的乐趣,也给用户提供了一个展现自己高超技艺的场所 [1]。
 
图 1.1  俄罗斯方块
1.2 Windows编程特点
(1)图形用户界面
Windows为用户提供了独立于应用程序的图形设备接口,利用这个接口,程序员可以在应用程序中显示文本和图形;所有的硬件设备都由Windows的设备驱动程序来管理。
(2)多任务
在传统的MS-DOS环境中,每次只能够执行一个任务,只有从一个任务中退出才能执行下一任务,这样在客观上就浪费了很多的资源。但是在Windows操作环境中,多个应用程序可以同时运行,每个应用程序在屏幕上都有一个显示的窗口,即为Windows的多任务环境。
(3)资源共享
在windows操作系统中,应用程序之间共享资源的方式共有三种:剪贴板、DDE和OLE。
1.3 Visual C++ 6.0编程特点
Visual C++6.0是微软公司推出的开发Win32应用程序(Windows 95/98/2000/XP/NT)的、面向对象的可视化集成工具。它的最大优点就是提供了功能强大的MFC类库,MFC是一个很大的C++类层次结构,其中封装了大量的类及其函数,很多Windows程序所共有的标准内容可以由MFC的类来提供,MFC类为这些内容提供了用户接口的标准实现方法,程序员所要做的就是通过预定义的接口把具体应用程序特有的东西填入这个轮廓,这将简化编程工作,大大的减少程序员编写的代码数量,使编程工作变得更加轻松容易。
1.4  课程设计目的
综合运用所学理论知识和技能,设计开发俄罗斯方块,使自己熟悉应用系统的开发过程,培养独立思考能力,检验学习效果和动手能力,提高工程实践能力,为将来实际工作打下坚实的基础。
2  需求分析
2.1 游戏需求
    随机给出不同的形状(长条形、Z字形、反Z形、田字形、7字形、反7形、T字型)下落填充给定的区域,若填满一条便消掉,记分,当达到一定的分数时,过关,设置六关,每关方块下落的速度不同,若在游戏中各形状填满了给定区域,为输者。游戏功能要求如下:
游戏界面需求:良好的用户界面,有关数显示和分数显示。让方块在一定的区域内运动和变形,该区域用一种颜色表明,既用一种颜色作为背景,最好设为黑色。还需用另一种颜色把黑色围起来,宽度适中,要实现美感。
游戏形状(方块)需求:良好的方块形状设计,绘制七种常见的基本图形(长条形、Z字形、反Z形、田字形、7字形、反7形、T字型),各个方块要能实现它的变形,可设为顺时针或逆时针变形,一般为逆时针。
键盘处理事件:方块下落时,可通过键盘方向键(上、下、左、右键)对该方块进行向上(变形),向下(加速)、向左、向右移动。
显示需求:当不同的方块填满一行时可以消行,剩余方块向下移动并统计分数。当达到一定的分数时过关。设置六关,每关方块下落的速度不同。
2.2 软硬件需求
操作系统 Windows98/me/2000/XP/2003
内存容量 64MB
显卡要求 8M
声卡要求 支持DirectX 8.0 音效卡
交互工具 键盘/鼠标
CPU 奔腾133以上
光驱 8倍速以上
硬盘空间 400MB
显示器 VGA以上显示器
开发软件 Java可视化编程3.0以上版本

2.3 接口控制
本软件需求通过键盘进行操作,在Windows的操作系统下,利用键盘的上、下、左、右键对方块进行移动变形,要使用键盘的接口事件。
3  概要设计
3.1 定义方块的数据结构
对于方块在某一瞬间的位置标识,我们采用一个4×2的小数组标识出来,即用4个存储单位空间存储当前下坠物的每一子块的位置,也就是说,用4个存储单位空间存储当前下坠物的每一子块的位置来对整个下坠物件的位置进行标识,而每个存储空间的大小就是一个典的坐标值(x,y),而每个方块按照从左到右的方式进行编号,并且在编号过程中对于同一列的方块实行从上到下进行编号 [2] 。

 图3.1 方块编号
ActiveStatus[0][0]和ActiveStatus[0][1]则是第0号方块的横坐标x和纵坐标y ;ActiveStatus[2][0]和ActiveStatus[2][1]则是第2号方块的横坐标x和纵坐标y。
3.2 游戏设计分析
有前面的功能描述可知,我先虚拟出俄罗斯方块游戏的类对象,并抽象出核心的数据属性和操作方法等,然后再作细化,最后将整个虚拟类的外壳脱掉,再移植到视图类中去,其实现如下:
CRectGameView : public CView
{
 //内部存取数据结构
 int m_stateMap[MAX_ROW][MAX_COL];
 //初始化操作
 GameInitnal();      //游戏的初始化
 
 //用于判断数据相关状态的操作
 IsLeftLimit();       //下坠物件是否可向左移动
 IsRightLitmit();     //
 IsBottom();          //是否已经到达了底部
 IsGameEnd();         //是否游戏已经结束
 //方块物件下坠过程中的操作
 RectChange();        //下坠物件变形
 RectDown();          //下坠物件正常下落
 RectArrow();         //下坠物件方向移动(左,右,下加速)
  //状态控制操作
 GameStart();         //游戏开始
 GamePause();         //游戏暂停
 GameEnd();           //游戏结束
}
通过上面的代码可以看出,在虚拟类中抽象出了核心的内部数据和一些基本的操作函数。对于操作函数,可以把它们分为内部实现的基本核心操作(如判断操作)以及明显提供给外部使用的整体模块外部操作(如状态控制操作)。而内部的基本操作又可以分为判断操作和执行操作这样两种类型[3]。

图3.1 正常流程的设计
 
图3.2 中断流程的设计
4  详细设计
4.1 总体设计
序号 方法名 作用
1 Main() 定义窗口
2 Rectview() 创建变量
3 Gamestart() 游戏开始
4 DrawGame() 游戏界面初始化
5 RectDown() 当前方块下降
6 invalidatecurrent () 刷新指定区域
7 OnTimer () 承担所有驱动
8 GamePaush () 游戏暂停
9 OnDraw () 绘制屏幕
10 IsBottom() 处理到达后的图形,进行加分,满行判断及消行处理等
11 KeyDown() 控制按键信息
12 Rectchange() 控制图形变化
13 GameEnd () 游戏结束
表4.1 涉及函数
图4.1  程序运行调用图
4.2 创建界面的主框架
  首先建立一个项目工程,名为skyblue_Rect,并在AppWizard的架构选择过程中选择单文档方式,其他保持默认选项。其项目的架构类视图信息如图所示:
   在构架类视图中是MFC基本架构组合:App(应用程序)类、Document(文档)类、View(视图)类、Frame(框架)类和用于提示关于作者的对话框CAboutDlg类,至于COptionDlg类是用作俄罗斯方块参数选择的对话框类对象
图 4.2  主框架图                             
4.3 正常流程的设计                                         
 (1)定时制机制
 从分析游戏的特性可以知道,定时器的产生与生效应该在游戏开始的时候,而在游戏暂停或者游戏结束时则将已经设定的定时器失效/销亡(对于暂停的情况,使它销亡,当游戏从暂停状态又进入游戏状态时候,则重新创建一个定时器并激活它的运作),所以分别在游戏的开始函数、暂停函数已经结束函数中实现定时器的激活与去激活工作。这里,先在资源编辑器菜单资源里面添加三个菜单选项,分别是游戏的“开始”、“暂停”、和“结束”,然后利用ClassWizard直接在视图类对象Cskyblue_RectView中为它们添加空白的处理函数,具体如表2所示。
表 4.3  菜单选项功能对应表
菜单选项名称 快捷键 资源ID 响应处理函数
开始游戏 &S ID_GAME_START OnGameStart()
暂停游戏 &P ID_GAME_PAISH OnGamePaush()
结束游戏 &E ID_GAME_END OnGameEnd()

(2)定时处理
 经过定时器的设置后,这里通过利用ClassWizard跳到定时器到时候的处理函数OnTimer()去实现,当固定时间片间隔到达后,先检测当前下坠物是否已经到达了底部,不是则进行RectDown()下坠物向下移动一个单位的操作,是则到底后产生一个新的“下一个下坠物”,并代替旧的,将原先旧的“下一个下坠物”用作当前激活状态下正在使用的下坠物,并对使用后的一些状态进行检测:是否马上到达底部,使则进行销行操作
;是否在到达底部的同时到达游戏区域的顶部,从而判定游戏是否因违规而结束。
 
图 4.3 装载方块
 视图类创建了m_icurrentStatus和m_inextStatus两个成员变量来记录下坠物的类型,共有七种形状,并从7种方块中随机抽取图形。而m_currentRect除了记录下坠物的类型外,还需记录其当前的变形状态,最多用两位表示,第1位用作类型标识(1~7),第2位用作同种类型的不同表现方式,最多有4种状态(1~4)。
在产生新的下一个下坠物前,需要先将当前状态物的记录和旧的下一个下坠物保存下来,然后用随机函数Random()产生一个最大值不大于指定值的随机正整数,将这个新生成的正整数用作新的“下一个下坠物”的形状值。
(3)底部到达的判断与销行的实现
 
图4.4 处理方块到达图
将新的下坠物放置到游戏区域中去,这时可能出现马上到达底部的情况,因此需要对它进行判断,如果是到达底部,则进行销行处理,并且修改相应的数据状态。而判断是否已经到达了底部,可以通过当前下坠物件所对应的接触面的方块位置为被占用状态(MAP_STATE_NOT_EMPTY=1)来确定,利用数组InterFace[74][4]记录1~7种下坠物的1~4种形态的接触面信息。
统计分数:在消行处理里面有一个专门用来统计消行数的变量,然后根据变量的值决定分数的多少,程序统计分数是:消一行得100分,同时消2行得400分,销掉x行,则分数为:x*(x*100)。如果总分数达到过关条件就过关,改变游戏速度,游戏初始化,开启新的一关,然后再加载方块。没有达到过关分数或者没有满行,则加载下一个方块继续游戏。

 
图 4.5 判断满行及销行图
检测游戏区域中的所有行,,并对每行的所有纵列状态进行检测,如果其中有一列是空闲状态则不可以销行。如果可以销行的话,将增加单位分数,并且将该行清空,再将该行上面的所有物件都向下偏移一个单位,以填充该行的空缺。

 

例销行与积分
 if (m_isBottom)
 {
  //判断是否已得分
  for (i=0;i<m_iRow;i++)
  {
   m_bIsSucced = TRUE;
   for (j=0;j<m_iCol;j++)
    if (GameStatus[i][j]==MAP_STATE_EMPTY)
     m_bIsSucced = FALSE;
   
   //如果得分,则销掉此行
   if (m_bIsSucced)
   {
    for (k=i;k>0;k--)
     for (j=0;j<m_iCol;j++)
      GameStatus[k][j] = GameStatus[k-1][j];
    //第1行清零
    for (j=0;j<m_iCol;j++)
     GameStatus[0][j]=MAP_STATE_EMPTY;

    m_iMuch += 1;
   }
  }
 
图 4.6 显示控制
在视图类对象中增加了成员变量m_iStartX和m_iStartY,分别为视图窗口的左上角起点的坐标偏移值。如果检测到有销行操作的话,经过一系列内部核心数据的修改后,应该将销行后的区域以及分数显示区域的部分进行重绘。所以函数体分别先计算出这两个小矩形区域的位置,然后再调用InvalidateRect()函数对这两个区域宣布重绘,从而触发视图的重绘。
4.4 中断操作流程的实现
(1) 处理键盘事件
关于按键命令消息的响应,可以通过对WM_KEYDOWN消息的处理函数进行截获并重写来实现,下面是对该处理函数OnKeyDown()的重写。
// 功能:处理用户的输入,方块的左,右移,加速及变形
void CSkyblue_RectView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
 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;
 }
 CView::OnKeyDown(nChar, nRepCnt, nFlags);
}
 
图 4.7 键盘事件功能图
当方块要进行上(变形)、下、左、右变化时,先对方块将要移动到的位置进行判断,判断是否有障碍,有则保持原来的位置不变,没有则根据所按的键执行相关的操作。再把方块的位置显示在屏幕上。方块的移动其实也只是对行或列坐标的增减而己。
(2) 变形的实现
 
图4.8 方块变化
当按下向上键时,将会执行方块变化事件(change())。常见的方块有7种(长条形、Z字形、反Z形、田字形、7字形、反7形、T字型),所有图形都是用两个一维数组来统计它的横坐标和纵坐标,每个方块有4种不同的变化形状。
例计算变形后的小方块的坐标和显示的状态值
//变形后位置在数组中的存放顺序仍需遵循先左后右,在同一列中先上后下
 xx1=x1; xx2=x2; xx3=x3; xx4=x4; yy1=y1; yy2=y2; yy3=y3; yy4=y4;
 switch(m_currentRect)
 {
 case 1:
  xx1=x1+1; yy1=y1-1; xx3=x3-1; yy3=y3+1; xx4=x4-2; yy4=y4+2;
  m_lscurrentRect = 11;
  break;
 case 11:
  xx1=x1-1; yy1=y1+1; xx3=x3+1; yy3=y3-1; xx4=x4+2; yy4=y4-2;
  m_lscurrentRect = 1;
  break;
     ……
//省略部分为同类实现的变形后小方块坐标的计算代码
case 73:
  xx2=x2+1; yy2=y2-1; xx3=x3+2; yy3=y3-2; xx4=x4-1; yy4=y4-1;
  m_lscurrentRect = 7;
  break;
 }
4.5 游戏区域绘图的实现
首先将外部位图文件rect.bmp中的位图动态导入(映射)到内存位图里面,根据游戏区域中的二维数组GameStatus[MAX_ROW][MAX_COL]中的内部数据将所有数据状态中为被占用状态MAP_STATE_NOT_EMPTY的小方块区域用指定的小方块图样类型来填充,然后将已经绘制好的游戏区域图像一次性的拷贝到与屏幕关联的设备环境中,从而达到屏幕的显示。
4.6 功能的完善
为了使得游戏功能更加健全,另外为用户提供了一些附加功能,如表3所示。
表 4.6    附加功能描述列表
功能名称 选项 其他描述
游戏等级选择 初级......顶级 对游戏快慢难度的设定
方块图案样式选择 深蓝......条纹 对下坠物的小方块图案样式选择
游戏区域大小选择 小、中、大 对游戏区域的行列数选择
网格的选用 有、无 是否需要在游戏区域绘制网格
背景音乐的选用 有、无 在游戏过程中是否播放背景音乐
先将这些目标功能通过资源编辑器在主菜单条进行添加,将前面已有的菜单选项补全,再通过ClassWizard添加对应的响应处理函数。其最终效果如图
1
图 4.9 游戏设置
游戏是用来给大家娱乐的,所以要能在使用的过程中给大家带来快乐,消除大家的疲劳,所以我在游戏中添加了漂亮的场景和动听的背景音乐,设置了个性化的工具栏快捷键,激发大家的娱乐激情。
4.7 游戏演示
游戏主界面如图所示。
 
图4.10  俄罗斯方块游戏运行主界面

结束语
   在本次课程设计中,我从指导老师颜宏文身上学到了很多东西。老师认真负责的工作态度,严谨的治学精神和深厚的理论水平都使我收益匪浅。她无论在理论上还是在实践中,都给与我很大的帮助,使我得到不少的提高这对于我以后的工作和学习都有一种巨大的帮助,感她耐心的辅导。另外,在游戏开发过程中谢中科老师也给于我们很大的帮助,帮助解决了不少的难点,使得游戏能及时开发完成,还有所有的同学同样给与我不少帮助,这里一并表示感谢。
参考文献
[1]Ben Sawyer. 游戏软件设计与开发指南[M].北京:人民邮电出版社,1998.8~46
[2]钦科技. Visual C++游戏设计[M]. 北京:科海电子出版社,2003.1~211
[3]坂本千寻.Visual C++专业游戏程序设计[M].  北京:中国铁道出版社,2004

附录1:主程序源程序清单

CSkyblue_RectView::CSkyblue_RectView()
{
 //第一次开始游戏
 m_bFistPlay = TRUE;
 //缺省为不是游戏暂停状态
 m_bGamePaush = FALSE;
 //缺省为不插放背景音乐
 m_bMusic = FALSE;
 //缺省为画网格线
 m_bDrawGrid = TRUE;
 //总分值清零
 m_iPerformance = 0;
 //测试值:为12行,10列
 m_iRow = 12;
 m_iCol = 10;
 //左上角X,Y坐标
 m_iStartX = 10;
 m_iStartY = 10;
 //缺省级别为3级
 m_iLevel = 2;
 //第一种样式
 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;
 
 InterFace[32][0] = 0; InterFace[32][1] = 2; InterFace[32][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; InterFace[41][2] = 3;
/*
5  --
    --
*/ 
 InterFace[5][0] = 1; InterFace[5][1] = 3;

 InterFace[51][0] = 0; InterFace[51][1] = 2; InterFace[51][2] = 3;
/*
6  --
    -
 -
*/ 
 InterFace[6][0] = 0; InterFace[6][1] = 3;
 
 InterFace[61][0] = 1; InterFace[61][1] = 2; InterFace[61][2] = 3;

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

 InterFace[63][0] = 0; InterFace[63][1] = 1; InterFace[63][2] = 3;
/*
7  --
   -
   -
*/
 InterFace[7][0] = 2; InterFace[7][1] = 3; 
 InterFace[71][0] = 1; InterFace[71][1] = 2; InterFace[71][2] = 3;

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

 InterFace[73][0] = 0; InterFace[73][1] = 1; InterFace[73][2] = 3;
}
// 功能:承担所有驱动工作
void CSkyblue_RectView::OnTimer(UINT nIDEvent)
{
 //如果原来的方块已到底或游戏刚开始,则掉下一个新的方块
 int i,j,k;
 if (m_isBottom)
 {
  //1.产生下一个随机下坠物
  m_icurrentStatus = m_inextStatus;
  m_inextStatus = Random(7);  //得到下一次的方块样式
 // if (m_inextStatus==0) m_inextStatus++;
  //2.修改新的“下一下坠物”
  RectStatusToNextStatus( m_inextStatus );
 // CRect rect(m_iStartY+320, m_iStartX, m_iStartY+440, m_iStartX+160);
 // InvalidateRect(&rect);
 // Invalidate(FALSE); 
  //3.将旧的“下一下坠物”用作当前使用
  m_currentRect = m_icurrentStatus;  
  //根据当前下坠物的形状去初始化激活状态下的下坠物坐标
  RectStatusToActiveStatus( m_icurrentStatus );
  //将当前动态数组中的数据反映到大数组中
  ActiveStatusToGameStatus();
  m_isBottom = FALSE;
  //4.判断当前方块是否已到底
  IsBottom();
     //5.判断游戏是否已结束: 碰了底,且第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();
 }
 CView::OnTimer(nIDEvent);
}
// 函数:产生一个最大值不大于指定值的随机正整数(Random)
// 参数:MaxNumber : 随机数的上限
// 返回值: 产生的随机数
int CSkyblue_RectView::Random(int MaxNumber)
{
 //布下随机种子
 srand( (unsigned)time( NULL ) );
 //产生随机数
 int random = rand() % MaxNumber;
 //保证非0
 if(random == 0 ) random++;
 return random;
}
//内部函数:初始掉落时,将根据方块的样式决定下一次将要掉下来的动态数组的值
void CSkyblue_RectView::RectStatusToNextStatus(int m_which)
{
 switch(m_which)
 {
 case 1:
  NextStatus[0][0] = 0; NextStatus[0][1] = 1; NextStatus[1][0] = 1; NextStatus[1][1] = 1;
  NextStatus[2][0] = 2; NextStatus[2][1] = 1; NextStatus[3][0] = 3; NextStatus[3][1] = 1;
  break;
 case 2:
  NextStatus[0][0] = 0; NextStatus[0][1] = 1; NextStatus[1][0] = 1; NextStatus[1][1] = 1;
  NextStatus[2][0] = 0; NextStatus[2][1] = 2; NextStatus[3][0] = 1; NextStatus[3][1] = 2;
  break;
 case 3:
  NextStatus[0][0] = 1; NextStatus[0][1] = 0; NextStatus[1][0] = 0; NextStatus[1][1] = 1;
  NextStatus[2][0] = 1; NextStatus[2][1] = 1; NextStatus[3][0] = 1; NextStatus[3][1] = 2;
  break;
 case 4:
  NextStatus[0][0] = 0; NextStatus[0][1] = 1; NextStatus[1][0] = 1; NextStatus[1][1] = 1;
  NextStatus[2][0] = 1; NextStatus[2][1] = 2; NextStatus[3][0] = 2; NextStatus[3][1] = 2;
  break;
 case 5:
  NextStatus[0][0] = 1; NextStatus[0][1] = 1; NextStatus[1][0] = 2; NextStatus[1][1] = 1;
  NextStatus[2][0] = 0; NextStatus[2][1] = 2; NextStatus[3][0] = 1; NextStatus[3][1] = 2;
  break;
 case 6:
  NextStatus[0][0] = 0; NextStatus[0][1] = 1; NextStatus[1][0] = 0; NextStatus[1][1] = 2;
  NextStatus[2][0] = 1; NextStatus[2][1] = 2; NextStatus[3][0] = 2; NextStatus[3][1] = 2;
  break;
 case 7:
  NextStatus[0][0] = 0; NextStatus[0][1] = 1; NextStatus[1][0] = 1; NextStatus[1][1] = 1;
  NextStatus[2][0] = 2; NextStatus[2][1] = 1; NextStatus[3][0] = 0; NextStatus[3][1] = 2;
  break;
 }
}
void CSkyblue_RectView::RectStatusToActiveStatus(int m_which)
{
 switch(m_which)
 {
 case 1:  
  ActiveStatus[0][0] = 0; ActiveStatus[0][1] = 5; ActiveStatus[1][0] = 1; ActiveStatus[1][1] = 5;
  ActiveStatus[2][0] = 2; ActiveStatus[2][1] = 5; ActiveStatus[3][0] = 3; ActiveStatus[3][1] = 5;
  break;
……
}
}
// 内部函数:将当前下坠物的位置映射到游戏区域地图数组中去
void CSkyblue_RectView::ActiveStatusToGameStatus()
{
 int x1,x2,x3,x4,y1,y2,y3,y4;
 x1 = ActiveStatus[0][0];
 x2 = ActiveStatus[1][0];
 x3 = ActiveStatus[2][0];
 x4 = ActiveStatus[3][0];
 y1 = ActiveStatus[0][1];
 y2 = ActiveStatus[1][1];
 y3 = ActiveStatus[2][1];
 y4 = ActiveStatus[3][1];
 GameStatus[x1][y1]=MAP_STATE_NOT_EMPTY;
 GameStatus[x2][y2]=MAP_STATE_NOT_EMPTY;
 GameStatus[x3][y3]=MAP_STATE_NOT_EMPTY;
 GameStatus[x4][y4]=MAP_STATE_NOT_EMPTY;
}
// 内部函数:判断当前方块是否已到底,并且销行等相关的工作
void CSkyblue_RectView::IsBottom()
{
 //到底有两种概念:1是已到底部,2是下面碰到了另外的方块
 int x1,x2,x3,x4;
 int x,xx,yy,i;

 x1 = ActiveStatus[0][0];
 x2 = ActiveStatus[1][0];
 x3 = ActiveStatus[2][0];
 x4 = ActiveStatus[3][0];
 //是否为底部的判断
 //1。到达游戏区域的底部
 //2。与接触面正下方的小方块区域为被占用状态
 if (x1>=m_iRow-1 || x2>=m_iRow-1 || x3>=m_iRow-1 || x4>=m_iRow-1)
  m_isBottom = TRUE;
 else
 {
  for (i=0;i<4;i++)
  {
   if (InterFace[m_currentRect][i] > -1)
   {//取当前下坠物有接触面的方块

    //获取有接触面的小方块的编号
    x=InterFace[m_currentRect][i];
    //根据编号获取ActiveStatus中该小方块的整下方的坐标
    xx=ActiveStatus[x][0]+1;
    yy=ActiveStatus[x][1];
    //判断该接触面整下方的小方块区域是否为被占用状态
    if (GameStatus[xx][yy]==MAP_STATE_NOT_EMPTY)
     m_isBottom = TRUE;
   }
  }
 }
 BOOL m_bIsSucced;
 int k,j;
 int m_iMuch=0; //本次销掉的行数
 //计分规则:一次销掉一行,加100分,一次销掉两行,加400分,三行,900分
 //例如销掉x行,则分数为:x*(x*100)
 if (m_isBottom)
 {
  //判断是否已得分
  for (i=0;i<m_iRow;i++)
  {
   m_bIsSucced = TRUE;
   for (j=0;j<m_iCol;j++)
    if (GameStatus[i][j]==MAP_STATE_EMPTY)
     m_bIsSucced = FALSE;
   //如果得分,则销掉此行
   if (m_bIsSucced)
   {
    for (k=i;k>0;k--)
     for (j=0;j<m_iCol;j++)
      GameStatus[k][j] = GameStatus[k-1][j];
    //第1行清零
    for (j=0;j<m_iCol;j++)
     GameStatus[0][j]=MAP_STATE_EMPTY;

    m_iMuch += 1;
   }
  }
  if (m_iMuch>0)
  {
   m_iPerformance += m_iMuch * m_iMuch * 100;
   //刷新游戏区域
   CRect rect1(m_iStartY, m_iStartX, m_iStartY+300, m_iStartX+360);
   //InvalidateRect(&rect1);
   //刷新分数区域
   CRect rect2(m_iStartY+320, m_iStartX+180, m_iStartY+440, m_iStartX+200);
   //InvalidateRect(&rect2);
   Invalidate(FALSE);
  }
 }
}
// 内部函数:当前方块下降
void CSkyblue_RectView::RectDown()
{
 IsBottom();
 if (!m_isBottom)
 {
  //清除以前的方块
  int x1,x2,x3,x4,y1,y2,y3,y4;
  x1 = ActiveStatus[0][0];
  x2 = ActiveStatus[1][0];
  x3 = ActiveStatus[2][0];
  x4 = ActiveStatus[3][0];
  y1 = ActiveStatus[0][1];
  y2 = ActiveStatus[1][1];
  y3 = ActiveStatus[2][1];
  y4 = ActiveStatus[3][1];
  GameStatus[x1][y1]=MAP_STATE_EMPTY;
  GameStatus[x2][y2]=MAP_STATE_EMPTY;
  GameStatus[x3][y3]=MAP_STATE_EMPTY;
  GameStatus[x4][y4]=MAP_STATE_EMPTY;
  InvalidateCurrent();
  //方块下落
  ActiveStatus[0][0] += 1;
  ActiveStatus[1][0] += 1;
  ActiveStatus[2][0] += 1;
  ActiveStatus[3][0] += 1;
  GameStatus[x1+1][y1]=MAP_STATE_NOT_EMPTY;
  GameStatus[x2+1][y2]=MAP_STATE_NOT_EMPTY;
  GameStatus[x3+1][y3]=MAP_STATE_NOT_EMPTY;
  GameStatus[x4+1][y4]=MAP_STATE_NOT_EMPTY;
  InvalidateCurrent();
 }
}
//内部函数:刷新当前的区域
void CSkyblue_RectView::InvalidateCurrent()
{
 int i;
 for (i=0;i<4;i++)
 {
 CRect rect(m_iStartX+ActiveStatus[i][1]*m_iLarge,
    m_iStartY+ActiveStatus[i][0]*m_iLarge,
    m_iStartX+(ActiveStatus[i][1]+1)*m_iLarge+5,
    m_iStartY+(ActiveStatus[i][0]+1)*m_iLarge);
   //InvalidateRect(&rect);
   Invalidate(FALSE);
 }
}
// 功能:处理用户的输入,方块的左,右移,加速及变形
void CSkyblue_RectView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
 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;
 }

 CView::OnKeyDown(nChar, nRepCnt, nFlags);
}
// 内部函数:当前方块下降加速,左移,右移
void CSkyblue_RectView::RectArrow(int m_Type)
{
 //获取当前下坠物4个小方块的位置坐标
 int x1,x2,x3,x4,y1,y2,y3,y4;
 x1 = ActiveStatus[0][0];
 x2 = ActiveStatus[1][0];
 x3 = ActiveStatus[2][0];
 x4 = ActiveStatus[3][0];
 y1 = ActiveStatus[0][1];
 y2 = ActiveStatus[1][1];
 y3 = ActiveStatus[2][1];
 y4 = ActiveStatus[3][1];
 //对不同的移动命令指示进行分类实现
 switch(m_Type)
 {
 case LEFT:
  //对每种不同的移动命令指示特性作相应的可移动分析
  if ( (ActiveStatus[0][1]>0) && IsLeftLimit() && !m_isBottom)
  {
   //清原来的方块
   GameStatus[x1][y1]=MAP_STATE_EMPTY;
   GameStatus[x2][y2]=MAP_STATE_EMPTY;
   GameStatus[x3][y3]=MAP_STATE_EMPTY;
   GameStatus[x4][y4]=MAP_STATE_EMPTY;
     //添加新的移动后数据状态
   ActiveStatus[0][1] -= 1;
   ActiveStatus[1][1] -= 1;
   ActiveStatus[2][1] -= 1;
   ActiveStatus[3][1] -= 1;
   GameStatus[x1][y1-1]=MAP_STATE_NOT_EMPTY;
   GameStatus[x2][y2-1]=MAP_STATE_NOT_EMPTY;
   GameStatus[x3][y3-1]=MAP_STATE_NOT_EMPTY;
   GameStatus[x4][y4-1]=MAP_STATE_NOT_EMPTY;
   InvalidateCurrent();
  }
  break;
 case RIGHT:
  if ( (ActiveStatus[3][1]< m_iCol-1) && IsRightLitmit() && !m_isBottom)
  {
   //清原来的方块
   GameStatus[x1][y1]=MAP_STATE_EMPTY;
   GameStatus[x2][y2]=MAP_STATE_EMPTY;
   GameStatus[x3][y3]=MAP_STATE_EMPTY;
   GameStatus[x4][y4]=MAP_STATE_EMPTY;
     //添加新的移动后数据状态
   ActiveStatus[0][1] += 1;
   ActiveStatus[1][1] += 1;
   ActiveStatus[2][1] += 1;
   ActiveStatus[3][1] += 1;
   GameStatus[x1][y1+1]=MAP_STATE_NOT_EMPTY;
   GameStatus[x2][y2+1]=MAP_STATE_NOT_EMPTY;
   GameStatus[x3][y3+1]=MAP_STATE_NOT_EMPTY;
   GameStatus[x4][y4+1]=MAP_STATE_NOT_EMPTY; 
   InvalidateCurrent();
  }
  break;
 case DOWN:
  RectDown();
  break;
 }
}
// 内部函数:方块是否还可以左移
BOOL CSkyblue_RectView::IsLeftLimit()
{
 int x1,x2,x3,x4,y1,y2,y3,y4;
 x1 = ActiveStatus[0][0];
 x2 = ActiveStatus[1][0];
 x3 = ActiveStatus[2][0];
 x4 = ActiveStatus[3][0];
 y1 = ActiveStatus[0][1];
 y2 = ActiveStatus[1][1];
 y3 = ActiveStatus[2][1];
 y4 = ActiveStatus[3][1];
 //根据当前下坠物的具体形态,分析判断其是否有向左移动的空间
 switch(m_currentRect)
 {
  /*
  |
     |
        |   "1"字形形态类型,判断其四个方块的正左边都没有任何物件(空间没有被占据)
     |
   */
 case 1:
  if (GameStatus[x1][y1-1] || GameStatus[x2][y2-1] || GameStatus[x3][y3-1] || GameStatus[x4][y4-1])
   return FALSE;
  break;
 case 11:
  if (GameStatus[x1][y1-1])
   return FALSE;
  break;
 case 2:
  if (GameStatus[x1][y1-1] || GameStatus[x2][y2-1])
   return FALSE;
  break;
 case 3:
  if (GameStatus[x1][y1-1] || GameStatus[x2][y2-1])
   return FALSE;
  break;
 case 31:
  if (GameStatus[x1][y1-1] || GameStatus[x2][y2-1] || GameStatus[x3][y3-1])
   return FALSE;
  break;
 case 32:
  if (GameStatus[x1][y1-1] || GameStatus[x3][y3-1])
   return FALSE;
  break;
 case 33:
  if (GameStatus[x1][y1-1] || GameStatus[x2][y2-1] || GameStatus[x4][y4-1])
   return FALSE;
  break;
 case 4:
  if (GameStatus[x1][y1-1] || GameStatus[x2][y2-1] || GameStatus[x4][y4-1])
   return FALSE;
  break;
 case 41:
  if (GameStatus[x1][y1-1] || GameStatus[x2][y2-1])
   return FALSE;
  break;
 case 5:
  if (GameStatus[x1][y1-1] || GameStatus[x2][y2-1] || GameStatus[x3][y3-1])
   return FALSE;
  break;
 case 51:
  if (GameStatus[x1][y1-1] || GameStatus[x3][y3-1])
   return FALSE;
  break;
 case 6:
  if (GameStatus[x1][y1-1] || GameStatus[x3][y3-1] || GameStatus[x4][y4-1])
   return FALSE;
  break;
 case 61:
  if (GameStatus[x1][y1-1] || GameStatus[x2][y2-1])
   return FALSE;
  break;
 case 62:
  if (GameStatus[x1][y1-1] || GameStatus[x2][y2-1] || GameStatus[x3][y3-1])
   return FALSE;
  break;
 case 63:
  if (GameStatus[x1][y1-1] || GameStatus[x3][y3-1])
   return FALSE;
  break;
 case 7:
  if (GameStatus[x1][y1-1] || GameStatus[x2][y2-1] || GameStatus[x3][y3-1])
   return FALSE;
  break;
 case 71:
  if (GameStatus[x1][y1-1] || GameStatus[x2][y2-1])
   return FALSE;
  break;
 case 72:
  if (GameStatus[x1][y1-1] || GameStatus[x2][y2-1] || GameStatus[x3][y3-1])
   return FALSE;
  break;
 case 73:
  if (GameStatus[x1][y1-1] || GameStatus[x4][y4-1])
   return FALSE;
  break;
 }
 return TRUE;   
}
// 内部函数:方块的变形
void CSkyblue_RectView::RectChange()
{
 //先预先变形,然后判断变形后的方块是否有空间,如有足够空间,则进行实际变形,否则不变
 int xx1,xx2,xx3,xx4,yy1,yy2,yy3,yy4;
 int m_lscurrentRect;
 CString lsStr; 
 int x1,x2,x3,x4,y1,y2,y3,y4;
 x1 = ActiveStatus[0][0];
 x2 = ActiveStatus[1][0];
 x3 = ActiveStatus[2][0];
 x4 = ActiveStatus[3][0];
 y1 = ActiveStatus[0][1];
 y2 = ActiveStatus[1][1];
 y3 = ActiveStatus[2][1];
 y4 = ActiveStatus[3][1];
 //变形后位置在数组中的存放顺序仍需遵循先左后右,在同一列中先上后下
 xx1=x1; xx2=x2; xx3=x3; xx4=x4; yy1=y1; yy2=y2; yy3=y3; yy4=y4;
 switch(m_currentRect)
 {
 case 1:
  xx1=x1+1; yy1=y1-1; xx3=x3-1; yy3=y3+1; xx4=x4-2; yy4=y4+2;
  m_lscurrentRect = 11;
  break;
 case 11:
  xx1=x1-1; yy1=y1+1; xx3=x3+1; yy3=y3-1; xx4=x4+2; yy4=y4-2;
  m_lscurrentRect = 1;
  break;
……
//省略部分为同类实现的变形后小方块坐标计算代码
 case 73:
  xx2=x2+1; yy2=y2-1; xx3=x3+2; yy3=y3-2; xx4=x4-1; yy4=y4-1;
  m_lscurrentRect = 7;
  break;
 }
//如果变形后所在的区域内无其他方块,则表示有足够空间,可以变形
 //且不能超越边界
 GameStatus[x1][y1] = MAP_STATE_EMPTY;
 GameStatus[x2][y2] = MAP_STATE_EMPTY;
 GameStatus[x3][y3] = MAP_STATE_EMPTY;
 GameStatus[x4][y4] = MAP_STATE_EMPTY;
 if (GameStatus[xx1][yy1]==MAP_STATE_EMPTY &&
  GameStatus[xx2][yy2]==MAP_STATE_EMPTY &&
  GameStatus[xx3][yy3]==MAP_STATE_EMPTY &&
  GameStatus[xx4][yy4]==MAP_STATE_EMPTY
  && yy1>=0 && yy4<=m_iCol-1
  && !(xx1<0 || xx2<0 || xx3<0 || xx4<0)
  && !(xx1>m_iRow-1 || xx2>m_iRow-1 || xx3>m_iRow-1 || xx4>m_iRow-1) )
 {
  InvalidateCurrent();

  ActiveStatus[0][0]=xx1;
  ActiveStatus[1][0]=xx2;
  ActiveStatus[2][0]=xx3;
  ActiveStatus[3][0]=xx4;
  ActiveStatus[0][1]=yy1;
  ActiveStatus[1][1]=yy2;
  ActiveStatus[2][1]=yy3;
  ActiveStatus[3][1]=yy4;
  GameStatus[xx1][yy1] = MAP_STATE_NOT_EMPTY;
  GameStatus[xx2][yy2] = MAP_STATE_NOT_EMPTY;
  GameStatus[xx3][yy3] = MAP_STATE_NOT_EMPTY;
  GameStatus[xx4][yy4] = MAP_STATE_NOT_EMPTY;
  InvalidateCurrent();
  //改变形状代码
  m_currentRect = m_lscurrentRect;
 }
 else
 {//恢复原来状态
  GameStatus[x1][y1] = MAP_STATE_NOT_EMPTY;
  GameStatus[x2][y2] = MAP_STATE_NOT_EMPTY;
  GameStatus[x3][y3] = MAP_STATE_NOT_EMPTY;
  GameStatus[x4][y4] = MAP_STATE_NOT_EMPTY;
 }
 //判断是否已到底
 IsBottom();
}
//绘图设备环境的初始化
void CSkyblue_RectView::DcEnvInitial(void)
{
 if(m_bFistPlay)
 {
  m_bFistPlay = FALSE;
  //用默认的参数,获取当前屏幕设备环境
  CDC *pWindowDC = GetDC();
  //1.用于映射屏幕的内存设备环境
  //获取游戏窗口的大小用于下面设置内存位图的尺寸
  CRect windowRect;
  GetClientRect(&windowRect);
  m_nWidth = windowRect.Width();
  m_nHeight = windowRect.Height();
  //内存设备环境与屏幕设备环境关联(兼容)
  m_memDC.CreateCompatibleDC(pWindowDC);
  //内存位图与与屏幕关联(兼容),大小为游戏窗口的尺寸
  m_memBmp.CreateCompatibleBitmap(pWindowDC,m_nWidth,m_nHeight);
  //内存设备环境与内存位图关联,以便通过m_memDC在内存位图上作画
  m_memDC.SelectObject(&m_memBmp);
  //2.存储方块位图的内存资源
  //内存设备环境与屏幕设备环境关联(兼容)
  m_memRectDC.CreateCompatibleDC(pWindowDC);
  //相当于将外部位图rect.bmp动态载入m_hMemRectBmp中  m_hMemRectBmp=(HBITMAP)LoadImage(NULL,"rect.bmp",IMAGE_BITMAP,150,30,LR_LOADFROMFILE);
  //内存设备环境与内存位图关联,以便通过m_memDC在内存位图上作画
  SelectObject(m_memRectDC.m_hDC, m_hMemRectBmp);  
 //黑色的黑笔
 m_pBlackPen  = new CPen(PS_SOLID,1,BLACK);
 //画刷
 m_pGrayBrush  = new CBrush(RGB(66,66,66));
 m_pBlackBrush  = new CBrush(BLACK);
 }
}
void CSkyblue_RectView::DCEnvClear(void)
{
 //设备环境
 m_memDC.DeleteDC();
 m_memRectDC.DeleteDC();
 //位图资源
 DeleteObject(m_memBmp);
 DeleteObject(m_hMemRectBmp);
 delete(m_pBlackPen);
 delete(m_pGrayBrush);
 delete(m_pBlackBrush);
}
void CSkyblue_RectView::DrawGame(CDC *pDC)
{
  int i,j;
 //选用黑色画刷,绘制整个游戏所在窗口的背景
 pDC -> SelectObject(m_pBlackBrush);
 CRect rect;
 GetClientRect(&rect);
 pDC -> Rectangle(rect);
 //选用灰色画刷,绘制游戏区域的背景
 pDC -> SelectObject(m_pGrayBrush);
 pDC -> Rectangle(m_iStartY ,m_iStartX, m_iStartY + 301, m_iStartX + 360); 
 pDC->SelectObject(m_pBlackPen); 
 //画网格线
 if (m_bDrawGrid)
 {
  //画横线
  for (i=0;i<m_iRow;i++)
  {
   pDC->MoveTo(m_iStartY, m_iStartX + i*m_iLarge);
   pDC->LineTo(m_iStartY+300, m_iStartX +i*m_iLarge);
  } 
  //画竖线
  for (i=0;i<m_iCol;i++)
  {
   pDC->MoveTo(m_iStartY+i*m_iLarge, m_iStartX);
   pDC->LineTo(m_iStartY+i*m_iLarge, m_iStartX+360);
  }
 }
 int x,y,nW,nH;
 //小方块的绘制
 for (i=0;i<m_iRow;i++)
  for (j=0;j<m_iCol;j++)
  {
   if (GameStatus[i][j]==MAP_STATE_NOT_EMPTY)
   {
    //在游戏区域中状态为被占用状态的区域绘制小方块
    x = m_iStartY+j*m_iLarge +2;
    y = m_iStartX+i*m_iLarge +2;
    nW = m_iLarge-4;
    nH = m_iLarge-4;
   pDC->BitBlt(x,y,nW,nH,&m_memRectDC,m_iBlockSytle*30,0,SRCCOPY);
   }
  }
 //显示游戏区域及游戏级别的汉字描述
 if (!m_bGameEnd)
 {
  pDC -> SetBkColor(BLACK);
  pDC -> SetTextColor(WHITE);
  pDC -> TextOut(m_iStartY+320, m_iStartX+220, "游戏区域大小:");
  pDC -> TextOut(m_iStartY+320, m_iStartX+240,m_strArea);

  pDC -> TextOut(m_iStartY+320, m_iStartX+280, "游戏级别:");
  pDC -> TextOut(m_iStartY+320, m_iStartX+300,m_strLevel);
 }    
 //显示总分
 if (!m_bGameEnd)
 {
  CString lsStr; 
  lsStr.Format("总分为:%d 分",m_iPerformance);
  pDC -> SetBkColor(BLACK);
  pDC -> SetTextColor(WHITE);
  pDC -> TextOut(m_iStartY+320, m_iStartX+180,lsStr);
 }
 //画下一次将要出现的方块,用于提示用户
 if (!m_bGameEnd)
 {
  pDC -> SetBkColor(BLACK);
  pDC -> SetTextColor(WHITE);
  pDC -> TextOut(m_iStartY+320, m_iStartX,"下一个方块:");
  int x,y,nW,nH;  
  for (UINT k=0;k<4;k++)
  {
   i = NextStatus[k][0];
      j = NextStatus[k][1];
    x = m_iStartY+j*30 +2+320;
    y = m_iStartX+i*30 +2+30;
    nW = m_iLarge-4;
    nH = m_iLarge-4;
  pDC->BitBlt(x,y,nW,nH,&m_memRectDC,m_iBlockSytle*30,0,SRCCOPY);
  }
 }
}
// 功能:承担所有绘制屏幕工作
void CSkyblue_RectView::OnDraw(CDC* pDC)
{
 DcEnvInitial();
 DrawGame(&m_memDC);  //在内存位图的游戏区域绘制
 pDC->BitBlt(0,0,m_nWidth,m_nHeight,&m_memDC,0,0,SRCCOPY);
}

 

(责任编辑:毕业设计论文网)
------分隔线----------------------------
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
验证码:点击我更换图片