本项目是基于easyX库来实现的一个C++版本的经典坦克大战的游戏界面。
easyX的全称为EasyX Graphics Library,是针对与Visual C++的免费绘图库,支持 VC6.0 ~ VC2019,简单易用,学习成本极低,应用领域广泛。
easyX的下载链接附上:
https://easyx.cn/download/EasyX_20210224.exe
#include
int main()
{
initgraph(650, 650);//创建一个650 ×650 的画布
system("pause");
}
首先,在我们创建任何游戏之前必然会有一个总体思路,即实现游戏的过程。在实现本次坦克大战的过程中,作者我主要是一下思路:
#include
int main()
{
initgraph(650, 650);//创建一个650 ×650 的画布
menu() //初始化菜单
init_map(&map[0][0], 26, 26);//初始化地图
play();//游戏开始
system("pause");
}
//显示logo
IMAGE logo_img; //定义一个IMAGE对象 logo_img
loadimage(&logo_img, _T("logo.bmp"), 433, 147);//将图片加载到logo_img里面去
putimage(110, 20, &logo_img); //显示图片
说明部分包括外部的方框,以及其中的说明二字,可以直接使用easyX中自带的fillrectangle 函数以及outtextxy函数,直接上代码:
setlinecolor(WHITE);//设置方框边框线的颜色
setfillcolor(BLACK);//设置方框填充的颜色
fillrectangle(220, 200, 300, 240);
settextstyle(25, 0, _T("宋体"));//设置字体类型
outtextxy(230, 210, _T("说明"));//显示字体
话不多说,直接上代码:
fillrectangle(350, 200, 430, 240);
outtextxy(360, 210, _T("开始"));
如果你前面的代码内容都操作无误的话,现在运行之后应该已经得到这样的结果了:
但是我们正常游戏界面并不希望是这样的,我们希望的是这样的效果:
所以还进行鼠标信息的处理,来实现鼠标一到达说明所在的位置,即可出现说明菜单的内容。同时,我们还希望在点击开始按键的时候,可以直接打开一个新的界面,进行游戏操作。好了,话不多说,直接上代码:
MOUSEMSG mouse; //定义一个MOUSEMSG对象的mouse,用来获取鼠标信息
IMAGE illustrate_img;//定义一个IMAGE类
loadimage(&illustrate_img, _T("illustrate.jpg"), 300, 300);//加载图片
while (1) {
mouse = GetMouseMsg();
switch (mouse.uMsg) {
case WM_MOUSEMOVE:
if (mouse.x > 220 && mouse.x < 300 && mouse.y > 200 && mouse.y < 240) {
putimage(150, 250, &illustrate_img);
}
else {
solidrectangle(150, 250, 450, 550);
}
break;
case WM_LBUTTONDOWN:
if ((mouse.x > 350 && mouse.x < 430) && (mouse.y > 200 && mouse.y < 240)) {
cleardevice();//清空
}
else
{
break;
}
return;
}
}
//注释: while循环中的内容主要为获取并分析鼠标的操作,因为鼠标经常性移动,多以创建的MOUSEMSG 类别的变量mouse必须时刻更新鼠标状态。switch语句用来分析鼠标状态:
至此,我们就可以实现一个基本的坦克大战初始化界面了。
玩过坦克大战游戏的都知道,我们进入游戏之后首先进入我们目光的就是游戏地图,而不同关卡的一个很大区别也是地图的不同。
所以如何实现地图显得至关重要。
如果想实现花里胡哨的游戏界面,那么图片必然是不可或缺的,所以卑微作者我的思路也就是通过加载地图图片来实现游戏的地图,话不多说,先上代码为敬:
void init_map2(int* map, int rows, int cols)
{
IMAGE img_home, img_wall_1, img_wall_2;
loadimage(&img_home, _T("home.jpg"), 50, 50);
loadimage(&img_wall_1, _T("wall1.jpg"), 25, 25);//不可消除的墙
loadimage(&img_wall_2, _T("wall2.jpg"), 25, 25);//可消除的墙
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
if (*(map + i * cols + j) == 1)
{
putimage(25 * j, 25 * i, &img_wall_2);
}
else if (*(map + i * cols + j) == 2)
{
putimage(25 * j, 25 * i, &img_wall_1);
}
else if (*(map + i * cols + j) == 3)
{
putimage(25 * j, 25 * i, &img_home);
*(map + i * cols + j) = 4;
*(map + i * cols + j + 1) = 4;
*(map + (i + 1) * cols + j) = 4;
*(map + (i + 1) * cols + j + 1) = 4;
}
}
}
}
*//注释:*因为在游戏中背景地图应该是包括三个部分,即老鹰和两种墙(可消除墙和不可消除墙),则本项目中就通过构造三个IMAGE对象:
img_home
img_wall_1
img_wall_2
来实现老巢(老鹰)、可消除墙、和不可消除墙的图片加载。
而加载后的图片应该如何展示出来呢,又应该在哪些位置展示呢,这就是后序循环中所实现的功能。通过两重循环,来扫描在最初我们所定义的map数组,来进行判断某一坐标是否应该展示图片,卑微作者在线献上map数组代码:
int map[26][26] = {
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{
0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0},
{
0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0},
{
0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0},
{
0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0},
{
0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 2, 2, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0},
{
0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 2, 2, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0},
{
0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0},
{
0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0},
{
0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0},
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{
1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1},
{
2, 2, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 2, 2},
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{
0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0},
{
0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0},
{
0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0},
{
0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0},
{
0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0},
{
0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0},
{
0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0},
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
};
至此,如果以上步骤都运行成功的话,那么恭喜你,我们已经得到了这样的一个效果
接下来,地图界面实现之后我们就要开始实现游戏中最重要的部分了,坦克之间的对决。
首先,我们创建一个描述坦克的结构体:
struct tank_s {
int x;//坦克所处位置的列
int y;//坦克所处位置的行
DIRECTION direction;//坦克的方向:上下左右
int live; //是否生存:1-活着,0-挂了
};
有坦克必然就会有子弹,然后我们定义一个子弹的结构体:
//子弹结构体
struct bullet_s {
int pos_x;
int pos_y;
DIRECTION direction;
int status;
};
接下来就是坦克和子弹的移动,即play函数的实现。
这之后的一些内容,我们将在后续坦克大战中为大家分享,学生党,更新速度有限,还请大家谅解…