基于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 = ;
} 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] = {}; //矩形对象数组,相当于逻辑部分,保存矩形的显示值,坐标
MyRect *myrect[LINELENGTH][LINELENGTH]; //填充画刷,可以控制矩形填充不同的颜色
CBrush *brush; //生成一个新数字,随机一个0-RECTNUM的整数,根据这个整数计算出二维数组的横坐标和竖坐标
// A/LINELENGTH 是横坐标, A%LINELENGTH 是竖坐标, 当生成的位置有值的时候,重新生成
// 初始值为2, 可以再这里加控制生成2,或 4 。
void GenerateNewNum()
{
srand(time());
int A = rand() % RECTNUM;
while (myrect[A/LINELENGTH][A%LINELENGTH]->uValue != )
{
A = rand() % RECTNUM;
}
myrect[A/LINELENGTH][A%LINELENGTH]->uValue = ;
} //判断游戏结束
bool GameOver()
{
//如果有值为0 的矩形,则游戏肯定可以继续,所以直接返回false
for (int i = ; i < LINELENGTH; i++)
for (int j = ; j < LINELENGTH; j++)
{
if ( myrect[i][j]->uValue == )
return false;
}
// 对每一行相邻的两个数,如果有相同的,那么游戏可以继续,返回false
for (int i = ; i < LINELENGTH; i++)
for (int j = ; j < LINELENGTH-; j++)
{
if ( myrect[i][j]->uValue == myrect[i][j+]->uValue )
return false;
} // 对每一列相邻的两个数,如果有相同的,那么游戏可以继续,返回false
for (int j = ; j < LINELENGTH; j++)
for (int i = ; i < LINELENGTH-; i++)
{
if ( myrect[i][j]->uValue == myrect[i+][j]->uValue )
return false;
}
return true;
}
在 CMy2048Dlg::OnInitDialog() 中 , 添加初始化代码,
// TODO: 在此添加额外的初始化代码
::SetWindowPos(this->m_hWnd, HWND_BOTTOM, , , +LINELENGTH*, +LINELENGTH*, SWP_NOZORDER); //初始化每个矩形的左上角点的坐标
for (int i = ; i < LINELENGTH; i++)
{
for (int j = ; j < LINELENGTH; j++)
{
point[i][j].x = j * + ;
point[i][j].y = i * + ;
}
}
//初始化矩形和填充画刷
for (int i = ; i < LINELENGTH; i++)
{
for (int j = ; j < LINELENGTH; j++)
{
myrect[i][j] = new MyRect(point[i][j].x, point[i][j].y, point[i][j].x+, point[i][j].y+);
myrect[i][j]->uValue = ;
}
} //初始化数字
srand(time());
int A = rand() % RECTNUM;
int B = rand() % RECTNUM;
while ( B == A )
{
B = rand() % RECTNUM;
}
myrect[ A / LINELENGTH][ A % LINELENGTH]->uValue = ;
myrect[ B / LINELENGTH][ B % LINELENGTH]->uValue = ;
在 OnPaint()函数的最后添加绘制代码,
CFont font;
font.CreateFont(,,,,,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, , RGB(, , ));
//选中字体
dc.SelectObject(pen); for (int i = ; i < LINELENGTH; i++)
{
for (int j = ; 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 == )
{
brush = new CBrush(RGB(0xFC,0xFC,0xFC));
dc.FillRect(rect[i][j], brush);
delete brush;
}
else if (myrect[i][j]->uValue != )
{
switch(myrect[i][j]->uValue)
{
case :brush = new CBrush(RGB(0xFF,0xFF,0xFF));break;
case :brush = new CBrush(RGB(0xFF,0xE4,0xC4));break;
case :brush = new CBrush(RGB(0xFF,0xB6,0xC1));break;
case :brush = new CBrush(RGB(0xFF,0x83,0xFA));break;
case :brush = new CBrush(RGB(0xFF,0xC1,0x25));break;
case :brush = new CBrush(RGB(0xFF,0x6A,0x6A));break;
case :brush = new CBrush(RGB(0xFF,0x14,0x93));break;
case :brush = new CBrush(RGB(0xCD,0x66,0x1D));break;
case :brush = new CBrush(RGB(0x94,0x00,0xD3));break;
case :brush = new CBrush(RGB(0xFF,0xFF,0x00));break;
case :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[] = {''};
itoa(myrect[i][j]->uValue, num, );
dc.DrawText(num, -, rect[i][j], DT_VCENTER|DT_CENTER|DT_SINGLELINE);
}
}
}
然后就是在类向导里添加键盘响应函数,OnKeyUp, 在里面添加以下代码:
// TODO: 在此添加消息处理程序代码和/或调用默认值
switch(nChar)
{
case VK_LEFT:
//判断是否有动作,用来控制是否生成新数字
bHaveDoneSth = false;
for (int i = ; i < LINELENGTH; i++)
{ //去掉空格
for (int j = ; j < LINELENGTH ; j++)
{
//
if ( myrect[i][j]->uValue != )
{
for (int k = ; k < j; k++)
{
if (myrect[i][k]->uValue == )
{
bHaveDoneSth = true;
myrect[i][k]->uValue = myrect[i][j]->uValue;
myrect[i][j]->uValue = ;
break;
}
}
}
} //相加
for (int j = ; j < LINELENGTH- ; j++)
{
if ( myrect[i][j]->uValue != )
{
if ( myrect[i][j+]->uValue == myrect[i][j]->uValue )
{
bHaveDoneSth = true;
myrect[i][j]->uValue += myrect[i][j+]->uValue;
myrect[i][j+]->uValue = ;
}
}
} //去掉空格
for (int j = ; j < LINELENGTH ; j++)
{
if ( myrect[i][j]->uValue != )
{
for (int k = ; k < j; k++)
{
if (myrect[i][k]->uValue == )
{
bHaveDoneSth = true;
myrect[i][k]->uValue = myrect[i][j]->uValue;
myrect[i][j]->uValue = ;
break;
}
}
}
}
}
break;
case VK_UP:
bHaveDoneSth = false;
for (int j = ; j < LINELENGTH; j++)
{
//去掉空格
for (int i = ; i < LINELENGTH ; i++)
{
//
if ( myrect[i][j]->uValue != )
{
for (int k = ; k < i; k++)
{
if (myrect[k][j]->uValue == )
{
bHaveDoneSth = true;
myrect[k][j]->uValue = myrect[i][j]->uValue;
myrect[i][j]->uValue = ;
break;
}
}
}
} //相加
for (int i = ; i < LINELENGTH- ; i++)
{
if ( myrect[i][j]->uValue != )
{
if ( myrect[i+][j]->uValue == myrect[i][j]->uValue )
{
bHaveDoneSth = true;
myrect[i][j]->uValue += myrect[i+][j]->uValue;
myrect[i+][j]->uValue = ;
}
}
} //去掉空格
for (int i = ; i < LINELENGTH ; i++)
{
//
if ( myrect[i][j]->uValue != )
{
for (int k = ; k < i; k++)
{
if (myrect[k][j]->uValue == )
{
bHaveDoneSth = true;
myrect[k][j]->uValue = myrect[i][j]->uValue;
myrect[i][j]->uValue = ;
break;
}
}
}
}
} break;
case VK_RIGHT:
bHaveDoneSth = false;
for (int i = ; i < LINELENGTH; i++)
{
//去掉空格
for (int j = LINELENGTH - ; j >= ; j--)
{
//
if ( myrect[i][j]->uValue != )
{
for (int k = LINELENGTH - ; k >= j; k--)
{
if (myrect[i][k]->uValue == )
{
bHaveDoneSth = true;
myrect[i][k]->uValue = myrect[i][j]->uValue;
myrect[i][j]->uValue = ;
break;
}
}
}
} //相加
for (int j = LINELENGTH - ; j > ; j--)
{
if ( myrect[i][j]->uValue != )
{
if ( myrect[i][j-]->uValue == myrect[i][j]->uValue )
{
bHaveDoneSth = true;
myrect[i][j]->uValue += myrect[i][j-]->uValue;
myrect[i][j-]->uValue = ;
}
}
} //去掉空格
for (int j = LINELENGTH - ; j >= ; j--)
{
//
if ( myrect[i][j]->uValue != )
{
for (int k = LINELENGTH - ; k >= j; k--)
{
if (myrect[i][k]->uValue == )
{
bHaveDoneSth = true;
myrect[i][k]->uValue = myrect[i][j]->uValue;
myrect[i][j]->uValue = ;
break;
}
}
}
}
} break;
case VK_DOWN:
bHaveDoneSth = false;
for (int j = LINELENGTH - ; j >= ; j--)
{ //去掉空格
for (int i = LINELENGTH - ; i >= ; i--)
{
//
if ( myrect[i][j]->uValue != )
{
for (int k = LINELENGTH - ; k >= i; k--)
{
if (myrect[k][j]->uValue == )
{
bHaveDoneSth = true;
myrect[k][j]->uValue = myrect[i][j]->uValue;
myrect[i][j]->uValue = ;
break;
}
}
}
} //相加
for (int i = LINELENGTH - ; i > ; i--)
{
if ( myrect[i][j]->uValue != )
{
if ( myrect[i-][j]->uValue == myrect[i][j]->uValue )
{
bHaveDoneSth = true;
myrect[i][j]->uValue += myrect[i-][j]->uValue;
myrect[i-][j]->uValue = ;
}
}
} //去掉空格
for (int i = LINELENGTH - ; i >= ; i--)
{
//
if ( myrect[i][j]->uValue != )
{
for (int k = LINELENGTH-; k >= i; k--)
{
if (myrect[k][j]->uValue == )
{
bHaveDoneSth = true;
myrect[k][j]->uValue = myrect[i][j]->uValue;
myrect[i][j]->uValue = ;
break;
}
}
}
}
} break;
default:
break;
} if (bHaveDoneSth)
{
GenerateNewNum();
} Invalidate(FALSE);
if ( GameOver())
{
AfxMessageBox("游戏结束!");
};
(完)
基于MFC对话框的2048游戏的更多相关文章
- MFC技术积累——基于MFC对话框类的那些事儿
1. 创建对话框类 (1)打开VC++6.0环境,点击:文件→新建: (2)在弹出的新建对话框中选择:工程→MFC AppWizard (exe)→输入工程名称(例如:功能调试)→工程保存路径名→确定 ...
- MFC技术积累——基于MFC对话框类的那些事儿2
3. 绘图 3.1 对话框资源编辑 首先通过添加控件的方式来创建一个简单的绘图对话框如图所示,创建步骤为: 第一.在VC++6.0软件环境的灰色空白区域右击,选中Controls,然后会弹出一个控件对 ...
- MFC技术积累——基于MFC对话框类的那些事儿3
3.3.2 创建图形画刷来实现位图加载 1.首先在Resource View中导入一幅位图,位图大小96×96像素: 2.其次在主对话框中添加一个静态文本资源,ID号是IDC_BITMAPAREA,添 ...
- MFC技术积累——基于MFC对话框类的那些事儿5
4. 菜单 4.1 弹出菜单 本节主要讲解如何在主对话框的指定区域内通过鼠标右击来弹出一个菜单选项.最终效果图如图4.1. 如图4.1鼠标只能在指定区域(图中深色区域)内右击时弹出菜单,在指定区域外点 ...
- MFC技术积累——基于MFC对话框类的那些事儿4
3.3.4 借助兼容DC加载DIB位图 创建一个与设备环境相兼容的DC,通过将位图暂时导入至兼容DC,然后利用CDC::BitBlt 或者CDC::StretchBlt函数将位图绘制到设备环境中. 示 ...
- c#调用c++制作的基于mfc的ocx控件
原文:http://blog.csdn.net/yhhyhhyhhyhh/article/details/51286926 原文中有问题部分已修改. c#调用c++制作的基于mfc的ocx控件 ...
- powershell字符界面的,powershell加WPF界面的,2048游戏
------[序言]------ 1 2048游戏,有段时间很火,我在地铁上看有人玩过.没错,坐地铁很无聊,人家玩我就一直盯着看. 2 我在电脑上找了一个,试玩了以下,没几次格子就满了.我就气呼呼的放 ...
- MFC对话框显示BMP图片
1.MFC对话框显示BMP图片我们先从简单的开始吧.先分一个类: (一) 非动态显示图片(即图片先通过资源管理器载入,有一个固定ID) (二) 动态载入图片(即只需要在程序中指定图片的路径即可载入) ...
- MFC对话框中显示BMP,JPG图片
//************************************ // 方法说明: 显示JPG和GIF.BMP图片 // 参数说明: CDC * pDC 设 ...
随机推荐
- python 简单了解一下 描述器
1.描述器是什么? 在Python中描述器也被称为描述符, 1)描述器实际上是任何新式类(新式类是继承自 type 或者 object 的类),这种类至少实现了3个特殊的方法__get__, __se ...
- centos7 mysql 启动mysqld.service - SYSV: MySQL database server错误
1.启动命令 systemctl start mysqld.service 或者 /etc/init.d/mysqld start 结果同样的错误 2.错误是: Job for mysqld.se ...
- C#与C++数据类型比较及结构体转换(搜集整理二)
原文网址:http://www.blogjava.net/heting/archive/2010/03/20/315998.html C++ C# ========================== ...
- process.env.NODE_ENV
Node 随记 if (process.env.NODE_ENV === 'production') { module.exports = require('./prod.js') } else { ...
- 【leetcode】520. Detect Capital
problem 520. Detect Capital 题意: 题目中给出的三种情况,分别是全是大写.全是小写.首字母大写,这三种情况返回True;否则返回False; solution: class ...
- iOS-MMDrawerController的使用【抽屉视图+(SUNSlideSwitchView)进度条手势滑动】转
下载网站:https://github.com/mutualmobile/MMDrawerController 首先,到下载网址下载MMDrawerController,将文件导入工程,里面有: MM ...
- selenium3 web自动化测试框架 二:页面基础操作、元素定位方法封装、页面操作方法封装
学习目的: 掌握自动化框架中需要的一些基础web操作 正式步骤: 使用title_contains检查页面是否正确 # -*- coding:utf-8 -*- import time from se ...
- 微信demo小游戏:飞机大战从无到有
微信demo游戏飞机大战从无到有 现在创建新项目会默认给飞机大战的demo,这里给大家从基础开始讲解游戏的从无到有是怎么实现的. 具体实现步骤: 创建背景图->背景图运动起来->创建飞机并 ...
- Ubuntu搭建Spring源码环境常见问题
在一心想要学习Spring框架源码时,我们会遇到很多麻烦的问题.开始本文前,你只需要拥有一个装好IDEA的Ubuntu系统就可以愉快启程了.如果还没有IDEA,可以参考在Ubuntu上安装Intell ...
- linux 怎样关闭x server?
如果想切换至纯粹一点的命令字符console下,一般人会认为切换Ctrl+Alt+F1(或者F2-F6都可以). 默认下,Ctrl+Alt+F7是图形界面(当然,各个Linux发行版本会有所差异). ...