作为一个C语言刚入门的小萌新,学完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;//tank1b = rand() % 5;//tank2c = rand() % 5;//tank3if (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)来控制切换速率
这样一个简单的坦克大战就完成了~