VC版雙人PK版俄羅斯方塊
1 題目要求
設計一個雙人俄羅斯方塊遊戲
2 功能需求
(1) 實現雙人俄羅斯方塊
(2) 實現下一個磚塊預測功能
(3) 隱藏工具欄、狀態列
(4) 實現難度可以選擇
(5) 新增遊戲說明選單 新增作者選單 新增網址超連結
(6) 實現磚塊三維化
(7) 實現總分統計功能,和每步消除所得分數顯示
(8) 實現背景音樂播放功能
(9) 暫停功能
(10) 增加了新的方塊類
(11) 設定視窗大小,禁用最大化按鈕,禁止滑鼠拖動改變視窗大小。
3 總體設計
1.矩形框類的設計:
設計CBin類描述Tetris遊戲的矩形框,用image的二維陣列來描述這個矩形框。設定不同的值顯示不同顏色的矩形,若沒有磚塊則為
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;
}
背景點陣圖的插入:
首先在資源檢視匯入檔案(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 測試與實現