在之前一篇《簡單數字拼板游戲學習》基礎上修改,地址:http://www.cnblogs.com/fwst/p/3706483.html
開發環境:Windows 7/ Visual Studio 2010 / MFC對話框用 / 字符集:使用多字節字符集
運行效果:
(4 X 4)
(7 X 7)
(1)已完成 2048 游戲基本功能,需要解決的幾個關鍵問題是
a. 首先是數據結構。先定義矩形類,然后定義矩形類對象的二維數組,長度由宏定義,可修改,即可自定義成N*N的游戲。這樣游戲就是由N*N個矩形對象組成。
b. 然后是游戲邏輯處理,也是最重要的一部分。方向鍵的響應。鍵盤上下左右四個方向鍵的邏輯一樣,代碼部分只是稍微修改一下。這部分邏輯有點糾結,應該有多種方法,這里介紹我的處理,有不同方法歡迎分享。以左鍵為例,這部分邏輯對每一行的處理步驟如下:
I. 清空空格, 並將所有數字按次序移到最左邊。每個矩形有一個值,當值為0時,不顯示,這些矩形就是空格。如一開始是0 2 0 2,那么經這一步處理后就應該是
2 2 0 0;
II. 從左邊開始,依次將與右邊相鄰值相等的矩形的值加倍,並將該相鄰值置為0。如 2 2 0 0,處理后應該是 4 0 0 0; 再如 2 2 2 2 處理后應該是 4 0 4 0 ;再如 4 2 2 8處理后是 4 4 0 8;
III. 再做一次第一步。這一步是為了處理做完第二步后新出現的空格。比如2 2 2 2做完第二步是4 0 4 0, 再經過這一步后就變成最終的4 4 0 0。
c. 生成新數字。每做完一個動作后需要生成一個新數字,本來原來游戲中新生成的為2或者4, 我這里就直接全都用2了,相對於原來游戲算是降低了難度,要生成4也很簡單,加個概率隨機生成就行。新生成數字的位置用一個循環,當生成的位置的值不為0 的時候就再次重新隨機生成,直到隨機到的位置的值為0。
另外,生成之前必須要加一個判斷,就是如果最近的按鍵沒有引起游戲面盤上的變化,則不能生成新數字。
d. 游戲結束的判斷。 用一個全局函數,游戲結束的條件是游戲面盤上有空格,或者沒有空格且任意兩個相鄰的數字的值都不相同,這里的相鄰是指行和列兩個方向上的相鄰。這里用三個循環,第一個循環判斷是否有空格,如果有空格,那么游戲肯定沒有結束,函數直接返回false。第二個循環是從行的方向上,依次判斷相鄰的兩個值。同理第三個循環是從列的方向上判斷。游戲結束判斷為true后用messagebox彈出對話框。
(2) 還沒有做的事
a. 可以用圓角矩形。
b. 界面色彩太花,傷眼,可以弄成原作那樣數字從小到大,顏色由淺到深。
c. 沒有記分功能。
d. 沒做重新開始。沒做回一步。
e. 界面框框大小不固定,可以拖動。。。(在對話框的屬性里將Border由Resizing改為Dialog Frame就可以了)
f. 這里沒有加2048就勝利的判斷,相當於Endless模式。
今天修改的效果圖:
將每個矩形的大小縮小了一點,然后給不同的數字配上了不同的顏色,由淺到深。
最終代碼如下:
MyRect.h
#include "stdafx.h" class MyRect { public: MyRect(UINT x1, UINT y1, UINT x2, UINT y2); ~MyRect(); public: //矩形框的當前值 UINT uValue;//矩形頂點坐標 UINT x1; UINT y1; UINT x2; UINT y2; };
MyRect.cpp
#include "stdafx.h" #include "MyRect.h" MyRect::MyRect(UINT x1, UINT y1, UINT x2, UINT y2) { this->x1 = x1; this->y1 = y1; this->x2 = x2; this->y2 = y2; uValue = 0; } MyRect::~MyRect() { }
在 2048Dlg.cpp中,首先添加頭文件,
#include "MyRect.h"
然后是全局變量和函數部分, 即在頭文件和宏定義之后添加,
//大矩形為 LINELENGTH * LINELENGTH #define LINELENGTH 4 #define RECTNUM (LINELENGTH*LINELENGTH) struct MyPoint{ int x; int y; }; //實際矩形數組,面板上顯示的每個矩形都是CRect類型,聲明在這里 CRect *rect[LINELENGTH][LINELENGTH]; //控制是否生成新數字,為true的時候說明有動作,就會生成新數字 bool bHaveDoneSth; //端點位置 MyPoint point[LINELENGTH][LINELENGTH] = {0}; //矩形對象數組,相當於邏輯部分,保存矩形的顯示值,坐標 MyRect *myrect[LINELENGTH][LINELENGTH]; //填充畫刷,可以控制矩形填充不同的顏色 CBrush *brush; //生成一個新數字,隨機一個0-RECTNUM的整數,根據這個整數計算出二維數組的橫坐標和豎坐標 // A/LINELENGTH 是橫坐標, A%LINELENGTH 是豎坐標, 當生成的位置有值的時候,重新生成 // 初始值為2, 可以再這里加控制生成2,或 4 。 void GenerateNewNum() { srand(time(0)); int A = rand() % RECTNUM; while (myrect[A/LINELENGTH][A%LINELENGTH]->uValue != 0) { A = rand() % RECTNUM; } myrect[A/LINELENGTH][A%LINELENGTH]->uValue = 2; } //判斷游戲結束 bool GameOver() { //如果有值為0 的矩形,則游戲肯定可以繼續,所以直接返回false for (int i = 0; i < LINELENGTH; i++) for (int j = 0; j < LINELENGTH; j++) { if ( myrect[i][j]->uValue == 0 ) return false; } // 對每一行相鄰的兩個數,如果有相同的,那么游戲可以繼續,返回false for (int i = 0; i < LINELENGTH; i++) for (int j = 0; j < LINELENGTH-1; j++) { if ( myrect[i][j]->uValue == myrect[i][j+1]->uValue ) return false; } // 對每一列相鄰的兩個數,如果有相同的,那么游戲可以繼續,返回false for (int j = 0; j < LINELENGTH; j++) for (int i = 0; i < LINELENGTH-1; i++) { if ( myrect[i][j]->uValue == myrect[i+1][j]->uValue ) return false; } return true; }
在 CMy2048Dlg::OnInitDialog() 中 , 添加初始化代碼,
// TODO: 在此添加額外的初始化代碼 ::SetWindowPos(this->m_hWnd, HWND_BOTTOM, 0, 0, 25+LINELENGTH*100, 48+LINELENGTH*100, SWP_NOZORDER); //初始化每個矩形的左上角點的坐標 for (int i = 0; i < LINELENGTH; i++) { for (int j = 0; j < LINELENGTH; j++) { point[i][j].x = j * 100 + 10; point[i][j].y = i * 100 + 10; } } //初始化矩形和填充畫刷 for (int i = 0; i < LINELENGTH; i++) { for (int j = 0; j < LINELENGTH; j++) { myrect[i][j] = new MyRect(point[i][j].x, point[i][j].y, point[i][j].x+90, point[i][j].y+90); myrect[i][j]->uValue = 0; } } //初始化數字 srand(time(0)); int A = rand() % RECTNUM; int B = rand() % RECTNUM; while ( B == A ) { B = rand() % RECTNUM; } myrect[ A / LINELENGTH][ A % LINELENGTH]->uValue = 2; myrect[ B / LINELENGTH][ B % LINELENGTH]->uValue = 2;
在 OnPaint()函數的最后添加繪制代碼,
CFont font; font.CreateFont(25,25,0,0,700,false,false,false, CHINESEBIG5_CHARSET,OUT_CHARACTER_PRECIS, CLIP_CHARACTER_PRECIS,DEFAULT_QUALITY, FF_MODERN,TEXT("宋體")); //客戶區設備環境 CClientDC dc(this); //新建畫筆 CPen pen; pen.CreatePen(PS_SOLID, 1, RGB(255, 0, 0)); //選中字體 dc.SelectObject(pen); for (int i = 0; i < LINELENGTH; i++) { for (int j = 0; j < LINELENGTH; j++) { //畫矩形 //dc.RoundRect(myrect[i][j]->getRect(), 4, 4); dc.Rectangle(myrect[i][j]->x1, myrect[i][j]->y1,myrect[i][j]->x2, myrect[i][j]->y2); //填充矩形 rect[i][j] = new CRect(myrect[i][j]->x1, myrect[i][j]->y1,myrect[i][j]->x2, myrect[i][j]->y2); //設置文字背景透明 dc.SetBkMode(TRANSPARENT); //選中字體 dc.SelectObject(font); //寫數字 if (myrect[i][j]->uValue == 0) { brush = new CBrush(RGB(0xFC,0xFC,0xFC)); dc.FillRect(rect[i][j], brush); delete brush; } else if (myrect[i][j]->uValue != 0) { switch(myrect[i][j]->uValue) { case 2:brush = new CBrush(RGB(0xFF,0xFF,0xFF));break; case 4:brush = new CBrush(RGB(0xFF,0xE4,0xC4));break; case 8:brush = new CBrush(RGB(0xFF,0xB6,0xC1));break; case 16:brush = new CBrush(RGB(0xFF,0x83,0xFA));break; case 32:brush = new CBrush(RGB(0xFF,0xC1,0x25));break; case 64:brush = new CBrush(RGB(0xFF,0x6A,0x6A));break; case 128:brush = new CBrush(RGB(0xFF,0x14,0x93));break; case 256:brush = new CBrush(RGB(0xCD,0x66,0x1D));break; case 512:brush = new CBrush(RGB(0x94,0x00,0xD3));break; case 1024:brush = new CBrush(RGB(0xFF,0xFF,0x00));break; case 2048:brush = new CBrush(RGB(0xFF,0x00,0x00));break; default:brush = new CBrush(RGB(0xFF,0x00,0x00));break; } dc.FillRect(rect[i][j], brush); delete brush; char num[10] = {'0'}; itoa(myrect[i][j]->uValue, num, 10); dc.DrawText(num, -1, rect[i][j], DT_VCENTER|DT_CENTER|DT_SINGLELINE); } } }
然后就是在類向導里添加鍵盤響應函數,OnKeyUp, 在里面添加以下代碼:
// TODO: 在此添加消息處理程序代碼和/或調用默認值 switch(nChar) { case VK_LEFT: //判斷是否有動作,用來控制是否生成新數字 bHaveDoneSth = false; for (int i = 0; i < LINELENGTH; i++) { //去掉空格 for (int j = 0; j < LINELENGTH ; j++) { // if ( myrect[i][j]->uValue != 0 ) { for (int k = 0; k < j; k++) { if (myrect[i][k]->uValue == 0) { bHaveDoneSth = true; myrect[i][k]->uValue = myrect[i][j]->uValue; myrect[i][j]->uValue = 0; break; } } } } //相加 for (int j = 0; j < LINELENGTH-1 ; j++) { if ( myrect[i][j]->uValue != 0 ) { if ( myrect[i][j+1]->uValue == myrect[i][j]->uValue ) { bHaveDoneSth = true; myrect[i][j]->uValue += myrect[i][j+1]->uValue; myrect[i][j+1]->uValue = 0; } } } //去掉空格 for (int j = 0; j < LINELENGTH ; j++) { if ( myrect[i][j]->uValue != 0 ) { for (int k = 0; k < j; k++) { if (myrect[i][k]->uValue == 0) { bHaveDoneSth = true; myrect[i][k]->uValue = myrect[i][j]->uValue; myrect[i][j]->uValue = 0; break; } } } } } break; case VK_UP: bHaveDoneSth = false; for (int j = 0; j < LINELENGTH; j++) { //去掉空格 for (int i = 0; i < LINELENGTH ; i++) { // if ( myrect[i][j]->uValue != 0 ) { for (int k = 0; k < i; k++) { if (myrect[k][j]->uValue == 0) { bHaveDoneSth = true; myrect[k][j]->uValue = myrect[i][j]->uValue; myrect[i][j]->uValue = 0; break; } } } } //相加 for (int i = 0; i < LINELENGTH-1 ; i++) { if ( myrect[i][j]->uValue != 0 ) { if ( myrect[i+1][j]->uValue == myrect[i][j]->uValue ) { bHaveDoneSth = true; myrect[i][j]->uValue += myrect[i+1][j]->uValue; myrect[i+1][j]->uValue = 0; } } } //去掉空格 for (int i = 0; i < LINELENGTH ; i++) { // if ( myrect[i][j]->uValue != 0 ) { for (int k = 0; k < i; k++) { if (myrect[k][j]->uValue == 0) { bHaveDoneSth = true; myrect[k][j]->uValue = myrect[i][j]->uValue; myrect[i][j]->uValue = 0; break; } } } } } break; case VK_RIGHT: bHaveDoneSth = false; for (int i = 0; i < LINELENGTH; i++) { //去掉空格 for (int j = LINELENGTH - 1; j >= 0 ; j--) { // if ( myrect[i][j]->uValue != 0 ) { for (int k = LINELENGTH - 1; k >= j; k--) { if (myrect[i][k]->uValue == 0) { bHaveDoneSth = true; myrect[i][k]->uValue = myrect[i][j]->uValue; myrect[i][j]->uValue = 0; break; } } } } //相加 for (int j = LINELENGTH - 1; j > 0 ; j--) { if ( myrect[i][j]->uValue != 0 ) { if ( myrect[i][j-1]->uValue == myrect[i][j]->uValue ) { bHaveDoneSth = true; myrect[i][j]->uValue += myrect[i][j-1]->uValue; myrect[i][j-1]->uValue = 0; } } } //去掉空格 for (int j = LINELENGTH - 1; j >= 0 ; j--) { // if ( myrect[i][j]->uValue != 0 ) { for (int k = LINELENGTH - 1; k >= j; k--) { if (myrect[i][k]->uValue == 0) { bHaveDoneSth = true; myrect[i][k]->uValue = myrect[i][j]->uValue; myrect[i][j]->uValue = 0; break; } } } } } break; case VK_DOWN: bHaveDoneSth = false; for (int j = LINELENGTH - 1; j >= 0; j--) { //去掉空格 for (int i = LINELENGTH -1 ; i >= 0; i--) { // if ( myrect[i][j]->uValue != 0 ) { for (int k = LINELENGTH - 1; k >= i; k--) { if (myrect[k][j]->uValue == 0) { bHaveDoneSth = true; myrect[k][j]->uValue = myrect[i][j]->uValue; myrect[i][j]->uValue = 0; break; } } } } //相加 for (int i = LINELENGTH - 1; i > 0 ; i--) { if ( myrect[i][j]->uValue != 0 ) { if ( myrect[i-1][j]->uValue == myrect[i][j]->uValue ) { bHaveDoneSth = true; myrect[i][j]->uValue += myrect[i-1][j]->uValue; myrect[i-1][j]->uValue = 0; } } } //去掉空格 for (int i = LINELENGTH -1 ; i >= 0; i--) { // if ( myrect[i][j]->uValue != 0 ) { for (int k = LINELENGTH-1; k >= i; k--) { if (myrect[k][j]->uValue == 0) { bHaveDoneSth = true; myrect[k][j]->uValue = myrect[i][j]->uValue; myrect[i][j]->uValue = 0; break; } } } } } break; default: break; } if (bHaveDoneSth) { GenerateNewNum(); } Invalidate(FALSE); if ( GameOver()) { AfxMessageBox("游戲結束!"); };
(完)