基於MFC對話框的2048游戲


在之前一篇《簡單數字拼板游戲學習》基礎上修改,地址: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("游戲結束!");
        };

 

 (完)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM