基于C++如何使用EGE做一个简单的坦克大战游戏

   作为一个C语言刚入门的小萌新,学完C语言基础语法后就迫不及待想要自己做一个游戏出来了,然后想到了小时候插卡玩的坦克大战,于是做了一个黑框框版的简单坦克大战,后面学了一点EGE图形库就在黑框框版的坦克大战上加以改进了一下,最后完成的成果如下

基于C++如何使用EGE做一个简单的坦克大战游戏

 

 

废话不多说了,先讲一下我的思路吧,首先因为这个游戏风格比较明显,地图是由几种小方块不断重复拼凑而成的,所以我选择用一个二维数组来实现这个游戏,二维数组中的不同的数值代表不同的模块。然后思考了一下地图硬件元素包括了不可破坏的小白块,可被破坏的小砖块,可以藏人的小树林,可以吞噬子弹的小水洼,还有需要保护的“小鸟”,这些元素是一开始就随着地图的确定而确定的,所以初始化地图时便都设定完成,需要注意的是这些坦克具有哪些属性,不同的坦克肯定拥有不同的属性,生命力高但是攻击弱移动速度慢的坦克,生命低攻击强移动速度快的坦克等等  这些设置离不开结构体,当然还有些问题需要解决,子弹的碰撞怎么检测呢? 无限子弹是如何实现的? 如何判断撞墙.坦克死亡.砖块消失等操作?当这些问题得到解决的时候简单的坦克大战游戏就完成了。

 

首先把需要使用的图片全部存储,例如下代码所示

PIMAGE tank1_up;
tank1_up = newimage();
getimage(tank1_up,"red_up.png");//将名字为red_up.png的图像存入tank_up中

然后利用二维数组建立初始地图

int map1[20][20] =
{
    { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 },//1代表小白块  0代表可通行区域
    { 1,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,1 },
    { 1,0,0,1,1,1,1,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,1,1,0,0,0,0,1 },
    { 1,0,0,1,1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1 },
    { 1,0,1,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,1 },
    { 1,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1 },
    { 1,0,0,0,1,1,0,0,1,1,1,0,0,0,0,0,0,1,0,1 },
    { 1,0,0,1,1,1,0,0,0,0,1,0,0,0,0,0,0,1,0,1 },
    { 1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,0,1 },
    { 1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,1,0,1 },
    { 1,1,0,0,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,1 },
    { 1,0,0,0,1,1,0,0,0,1,1,0,0,0,0,1,1,1,0,1 },
    { 1,0,0,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1 },
    { 1,0,0,0,1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1 },
    { 1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1 },
    { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1 },
    { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1 },
    { 1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 },
    { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 },
};

分析了一下发现具有多个属性的元素有 坦克,砖块,草丛,子弹,小鸟等所以这些元素需要用到结构体,因为本博主比较懒,所以把他们属性综合起来了只建立了一个结构体,有兴趣的小伙伴可以挨个建立

typedef struct action
{
    int x;//横坐标
    int y;//纵坐标
    int alive;//是否存活
    int direction;//方向
    int speed;//速度
    int HP;//生命值
    int CD;//吃了特殊果实后的不同效果
    PIMAGE imag;//储存图片
} ACT;

利用横纵坐标确定他们的位置,然后坐标的加减实现他们的移动,通过不断清屏贴图实现动图,不过能否真正移动还需要坐标检测,坐标检测:假定进行了下一步操作通过横纵坐标判断是否移动到了地形(小白块,砖块,水洼,草丛等)上,如果移动到了,那么不进行这一步操作,但是方向会转过来(假如你本来向上,但是你向左移移不动,方向改为向左)。

在EGE中读取是否有键盘输入的函数是 kbhit() 通过他和getch()的配合可以达到键盘控制游戏代码如下图所示

二维数组中数值为12 代表特殊果实的地点,吃了会增加特殊能力,比如穿墙什么的,CD代表能力是什么,暂定能力都为1,也就是穿墙,wasd 分别是上左下右  j 代表发射子弹

if (kbhit())
    {
        select = getch();
        switch (select)
        {
        case 'd':
            if (map2[player.x][player.y + 1] == 0|| map2[player.x][player.y + 1] == 11)
                player.y++;
            if (map2[player.x][player.y + 1] == 12)
            {
                player.y++;
                food.alive = 0;
                player.CD = 1;
            }
            if (map2[player.x][player.y + 1] == 1 && player.CD == 1)
                player.y++;
            player.direction = right;
            break;
        case 'a':
            if (map2[player.x][player.y - 1] == 0|| map2[player.x][player.y - 1] == 11)
                player.y--;
            if (map2[player.x][player.y - 1] == 12)
            {
                player.y--;
                food.alive = 0;
                player.CD = 1;
            }
            if (map2[player.x][player.y - 1] == 1 && player.CD == 1)
                player.y--;
            player.direction = left;
            break;
        case 'w':
            if (map2[player.x - 1][player.y] == 0|| map2[player.x - 1][player.y] == 11)
                player.x--;
            if (map2[player.x - 1][player.y] == 12)
            {
                player.x--;
                food.alive = 0;
                player.CD = 1;
            }
            if (map2[player.x - 1][player.y] == 1 && player.CD == 1)
                player.x--;
            player.direction = up;
            break;
        case 's':
            if (map2[player.x + 1][player.y] == 0|| map2[player.x + 1][player.y] == 11)
                player.x++;
            if (map2[player.x + 1][player.y] == 12)
            {
                player.x++;
                food.alive = 0;
                player.CD = 1;
            }
            if (map2[player.x + 1][player.y] == 1 && player.CD == 1)
                player.x++;
            player.direction = down;
            break;
        case 'j':
            if (k == 50)
                k = 0;
            bullet[k].x = player.x;
            bullet[k].y = player.y;
            bullet[k].alive = 1;
            bullet[k].direction = player.direction;
            k++;
            break;
        }
    }

