基于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("游戏结束!");
        };

 (完)

原文地址:https://www.cnblogs.com/fwst/p/3951912.html