大家好,这里先祝大家月饼节快乐了!博主我呢,目前是名大二学生,学校在本学期开设了C++的课程。为了能练习C++,我尝试着完成了一个简易的打砖块游戏。本着记录心得和同大家交流的想法,我决意写下这篇博客。需要说明的是,游戏的主要思路借鉴了某培训机构的公开课视频,我在此对老师表示感谢。不过老师是用纯C语言完成的,在我用C++重写时,遇到了不少问题,也做了很多学习和思考,最后完成得也算是比较满意吧。希望这个简单的项目能让初学的我们更好地学习和理解C++吧。
整个代码不过200行,这里先贴出几张效果图吧。
我目前打算把这篇博客写成上下两篇,毕竟我也是第一次打算写这么长的文章。若其中有疏漏,欢迎大家指出,当然也包括之后代码里可能出现的各种bug啦等等。
不过,还需说明的是,我使用的编译器是VS2015,另需用到EasyX。这里附上它的下载地址吧。EasyX下载请点击这里.
在下载好了以后,选择和你相对应的编译器进行安装就好了。
那么,准备工作完成后,让我们开始教程吧。
打开编译器后,自然是要先创建一个空项目,项目名称用“打砖块”就好了。创建好了以后,我们就可以开始书写代码了。
首先调用以下头文件。前者是图形库文件,后者主要用于进行键盘操作。
#include
#include
接着我们要确定游戏界面的大小,这需要调用函数
initgraph(int width, int height, int flag)
它的作用是初始化一个图形区域,前两个参数是图形区域的宽和高,后一个参数是绘图环境的样式,常设置为空(具体作用我也不明白…)。而我的设定是一个600*400像素单位的矩形,代码如下。
#include
#include
const int WINDOW_HEIGHT = 600; //定义窗口的高
const int WINDOW_WIDE = 400; //定义窗口的宽
int main()
{
initgraph(WINDOW_WIDE, WINDOW_HEIGHT); //初始化窗口
_getch();
return 0;
}
那么编译运行后我们应该得到一个600*400像素的矩形界面了,如下图所示。
现在框架已经确定好啦,就让我们在其中添砖加瓦吧。要知道,在这个打砖块的游戏中,无非只有砖块,木板,小球这三种游戏元素。那么在编程时,显然我们应该将他们定义成三种不同的类。不过在这之前,我们得先了解几个用于作图的函数,他们分别是:
- setfillcolor(COLORREF color);
设置图形颜色,参数可以用RBG设定,也可以直接写颜色,如RED, BLACK等。fillrectangle(int left, int top, int right, int bottom);
画一个带边框的矩形,其四个参数分别指两个顶点的坐标。
如图,当我们确定A, D两点的坐标后,这个矩形的位置是不是就可以确定了呢。
solidrectangle(int left, int top, int right, int bottom);
画一个不带边框的矩形,其参数含义与上一个函数相同。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
#include
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,也不知道在这一篇里我讲的怎么样,说的好不好。呀,不管怎样,希望大家都能从中有所得有所学吧。
至于下篇,乘着国庆放假,我会尽快完成的!(认真脸…)回见啦。