如何检测子弹碰撞?我使用的方法是把所有存活子弹遍历一遍,当子弹的横纵坐标与其他元素重合时子弹死亡,当然也会检测与子弹碰撞的是什么元素,假若是小砖块,那么小砖块和子弹一起死亡,也就是消失,假若是坦克,会扣除坦克生命值,假若是小白块,那么子弹自己消失等

如何实现无限子弹? 上图代码里最后的那几行已经写出来了,由于子弹消亡的速度很快,所以可以重复使用子弹数组里子弹,当k(子弹数目)到达50时 重置k 的值为0,这样就能达到无限子弹了

如何实现人机?  也就是其他坦克自己会动会攻击?  

我的答案是利用伪随机数来实现

在 #include<time.h>里有一个函数rand()(自行百度。。。),建立时间种子(随机数随着时间变化),使得每一次随机到的数字不一样,根据数字让人机“自主”行动。不过这可以改进一下,因为行动完全随机无法预测这样就失去了这个游戏的乐趣,所以可以添加一些小算法进去使得游戏更加有趣,比如说利用最短路径搜索人机坦克与玩家的位置,针对性的有意识的让人机更加趋向于向玩家靠近或者小鸟靠近。这里的up 就代表着0,因为我一开始就用了枚举enum { up, down, left, right, shut };

 srand(time(NULL));
    a = rand() % 5;//tank1

    b = rand() % 5;//tank2

    c = rand() % 5;//tank3
    if (tank1.alive == 1 && (++count1) % tank1.speed == 0)
        switch (a)
        {
        case up:
            if (map2[tank1.x - 1][tank1.y] == 0)
                tank1.x--;
            if (map2[tank1.x - 1][tank1.y] == 12)
            {
                tank1.x--;
                food.alive = 0;
                tank1.CD = 1;
            }
            if (map2[tank1.x - 1][tank1.y] == 1&&tank1.CD == 1)
                tank1.x--;
            tank1.direction = up;
            break;
        case down:
            if (map2[tank1.x + 1][tank1.y] == 0)
                tank1.x++;
            if (map2[tank1.x + 1][tank1.y] == 12)
            {
                tank1.x++;
                food.alive = 0;
                tank1.CD = 1;
            }
            if (map2[tank1.x + 1][tank1.y] == 1 && tank1.CD == 1)
                tank1.x++;
            tank1.direction = down;
            break;
        case left:
            if (map2[tank1.x][tank1.y - 1] == 0)
                tank1.y--;
            if (map2[tank1.x][tank1.y - 1] == 12)
            {
                tank1.y--;
                food.alive = 0;
                tank1.CD = 1;
            }
            if (map2[tank1.x][tank1.y - 1] == 1 && tank1.CD == 1)
                tank1.y--;
            tank1.direction = left;
            break;
        case right:
            if (map2[tank1.x][tank1.y + 1] == 0)
                tank1.y++;
            if (map2[tank1.x][tank1.y + 1] == 12)
            {
                tank1.y++;
                food.alive = 0;
                tank1.CD = 1;
            }
            if (map2[tank1.x][tank1.y + 1] == 1 && tank1.CD == 1)
                tank1.y++;
            tank1.direction = right;
            break;
        case shut:
            if (k == 50)
                k = 0;
            bullet[k].x = tank1.x;
            bullet[k].y = tank1.y;
            bullet[k].alive = 1;
            bullet[k].direction = tank1.direction;
            k++;
            break;
        }

最后  判断游戏结束的条件  小鸟死了或者人机全死亡或者玩家死亡,代码如下

if (player.alive == 0 || goal.alive == 0)
        {
            cleardevice();
            
            putimage(0, 0, shibai.imag);
            printf("shule");
            break;
        }
        if (tank1.alive == 0 && tank2.alive == 0 && tank3.alive == 0)
        {
            cleardevice();
      
            putimage(0, 0, shengli.imag);
            printf("yingle");
            break;
        }

主要躯干的代码就是这样了 ,实现动画的要点就是不断清屏不断贴图,可以使用sleep(n)来控制切换速率

这样一个简单的坦克大战就完成了~