您的位置:首页 > 其它

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.正招人.欢迎加入
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: