使用EasyX图形库编写2D游戏



什么是 EasyX

一言以蔽之,EasyX 是一个 C++ 图形库,封装了一些基本的绘图函数,可以实现在控制台窗口绘制图像,帮助 C/C++语言初学者快速上手图形编程。

开始使用

那么如何使用呢?
首先去它的官网下载安装包,选择对应的 VS 版本,可以看到这个库虽然年代比较久远,不过也对新版本的 VS 有所支持。
由于我使用的是 VS2017,所以下载最新的 2018 春分版。下载好解压,双击安装,库文件就会一键安装到你的 VS 路径里。
然后在项目里引用头文件 graphics.h 就可以使用了。
我们可以在 EasyX 官网的范例程序里看到很多免费的小项目和例子,再结合官网的入门教程,就可以很轻松的入门了。

开始设计

这里我用它来设计一个基础的小游戏 —— Flappy Paper Plane。
游戏类似 Flappy Bird ,玩家控制纸飞机躲避障碍。

设计要求

本程序应完成以下几方面的功能:

  • 开始界面菜单的显示;

  • 通过按键对玩家进行控制;

  • 实现障碍物的自动移动;

  • 游戏结束提示。

设计思路

  • 整个游戏流程放在一个 while 循环中。
  • 通过控制图片坐标来实现玩家的上下移动和障碍物的左右移动。
  • 程序函数模块:
    • 初始化游戏数据 initGame()
    • 加载游戏所需位图 loadImages()
    • 控制玩家移动 fly()
    • 控制障碍物移动 thornMove()
    • 碰撞检测 judgement()
    • 打印图像 print()

程序

首先创建玩家和障碍物的结构体,用结构体以便后续对游戏功能进行拓展。

1
2
3
4
5
6
7
8
9
10
11
struct thornPoint
{
int x;
int y;
}thornPoint;
struct playerPoint
{
int x;
int y;
}playerPoint;

初始化游戏数据

包括创建绘图窗口,初始化坐标系,初始化角色与障碍物坐标。

首先使用 initgraph 函数用来创建一个 1000x600 大小的绘图窗口。

由于图形库默认坐标原点在窗口左上角,因此将其设置为常见的平面直角坐标系以便于坐标计算。但由于将坐标系倒过来了,因此要求图片文件也是倒过来的,这样加载进绘图窗口中就是正的了。

1
2
setorigin(0, 600);
setaspectratio(1, -1);

接下来对玩家和障碍物坐标的初始化。完整代码段如下:

1
2
3
4
5
6
7
8
9
10
11
initgraph(HORIZONAL, VERTICAL); // 创建绘图窗口
setorigin(0, 600); //设置坐标原点
setaspectratio(1, -1); //设置为直角坐标系
srand((unsigned)time(NULL)); //初始化障碍物与玩家坐标
thornPoint.y = -300;
thornPoint.x = 950;
playerPoint.x = 400;
playerPoint.y = 300;
isDead = false;
speed = 0;

加载游戏所需位图

我们需要实现一个图片的背景透明,方法之一就是使用位图的掩码图。

拿玩家的图片为例:

掩码图中想要显示的区域为黑色,想透明的区域为白色,如下:


用 loadimage 函数将所有位图素材的引用保存到定义好的 IMAGE 变量中。拿玩家的位图为例,后两个参数为图片的尺寸(毫米):

1
2
3
IMAGE player,player_;
loadimage(&player, L"res\\fly.bmp", 120, 46);
loadimage(&player_, L"res\\flyx.bmp", 120, 46);

控制玩家与障碍物移动

采用两个变量 isPress 和 speed 控制玩家自然下落和按键上升。
当没有按下按键时,此时检测 isPress 的值为 false,玩家坐标下移。
当按下按键时,isPress 为 true,这是给 speed 一个值,即为按下按钮时玩家上升的速度。判断速度是否为 0,不为 0 时玩家坐标上移,speed 递减直到为 0。
这部分代码段如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ispress = false;
if (isPress == false)
{
playerPoint.y -= 1;//下落
}
if (_kbhit())//读取空格
{
key = _getch();
if (key == ' ')
{
speed = 40;
isPress = true;
}
for(;speed != 0;speed --;)
{
playerPoint.y += 2;
}

控制障碍物移动:

1
thornPoint.x -= 2;

使边界坐标判断,使障碍物在离开窗口最左边时回到窗口最右边,并且随机一个纵坐标值。

1
2
3
4
5
if (thornPoint.x <= -100)
{
thornPoint.y = -(rand() % 250 +100);
thornPoint.x = 950;
}

碰撞检测

使用坐标是否重叠来判断碰撞。

1
2
3
4
5
6
7
8
9
if ( playerPoint.y <= 0)
{
isDead = true;
}
else if (playerPoint.y < thornPoint.y + 580)
{
if (playerPoint.x + 120 == thornPoint .x+ 50 || playerPoint.x == thornPoint.x + 50)
isDead = true;
}

将位图输出至绘图窗口

使用 putimage 函数,拿玩家位图为例:

1
2
putimage(playerPoint.x, playerPoint.y, &player_, SRCAND);
putimage(playerPoint.x, playerPoint.y, &player, SRCPAINT);

putimage 函数的最后一个参数为三元光栅操作码,代表屏幕颜色和图像颜色的叠加方式,SRCAND 是 AND 方式,而 SRCPAINT 是 OR 方式,大概意思就是:

白色(1)&任何颜色=原颜色,黑色(0)&任何颜色=黑色,黑色(0)| 任何颜色=原颜色

游戏效果

后记

  • 这个项目是我在课设中做的一个相当简陋的 demo,本来想做成 3D 视角,不过因为时间关系就放弃了。当时只实现了基本功能,甚至连计分功能的没有,不过作为 EasyX 的初学者示范程序来说还是很不错的。
  • 游戏背景来自画师 Philip Govedare,很喜欢他笔下的天空和大地。