源代码下载地址: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 ,如果任何一行完全填 满,则删除这一行,并让上面行的 数据下移一行,返回删除的总行数。 |
#include"stdafx.h"
#include "bin.h" //" "从自己编写的头文件中找,<>从系统自带头文件中找.
////////////CBin////////////////////
CBin::CBin(unsigned int w, unsigned int h)
{
width=w;
height=h;
image = new unsigned char* [height];
for (unsigned int i = 0; i0; m--)
{
for (j=0; j
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 here
gameOver=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 here
difficulty=500;
OnGameStart();
}
void CNewTetrisView::OnDiffMid()
{
//TODO: Add your command handler code here
difficulty=350;
OnGameStart();
}
void CNewTetrisView::OnDiffSup()
{
//TODO: Add your command handler code here
difficulty=150;
OnGameStart();
}
暂停功能的实现:
按下暂停键的时候停止定时器,按下重新开始键开启定时器就行了。
voidCNewTetrisView::OnPause()
{
// TODO: Add your commandhandler code here
KillTimer(0);
}
voidCNewTetrisView::OnRestart()
{
// TODO: Add your commandhandler code here
SetTimer(0,difficulty,NULL);
}
(4)为视图类添加WM_TIMER的消息响应函数。初始化面板,并设置定时器。
(这里省略代码,最终版代码在下面)
(5)在OnDraw()函数中调用DrawImage函数,显示面板。
(这里省略代码,最终版代码在下面)
(6)添加WM_KEYDOWON的消息响应函数,以响应用户按键。
void CNewTetrisView::OnKeyDown(UINT nChar, UINTnRepCnt, UINT nFlags)
{
//控制面板1
if(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函数中绘制游戏砖块,显示在另一个区域。
部分代码如下:
//2
if(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的消息响应函数,以响应用户按键。
部分代码如下:
//控制面板1
if (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);
//绘制面板
//1
if (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]]));
}
//2
if (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(); //删除DC
m_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 cs
cs.cx = 1024;//设定窗口的大小 (窗口大小可以用QQ截图获得)
cs.cy = 571;
cs.style &= ~WS_THICKFRAME;//使窗口不能用鼠标改变大小
cs.style &= ~WS_MAXIMIZEBOX; //禁止窗口最大化
return TRUE;
}
CRect rect;
CBitmap bitmap;
bitmap.LoadBitmap(IDB_BITMAP3); //加载位图
CBrush brush;
brush.CreatePatternBrush(&bitmap); //创建位图画刷
GetClientRect(&rect); //获得客户区大小
pDC->FillRect(rect,&brush); //将矩形区域用位图填充
添加背景音乐功能:
首先先把你要添加的音乐用格式工厂转化为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 default
if (point.x>m_pRectLink.left&&point.xm_pRectLink.top&&point.y
5 测试与实现