用C++和EasyX图形库编写一个简单的打砖块游戏(上)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xMaii/article/details/78154870

  大家好,这里先祝大家月饼节快乐了!博主我呢,目前是名大二学生,学校在本学期开设了C++的课程。为了能练习C++,我尝试着完成了一个简易的打砖块游戏。本着记录心得和同大家交流的想法,我决意写下这篇博客。需要说明的是,游戏的主要思路借鉴了某培训机构的公开课视频,我在此对老师表示感谢。不过老师是用纯C语言完成的,在我用C++重写时,遇到了不少问题,也做了很多学习和思考,最后完成得也算是比较满意吧。希望这个简单的项目能让初学的我们更好地学习和理解C++吧。
  整个代码不过200行,这里先贴出几张效果图吧。
这里写图片描述

  我目前打算把这篇博客写成上下两篇,毕竟我也是第一次打算写这么长的文章。若其中有疏漏,欢迎大家指出,当然也包括之后代码里可能出现的各种bug啦等等。
  不过,还需说明的是,我使用的编译器是VS2015,另需用到EasyX。这里附上它的下载地址吧。EasyX下载请点击这里.
  在下载好了以后,选择和你相对应的编译器进行安装就好了。
  那么,准备工作完成后,让我们开始教程吧。
  打开编译器后,自然是要先创建一个空项目,项目名称用“打砖块”就好了。创建好了以后,我们就可以开始书写代码了。
  首先调用以下头文件。前者是图形库文件,后者主要用于进行键盘操作。

#include<graphics.h>
#include<conio.h>

  接着我们要确定游戏界面的大小,这需要调用函数  

initgraph(int width, int height, int flag)

  它的作用是初始化一个图形区域,前两个参数是图形区域的宽和高,后一个参数是绘图环境的样式,常设置为空(具体作用我也不明白…)。而我的设定是一个600*400像素单位的矩形,代码如下。

#include<graphics.h>
#include<conio.h>

const int WINDOW_HEIGHT = 600;  //定义窗口的高
const int WINDOW_WIDE = 400;    //定义窗口的宽

int main()
{
    initgraph(WINDOW_WIDE, WINDOW_HEIGHT);  //初始化窗口
    _getch();
    return 0;
}

  那么编译运行后我们应该得到一个600*400像素的矩形界面了,如下图所示。
这里写图片描述

  现在框架已经确定好啦,就让我们在其中添砖加瓦吧。要知道,在这个打砖块的游戏中,无非只有砖块,木板,小球这三种游戏元素。那么在编程时,显然我们应该将他们定义成三种不同的类。不过在这之前,我们得先了解几个用于作图的函数,他们分别是:

  1. setfillcolor(COLORREF color);
    设置图形颜色,参数可以用RBG设定,也可以直接写颜色,如RED, BLACK等。
  2. fillrectangle(int left, int top, int right, int bottom);
    画一个带边框的矩形,其四个参数分别指两个顶点的坐标。
    如图,当我们确定A, D两点的坐标后,这个矩形的位置是不是就可以确定了呢。
    这里写图片描述

  3. solidrectangle(int left, int top, int right, int bottom);
    画一个不带边框的矩形,其参数含义与上一个函数相同。

  4. solidcircle(int x, int y, int radius);
    画一个圆形,参数x,y,radius分别表示圆心的横坐标,纵坐标和圆的半径

  ok,简单了解就行。下面让我们从最简单的写起,首先来定义砖块的类,想一想这其中应该包含哪些成员变量和成员函数。
  我用一个二维数组存储砖块的位置,我们可以暂定砖块是5排10列,那么砖块总数,以及每个砖块的长和宽都可以计算出来了。另外,除了初始化函数外,我们还需要一个成员函数把所有的砖块都画出来,这只需在对数组循环遍历时调用上述作图函数即可。不过我们需要知道的是,对于第i行第j个(列)砖块,它的坐标又是多少呢。具体可见下图,方便理解。
  这里写图片描述
  易知,当得出A点坐标后,B点的横坐标就是在A点横坐标上加上矩形的长度,B点的纵坐标就是在A点纵坐标上加上矩形的宽度了。现给出砖块类的定义如下。

class Bricks    //定义砖块类
{
public:
    int bricks[12][12];                 //用二维数组保存所有砖块
    int count;                          //记录砖块总数
    const int x = 10, y = 5;            //确定砖块有几排(y)几列(x)
    const int length = WINDOW_WIDE / x; //计算每个砖块的长
    const int wide = 20;                //定义每个砖头的宽

    //构造函数
    Bricks()
    {
        memset(bricks, 0, sizeof(bricks));  //初始化 0表示有砖块
        count = x*y;                        //计算砖块总数
    }

    //画出所有的砖块
    void drawAllbricks()
    {
        setfillcolor(YELLOW);   //设置砖块颜色
        setlinecolor(BLACK);    //设置边框颜色
        for (int i = 0; i < y; i++)
            for (int j = 0; j < x; j++)
                fillrectangle(j*length, i*wide, (j + 1)*length, (i + 1)*wide);
    }
};

  需要说明的是,函数setlinecolor(COLORREF color),它的作用是设置图形边框颜色,如果我们试着把它在成员函数drawAllbricks()中的调用注释掉的话,它将默认砖块的边框为白色,但是我们的背景是黑色,结果运行后一点儿也不美观,所以我们把边框颜色设置成背景色黑色。不过需要注意的是,每个砖块的边框的长度是1,这在以后处理细节时需要用到,现在先不用理会。

