zoukankan      html  css  js  c++  java
  • 基于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("游戏结束!");
            };

     (完)

  • 相关阅读:
    mkdir命令
    pwd命令
    chmod命令
    chown命令
    chgrp命令
    687. Longest Univalue Path
    HYSBZ 1036 树的统计Count (水题树链剖分)
    POJ 3709 K-Anonymous Sequence (斜率优化DP)
    LightOJ 1065 Island of Survival (概率DP?)
    LightOJ 1248 Dice (III) (水题,期望DP)
  • 原文地址:https://www.cnblogs.com/fwst/p/3951912.html
Copyright © 2011-2022 走看看