VC版双人PK版俄罗斯方块

源代码下载地址: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用于标记是不是第一次下落,并初始化。

2DrawImage函数中绘制游戏砖块,显示在相应的区域。

部分代码如下:

//预显示
	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;
}

背景位图的插入:

首先在资源视图导入文件(bmp格式)

然后在DrawImage函数中添加如下代码:

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 测试与实现

VC版双人PK版俄罗斯方块_第1张图片


你可能感兴趣的:(MFC)