这里写图片描述

  
  在定义完砖块类后,我们紧接着来定义木板类。对于木板而言,我们只需要考虑它的长度和宽度,以及它得根据玩家的控制来左右移动。而实现运动的原理,就是不停地在新位置画木板,同时用背景色覆盖掉原先位置的木板,这样木板看起来就像是在运动了,小球运动也是同理。另外,关于类中的成员变量x,y,即木板的坐标,并非指木板中心点的坐标,而是指这个矩形木板左上角的顶点的坐标,所以对于类中木板初始位置的计算,以及边界限制的计算,都应该是很好理解的。

class Board                 //定义木板类
{
public:
    int x, y;               //定义板的坐标
    const int length = 60;  //定义板的长度
    const int wide = 15;    //定义板的宽度

    //构造函数 将木板初始化在中心位置
    Board()
    {
        x = WINDOW_WIDE / 2 - length / 2;
        y = WINDOW_HEIGHT - wide;
    }

    //木板移动函数
    void Move()
    {
        //键盘的每个按键都对应一个确定的键值,如方向键 → 对应77, ← 对应75
        int ch;         //接受一个键值
        ch = _getch();

        setfillcolor(BLACK);    //将木板当前位置用背景色黑色覆盖
        solidrectangle(x, y, x + length, y + wide);

        switch (ch)
        {
        case 75:        //每次左移木板长度的1/3
        case 'A':
        case 'a':
            x -= length / 3;
            break;
        case 77:        //每次右移木板长度的1/3
        case 'D':
        case 'd':
            x += length / 3;
            break;
        }
        //木板左右移动的边界限制
        if (x <= 0) x = 0;                                      //左边界
        if (x >= WINDOW_WIDE - length) x = WINDOW_WIDE - length;//右边界
        setfillcolor(BLUE);     //更新坐标后画新木板
        solidrectangle(x, y, x + length, y + wide);
    }
};

  现在,我们已经完成了砖块类和木板类的定义了,是不是想快点检验成果呢。我们在主函数前编写自定义函数,用来实现整个游戏的逻辑控制,并在主函数中调用。代码如下:

#include<graphics.h>
#include<conio.h>

const int WINDOW_HEIGHT = 600;  //定义窗口的高
const int WINDOW_WIDE = 400;    //定义窗口的宽

class Bricks    //定义砖块类
{
public:
    int bricks[12][12];                 //用二维数组保存所有砖块
    int count;                          //记录砖块总数
    const int x = 10, y = 5;            //确定砖块有几排(y)几列(x)
    const int length = WINDOW_WIDE / x; //计算每个砖块的长和宽
    const int wide = 20;

    //构造函数
    Bricks()
    {
        memset(bricks, 0, sizeof(bricks));  //初始化 0表示有砖块
        count = x*y;                        //计算砖块总数
    }

    //画出所有的砖块
    void drawAllbricks()
    {
        setfillcolor(YELLOW);   //设置砖块颜色
        setlinecolor(BLACK);    //设置边框颜色
        for (int i = 0; i < y; i++)
            for (int j = 0; j < x; j++)
                fillrectangle(j*length, i*wide, (j + 1)*length, (i + 1)*wide);
    }
};

class Board                 //定义木板类
{
public:
    int x, y;               //定义板的坐标
    const int length = 60;  //定义板的长度
    const int wide = 15;    //定义板的宽度

    //构造函数 将木板的坐标初始化在中心位置
    Board()
    {
        x = WINDOW_WIDE / 2 - length / 2;
        y = WINDOW_HEIGHT - wide;
    }

    //木板移动函数
    void Move()
    {
        int ch;         //接受一个键值
        ch = _getch();

        setfillcolor(BLACK);    //将木板当前位置用背景色黑色覆盖
        solidrectangle(x, y, x + length, y + wide);

        switch (ch)
        {
        case 75:        //每次左移木板长度的1/3
        case 'A':
        case 'a':
            x -= length / 3;
            break;
        case 77:        //每次右移木板长度的1/3
        case 'D':
        case 'd':
            x += length / 3;
            break;
        }
        //木板左右移动的边界限制
        if (x <= 0) x = 0;
        if (x >= WINDOW_WIDE - length) x = WINDOW_WIDE - length;
        setfillcolor(BLUE);     //更新坐标后画新木板
        solidrectangle(x, y, x + length, y + wide);
    }
};

void Gaming()
{
    Bricks brick;           //声明砖块对象
    brick.drawAllbricks();  //调用成员函数,画出所有的砖块

    Board board;            //声明木板对象
    setfillcolor(BLUE);     //设置木板颜色
    solidrectangle(board.x, board.y, board.x + board.length, board.y + board.wide);                        //画出木板

    while (1)
    {
        if (_kbhit())       //用该函数判断你是否按下某个键,按下返回1,否则返回0
        {
            board.Move();   //按下键后,调用木板类的成员函数,控制木板的移动
        }
    }
}

int main()
{
    initgraph(WINDOW_WIDE, WINDOW_HEIGHT);  //初始化窗口
    Gaming();
    _getch();
    return 0;
}

  如果一切顺利的话,运行后你将得到如下画面,同时木板可以在你的控制下左右移动。
  这里写图片描述

  至此,我们的教程已经完成了将近一半,剩下的只有游戏中最关键的部分—小球了。因为小球类是代码中相对难写的部分,所以这部分我将放在下一篇里详细介绍。
  hh,也不知道在这一篇里我讲的怎么样,说的好不好。呀,不管怎样,希望大家都能从中有所得有所学吧。
  至于下篇,乘着国庆放假,我会尽快完成的!(认真脸…)回见啦。
  

猜你喜欢

转载自blog.csdn.net/xMaii/article/details/78154870