1. 程式人生 > >VC版雙人PK版俄羅斯方塊

VC版雙人PK版俄羅斯方塊

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 測試與實現