发布时间:2025-12-09 19:56:16 浏览次数:3
源代码下载地址:http://download.csdn.net/detail/qq_29187355/9855657
1 题目要求
设计一个双人俄罗斯方块游戏
2 功能需求
(1) 实现双人俄罗斯方块
(2) 实现下一个砖块预测功能
(3) 隐藏工具栏、状态栏
(4) 实现难度可以选择
(5) 添加游戏说明菜单 添加作者菜单 添加网址超链接
(6) 实现砖块三维化
(7) 实现总分统计功能,和每步消除所得分数显示
(8) 实现背景音乐播放功能
(9) 暂停功能
(10) 增加了新的方块类
(11) 设置窗口大小,禁用最大化按钮,禁止鼠标拖动改变窗口大小。
3 总体设计
1.矩形框类的设计:
设计CBin类描述Tetris游戏的矩形框,用image的二维数组来描述这个矩形框。设置不同的值显示不同颜色的矩形,若没有砖块则为0。
2.砖块的设计:
设计CBrick抽象类来设计砖块,应用多态性的原理,其他不同类型的砖块类继承CBrick,来设计不同的砖块。
3.砖块在面板中的显示设计:
在视图类中设计并显示砖块。
4 详细设计
CBin类:
| 函数名称 | 函数说明 |
| CBin(unsigned int w, unsigned int h) | 构造函数,用来初始化数据成员 width 和 height ,并为image 分配空间并初始化。 |
| ~CBin() | 析构函数,删除在构造函数中为 image分配的空间。 |
| void getImage(unsigned char** destImage) | 将image 的数据拷贝到 destImage. 你可以假设destImage 指向的空间足够容纳image |
| void setImage(unsigned char** srcImage) | 把srcImage 中的数据拷贝到image. 你可以假设srcImage 是一个合法的指针 |
| unsigned int removeFullLines() | 检查image ,如果任何一行完全填 满,则删除这一行,并让上面行的 数据下移一行,返回删除的总行数。 |
CBrick类
设计CBrick抽象类来设计砖块。
CBrick类的成员函数:
virtual int shiftLeft(CBin*bin)=0; //将砖块在游戏的矩形框内左移一位
virtual int shiftRight(CBin* bin)=0; //将砖块在游戏的矩形框内右移一位
virtual int shiftDown(CBin* bin)=0; //将砖块在游戏的矩形框内下移
virtual int rotateClockwise(CBin* bin)=0; //将砖块在游戏的矩形框内顺时针旋转
virtual int checkCollision(CBin* bin)=0; //检查砖块是否冲突
virtual void operator>>(unsigned char**binImage)=0; //重载运算符>>,通过设置映射到游戏矩形的二维数组binImage,设置砖块的颜色,这里假设binimage是一个合适的二维数组。
virtual void putAtTop(int newOrient, int newPosX)=0; //置顶
应用多态性的原理,其他不同类型的砖块类继承CBrick,来设计不同的砖块
共设计了七种不同类型的砖块。
可视化设计:
在视图类中设计并显示砖块。
(1).定义相关的变量并在构造函数中初始化。
部分代码如下:
public:CNewTetrisDoc* GetDocument();COLORREF GetLightColor(COLORREF m_crBody);COLORREF GetDarkColor(COLORREF m_crBody);面板 1///CBin*bin; //定义游戏矩形框指针CBrick*activeBrick; //定义指向当前下落砖块的指针int gameOver; //判断游戏是否结束int brickInFlight; //判断砖块是否处于下落状态int brickType; //砖块类别unsigned int initOrientation; //初始状态int notCollide; //冲突否unsigned int numLines; //消的行数unsigned char**outputImage;int difficulty; //定义难度void DrawImage(CBin*bin,CBin *bin2,unsigned char** image,unsigned char** image2,unsigned char**imageY1,unsigned char **imageY2,CDC *pDC);(初始化省略)(2).添加DrawImage(CBin *bin,CBin *bin2,unsignedchar** image,unsigned char** image2,unsigned char **imageY1,unsigned char**imageY2,CDC *pDC)函数,用来绘制游戏砖块。
部分代码如下:
unsigned intwidth,i,j; unsigned int height; width=bin->getWidth();height=bin->getHeight();int nSize = 20; //砖块大小CRect rect;CBitmap bitmap;bitmap.LoadBitmap(IDB_BITMAP3); //加载位图CBrush brush; brush.CreatePatternBrush(&bitmap); GetClientRect(&rect);pDC->FillRect(rect,&brush); /* pDC->FillSolidRect(rect,RGB(255,255,255)); //绘制背景色*//* pDC->Rectangle(100,0,300,400); */char buf[100];sprintf(buf,"玩家一:分数:%d",numLines*10);pDC->TextOut(300,20,buf);/* pDC->Rectangle(450,0,650,400); */char buf2[100];sprintf(buf2,"玩家二:分数:%d",numLines2*10);pDC->TextOut(850,20,buf2);CRect rc,rc2; CRect rcY1;CRect rcY2; //定义矩形区COLORREFBrickColor[8]={0xFFFFFF,0xFF0000,0x00FF00,0x0000FF,0x00FFFF,0xFFFF00,0x800000,0x800080};/游戏界面显示for (i=0; iFillRect(rc,&CBrush(BrickColor[image[i][j]])); //画临时砖块(运动中)pDC->Draw3dRect(rc,GetLightColor(BrickColor[image[i][j]]),GetDarkColor(BrickColor[image[i][j]]));}(3)在菜单中添加
ID_Game_Start 开始(&S)
难度(&D)属性选择pop-up,子菜单为:
ID_DIFF_EASY 容易
ID_DIFF_MID 中等
ID_DIFF_SUP 高级
暂停(&P)属性选择pop-up,子菜单为:
IDD_Pause 暂停
IDD_Restart 开始
为其分别添加消息响应函数:
void CNewTetrisView::OnGameStart(){//TODO: Add your command handler code heregameOver=0; brickInFlight=0; numLines=0;for(unsigned int i = 0; i<20; i++){for(unsigned int j = 0; j<10; j++){outputImage[i][j]=0;}}bin->setImage(outputImage);SetTimer(0,difficulty,NULL); //设置定时器}void CNewTetrisView::OnDiffEasy(){//TODO: Add your command handler code heredifficulty=500;OnGameStart();}void CNewTetrisView::OnDiffMid(){//TODO: Add your command handler code heredifficulty=350;OnGameStart();}void CNewTetrisView::OnDiffSup(){//TODO: Add your command handler code heredifficulty=150;OnGameStart();}暂停功能的实现:
按下暂停键的时候停止定时器,按下重新开始键开启定时器就行了。
voidCNewTetrisView::OnPause(){// TODO: Add your commandhandler code hereKillTimer(0);}voidCNewTetrisView::OnRestart(){// TODO: Add your commandhandler code hereSetTimer(0,difficulty,NULL); }(4)为视图类添加WM_TIMER的消息响应函数。初始化面板,并设置定时器。
(这里省略代码,最终版代码在下面)
(5)在OnDraw()函数中调用DrawImage函数,显示面板。
(这里省略代码,最终版代码在下面)
(6)添加WM_KEYDOWON的消息响应函数,以响应用户按键。
void CNewTetrisView::OnKeyDown(UINT nChar, UINTnRepCnt, UINT nFlags){//控制面板1if(nChar == VK_RIGHT) activeBrick->shiftRight(bin);elseif (nChar == VK_LEFT) activeBrick->shiftLeft(bin);elseif (nChar == VK_UP) activeBrick->rotateClockwise(bin);elseif (nChar == VK_DOWN) activeBrick->shiftDown(bin);Invalidate();CView::OnKeyDown(nChar,nRepCnt, nFlags);}这样一个基本的单人版俄罗斯方块游戏就做好了
进一步完善和改进:
双人版功能的实现:
就是把前面的代码重新写一遍再显示在另一个区域,用另一个定时器。
(1).定义相关的变量并在构造函数中初始化。
(2)在DrawImage函数中绘制游戏砖块,显示在另一个区域。
部分代码如下:
//2if(0 != imageY2[i][j]){pDC->FillRect(rcY2,&CBrush(BrickColor[imageY2[i][j]]));//画临时砖块(运动中)pDC->Draw3dRect(rcY2,GetLightColor(BrickColor[imageY2[i][j]]),GetDarkColor(BrickColor[imageY2[i][j]]));}(3)在OnDraw()函数中调用DrawImage函数,显示面板。代码省略。
( 4) 修改WM_TIMER的消息响应函数。初始化面板,并设置另一个定时器。
(5)修改ID_Game_Start的消息响应函数,初始化面板,启动定时器。这里省略代码
(6)修改WM_KEYDOWON的消息响应函数,以响应用户按键。
部分代码如下:
//控制面板1if (nChar == VK_RIGHT) activeBrick->shiftRight(bin);else if (nChar == VK_LEFT) activeBrick->shiftLeft(bin);else if (nChar == VK_UP) activeBrick->rotateClockwise(bin); else if (nChar == VK_DOWN) activeBrick->shiftDown(bin);Invalidate();(7)修改暂停和开始的消息响应函数,这里省略代码。
预显示功能的实现:
在生成砖块的时候,一次生成两块,一块显示,一块下落。
(1)定义flag用于标记是不是第一次下落,并初始化。
(2)在DrawImage函数中绘制游戏砖块,显示在相应的区域。
部分代码如下:
//预显示for (i = 0; i < 4; ++i)//一行一行的画砖块{for (j = 2; j < 8; ++j){rcY1 = CRect((j-2)*nSize+380, i*nSize+250, (j+1-2)*nSize+380, (i+1)*nSize+250);rcY2 = CRect((j-2)*nSize+530, i*nSize+250, (j+1-2)*nSize+530, (i+1)*nSize+250);//绘制面板//1if (0 != imageY1[i][j]){pDC->FillRect(rcY1, &CBrush(BrickColor[imageY1[i][j]]));//画临时砖块(运动中)pDC->Draw3dRect(rcY1,GetLightColor(BrickColor[imageY1[i][j]]),GetDarkColor(BrickColor[imageY1[i][j]]));}//2if (0 != imageY2[i][j]){pDC->FillRect(rcY2, &CBrush(BrickColor[imageY2[i][j]]));//画临时砖块(运动中)pDC->Draw3dRect(rcY2,GetLightColor(BrickColor[imageY2[i][j]]),GetDarkColor(BrickColor[imageY2[i][j]]));}}}(3)在OnDraw()函数中调用DrawImage函数,显示面板。
代码如下:
int m_nWidth,m_nHeight; CDC m_memDC; CBitmap m_memBmp;/*m_memBmp.LoadBitmap(IDB_BITMAP2); //装载位图*///1.用于映射屏幕的内存设备环境//获取游戏窗口的大小用于下面设置内存位图的尺寸CRect windowRect;GetClientRect(&windowRect); m_nWidth = windowRect.Width(); m_nHeight = windowRect.Height();//内存设备环境与屏幕设备环境关联(兼容)m_memDC.CreateCompatibleDC(pDC);//内存位图与与屏幕关联(兼容),大小为游戏窗口的尺寸/*m_memBmp.CreateCompatibleBitmap(pDC,m_nWidth,m_nHeight); */m_memDC.FillSolidRect(windowRect,RGB(0,0,0));//内存设备环境与内存位图关联,以便通过m_memDC 在内存位图上作画m_memDC.SelectObject(&m_memBmp); DrawImage(bin,bin2,outputImage,outputImage2,outputImageY1,outputImageY2,pDC);//把内存DC 上的图形拷贝到电脑屏幕pDC->BitBlt(0,0,m_nWidth,m_nHeight,&m_memDC,0,0,SRCCOPY);m_memDC.DeleteDC(); //删除DCm_memBmp.DeleteObject(); //删除位图界面的美化:
修改标题:在doc类中OnNewDocument()函数修改
BOOL CNewTetrisDoc::OnNewDocument(){if (!CDocument::OnNewDocument())return FALSE;// TODO: add reinitialization code here// (SDI documents will reuse this document)SetTitle(_T("俄罗斯方块"));return TRUE;}设置标题栏图标: 首先在资源视图引入ICON文件
然后在CMainFrame中的OnCreate()函数中添加如下代码:
//设置标题栏的图标 HICON m_hIcon = AfxGetApp()->LoadIcon(IDI_ICON1); SetIcon(m_hIcon,TRUE); SetIcon(m_hIcon,FALSE); return 0;工具栏、菜单栏、状态栏的隐藏,最大化按钮的禁用,窗口大小的设定:
在CMainFrame中的OnCreate()函数中添加如下代码:
m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);EnableDocking(CBRS_ALIGN_ANY);DockControlBar(&m_wndToolBar);/*SetMenu(NULL);//菜单栏的隐藏*/ShowControlBar(&m_wndToolBar,FALSE,FALSE);//工具栏的隐藏ShowControlBar(&m_wndStatusBar,FALSE,FALSE);//状态栏的隐藏在CMainFrame中的PreCreateWindow函数中添加如下代码:
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs){if( !CFrameWnd::PreCreateWindow(cs) )return FALSE;// TODO: Modify the Window class or styles here by modifying// the CREATESTRUCT cscs.cx = 1024;//设定窗口的大小 (窗口大小可以用QQ截图获得)cs.cy = 571;cs.style &= ~WS_THICKFRAME;//使窗口不能用鼠标改变大小cs.style &= ~WS_MAXIMIZEBOX; //禁止窗口最大化return TRUE;}添加背景音乐功能:
首先先把你要添加的音乐用格式工厂转化为WAV格式,然后放入res文件夹。将VC98文件夹下的WINMM.LIB文件放入res文件夹。
然后添加lib文件 选择 Project->Add to Project->Files ,然后找到WINMM,添加进去。因为后面要用到PlaySound函数。
然后在资源中插入 WAVE 命名为 IDR_WAVE3 。
然后在CNewTetrisView类实现文件中引入mmsystem.h
然后在Gamestart函数中添加
PlaySound((LPCTSTR)IDR_WAVE3,AfxGetInstanceHandle(),SND_RESOURCE |SND_ASYNC |SND_LOOP);
帮助对话框的添加:
首先插入对话框资源,然后为它对应一个类 HelpDialog.h
在视图类实现文件中添加#include "HelpDialog.h"
为帮助 菜单添加消息响应函数:
HelpDialog dlg;
dlg.DoModal();
超文本链接的添加:
(1) 在资源中插入光标文件 ID设为IDC_HAND
(2) 在Dialog中加入静态文本控件,并更改其ID为IDC_LINK。
(3) 在对话框头文件中加入数据成员:
protected:
RECT m_pRectLink; //用于保存静态文本框的屏幕坐标
(4) 在对话类成员函数OnInitDialog()中添加以下代码
GetDlgItem(IDC_LINK)->GetWindowRect(&m_pRectLink);//将静态文本的屏幕坐标存放在m—pRectLink中
ScreenToClient(&m_pRectLink);//将屏幕坐标转换为客户坐标
(5)变换鼠标形状
为对话框添加OnMouseMove函数 添加如下代码:
//下面设置鼠标在静态文本区时,将光标设成小手状if (point.x>m_pRectLink.left&&point.xm_pRectLink.top&&point.yLoadCursor(IDC_HAND); //将鼠标设为小手状SetCursor(hCursor);}(6) 添加鼠标单击响应事件, 为Dialog类添加OnLButtonDown函数 添加如下代码:
void HelpDialog::OnLButtonDown(UINT nFlags, CPoint point) {// TODO: Add your message handler code here and/or call defaultif (point.x>m_pRectLink.left&&point.xm_pRectLink.top&&point.y5 测试与实现