MFC 实现 俄罗斯方块
2008-12-03 11:31
232 查看
很经典的游戏.简单又让人着迷.
今天就用mfc来实现个自己的俄罗斯方块.运行截图如下:
看起来还行吧..
网上有很多版本的实现代码.但是看了几个都是用一个很大的数组记录这些下落的物体的形状.然后写一个很大的switch case来实现变形.感觉真麻烦.今天提出一个容易的实现方法.下面切入正题
思路是这样的:
一:随机生成物体
二:控制
1.变形
围绕一个方块向右旋转90度.以变形
2.左右下移动
1>物体左右移动的时候不要过界.
2>物体落定后.
(1).设定它落下的位置.
(2).看是否满了一行.满了一行消去
(3).看是否方块垒到顶了.到了game over
三:重复以上步骤
约定:
下落的会变形的那个小东东,我们叫 物体Object (--!!.大家忍受下这个名字.我是想不出其他的名字了.)
左侧那个 物体在里面移动的区域叫 游戏区域 Block
实现这个游戏的思路跟玩这个游戏一样简单...
1,随机生成一个物体.
2,响应键盘的输入,做左右下移动和变形
3,物体落到底部,判断是否满一行,物块垒满一行消除之.垒到顶部game over
但是实现起来貌似又不是那么简单了.
首先看起来随机生成一个不同形状的物体就是挑战
怎么在视图上画一个个的不同形状的物体呢?
我们看下这个游戏.每个物体都是由更小的方块组成.这些基本的小方块没有什么不同,
再看看这个二维的游戏界面,左边的游戏区域,一个个的小方块合在一起是否就是我们所熟悉的二维数组?
假如我们把这个游戏区域映射到一个二维数组,当这个数组元素有物体占据的时候就在上面涂颜色,没有则涂背景色.不就实现了么?.
好了可以定义游戏的区域的数据结构了.这个游戏中由于要画不同的颜色,因此数据结构可以如下定义:
struct tagBlock
{
bSet;
COLORREF color;
}block[18][10]; // 我们将这个游戏区域格式化为 18*10 (row*col);
那么画一个物体又该如何画呢?
假如我们先用数组定义好物体的形状,然后移动变化,那就太麻烦了.可以看到这些物体都是更小的方块组成的.(我们用四个小方块组成一个大的物体).用四个小方块,通过相对位置的不同可以变化成各种各样的不同形状.
我们最终是要在那个二维数组中画不同形状的物体.那么在画的时候就要直到当前的物体应该画到哪儿.假如我们对二维数组的每个元素再抽象出来一个数据结构.在这个数据结构中保存当前它在二维数组中的位置(行列值).那就容易多了.
typedef struct tagPane
{
int row; // 在block[18][10]的行
int col; // 列
}Pane;
这是一个小方格的定义,那每个物体要四个小方格,于是
struct tagObject
{
Pane object[4];
COLORREF color; // 每个物体有自己的颜色
int t; // 对应于最上面的一个方块的行值row
int b; // 对应于最下面一个方块行值
int l; // 对应于最左面一个方块的列值col
int r; // 对应于最右面一个方块列值
};
由于我们要在画一个当前的下落的物体和一个下次将出现的物体.因此定义两个物体的对象
tagObject Object,NextObject;
每个物体都是有边界的(把四个小方格全部盛下的最小矩形边界),我们把这个物体的四个小方块当前在block中的行列值,记录下来作为边界,放置在 tblr中.下面很多地方是用到这几个值的
我们规定每个小方块的实际大小是 (24*24)像素
那这个游戏区域实际上就是 height = 18*24 ,width = 10*24大小的矩形内(要注意数组的行列和矩形宽高的对应,col 对应的是width, row 对应的是height)
// 定义方格大小和游戏区域的矩形
const int PANE_WIDTH = 24; // 每个方格大小时24 * 24 pixel
const CRect BLOCK_RECT = CRect(29,31,271,463); // 游戏区域矩形
现在的问题是,你怎么知道具体应该在视图的哪个地方画这些小方块呢?
太简单了.我们可以由行列值(row,col)立即得到它在视图中所对应的矩形区域
CRect(col*PANE_WIDTH,row*PANE_WIDTH,(col+1)*PANE_WIDTH,(row+1)*PANE_WIDTH);
假如我们 要画一个 物体,物体的四个方块的行列值都知道的.比如是在二维数组中
(<0,0> <0,1> <1,0> <1,1>) 那
<0,0>对应的视图的坐标矩形是(0,0,24,24)
<0,1> -----(24,0,48,24)
....
自己那么对比几下就明白了.
可以写一个函数专门获取小方块对应的实际视图中的矩形区域(实际上,我们的游戏区域不是从视图的0,0开始画的,而是上和左都有空白,左边空白LEFT_MARGIN个像素,上边空白是TOP_MARGIN个像素,因此是以(LEFT_MARGIN,TOP_MARGIN)点处作为游戏区域的左上角 .方块的长和宽相等,定义为PANE_WIDTH = 24)
// 根据坐标取得客户区矩形
CRect CRBlock::GetPaneRect(int row, int col)
{
if (row < 0 || col < 0)
{
return CRect(0,0,0,0);
}
return CRect(col*PANE_WIDTH+LEFT_MARGIN,row*PANE_WIDTH+TOP_MARGIN,
(col+1)*PANE_WIDTH+LEFT_MARGIN,(row+1)*PANE_WIDTH+TOP_MARGIN);
}
//为方便.给出另一个重载的版本
// 由Pane的坐标取得对应的客户区的矩形
CRect CRBlock::GetPaneRect(Pane *p)
{
return GetPaneRect(p->row,p->col);
}
现在再绘制一个物体还难么???张飞吃豆芽.!
#define RANDOM_COLOR RGB(rand()%256,rand()%256,rand()%256)
// 绘制当前下落的物体
void CRBlock::DrawFallingObject(CDC *pDC)
{
CBrush brush(Object.color);
CBrush brBorder(RANDOM_COLOR);
CBrush *pOld = pDC->SelectObject(&brush);
for (int i = 0; i < 4; i++)
{
CRect rc = GetPaneRect(&(Object.object[i]));
if (!rc.IsRectNull()) //这个组成物体的小方块 还未出现在游戏区域内
{
pDC->Rectangle(&rc);
pDC->FrameRect(&rc,&brBorder); // 绘制边界,看起来会漂亮点哦.
}
}
pDC->SelectObject(pOld);
}
咦.郁闷了...吃豆芽的时候遇到点problem.....
需要绘制下一个将出现的物体.这个物体不是绘制在游戏区域内的.而是在游戏区域的右侧.
没关系.假如我们在游戏区域和这下个物体绘制的区域之间隔一个 方块的宽度.所有的问题都又解决了,原来的函数还照样可以用...
...继续吃豆芽
const CRect RECT_NEXTOBJECT = CRect(294,30,438,174); // 在这个矩形局域画下一个物体 4*6 *PANE_WIDTH 大小
//294 = LEFT_MARGIN + 10 * PANE_WIDTH + 1*PANE_WIDTH;
现在这个4*6的区域的 左上角,即是第一个方格的列坐标就是 10+1 = 11 了.至于行坐标,由于这个区域是和原来的游戏区域上对齐的,所以依然是0;
// 绘制下一个将出现的物体
void CRBlock::DrawNextObject(CDC *pDC)
{
int l = NextObject.l;
int t = NextObject.t;
int r = NextObject.r;
int b = NextObject.b;
// 把 NextObject 的行列映射到 要画在的矩形中
int offsetRow = (0+(6-(b-t+1))/2)-t; /// 这只是要把物体画到这个区域的中心
int offsetCol = (11+(6-(r-l+1))/2)-l; //
// 这一段只是美化
CBrush brBkgnd(COLOR_BKGND);
CBrush *pOld = pDC->SelectObject(&brBkgnd);
CPen pen;
pen.CreatePen(PS_SOLID,1,RANDOM_COLOR);
CPen *pOldPen = pDC->SelectObject(&pen);
pDC->Rectangle(&RECT_NEXTOBJECT);
pDC->SelectObject(pOldPen);
pDC->SelectObject(pOld);
CBrush brush(NextObject.color);
CBrush brBorder(RANDOM_COLOR);
pDC->SelectObject(&brush);
// 画下一个物体
for (int i = 0; i < 4; i++)
{
int row = NextObject.object[i].row;
int col = NextObject.object[i].col;
CRect rc = GetPaneRect(row+offsetRow,col+offsetCol);
if (!rc.IsRectNull())
{
pDC->Rectangle(&rc);
pDC->FrameRect(&rc,&brBorder);
}
}
}
好了.万事具备了.下次讲如何生成不同形状的物体物体编程群C,C++,MFC,Java 58698324.正招人.欢迎加入
相关文章推荐
- [原创]MFC实现的俄罗斯方块
- 半透明子窗体MFC实现
- 用MFC实现COM
- MFC 实现分割窗口
- OpenFileDialog 在Win32、MFC、C#的实现
- mfc实现图文打印
- MFC实现TabCtrl
- MFC简单的登录注册实现
- 用回车键实现MFC对话框中TAB键控件输入焦点在控件中跳转的效果(转)
- MFC中利用CSocket实现UDP通信
- MFC 实现保存数据为excel文件格式用那种方法好?
- MFC实现静态和动态显示bmp图片
- MFC实现Edit输入限制(只允许输入数字,负号和小数点)
- MFC实现文本插入描述符
- 使用MFC快速实现网络编程 CAsyncSocket
- 底层MFC窗口的实现
- MFC+OPENCV实现角点检测
- MFC使用SendMessage()发送自定义消息实现进程间通信
- 半透明: MFC实现父窗口不透明,子窗口半透明效果