◆摘 要:本文从坦克大战游戏的设计细节入手,深入探讨了其数据结构和算法的设计,对于一般程序设计具有指导意义,使用的编程语言是html5 + JavaScript,软件开发环境使用Visual Studio Code,运行环境为网页。
◆关键词:游戏软件;数据结构;算法设计
计算机程序设计教学中,有一个重点,那就是二维数组的构建和使用。本文介绍了坦克大战小游戏的软件开发细节,这些讨论可以帮助学生增加对这种数据结构的理解。很多小游戏的界面主要是一种平面关系的体现,其内部数据结构和外面界面设计基本上都应该是二维数组的应用。在进行游戏程序设计中,其数据结构的设计占据了主要的地位,其次其游戏策略决定了其算法的设计。本文结合坦克大战游戏开发细节讨论对涉及到的部分数据结构和游戏策略进行讨论。具体实现中html文件用来实现网页上的文字、图片或音频文件的加载,具体显示的实现以及其他功能的实现是启用js文件,css文件是用来美化网页,结构与样式的分离。
一、总体设计
本游戏的总体设计目标为网页上运行的坦克打坦克。为了达成这款游戏的设计,我们必须考虑和设计以下一些因素:游戏开始界面的设计,不同关数的背景图片显示,己方坦克的射击,己方坦克在发射子弹的时候的子弹飞行轨迹和是否击中敌方坦克,自己的坦克被击中的时候所产生的判定,游戏之中所获得的分数的计算和显示,还需要判断坦克之间是否相撞等游戏结束的判定。
功能和界面设计方面主要是简洁和自然,主界面上只有一个start(开始)按钮可点。然后是选择模式界面,当鼠标移到两个坦克里,会出现“简单模式”和“困难模式”字样。点击后可以进入相应的界面。彻底搞清楚机制后可以相应增加更多的关数。在简单模式中,点击左上角的橙色音量图标开启音乐(默认进入游戏是关闭的),上下左右方向键用来控制己方坦克的移动,空格键用来给坦克发射子弹,当分数达到一定条件时,子弹移动速度会变快和子弹一次出现两颗等。在简单模式中,只要己方坦克碰到敌方坦克或敌方子弹后游戏都会结束;在困难模式中启用了一个默认的护甲值,两方坦克都是中两次子弹后才会消失,但是两方坦克相撞的话就马上结束游戏。另外在第二關中我方子弹是斜着飞的。如果我方坦克被子弹击中一次后,子弹移动速度就不会提升了。
二、数据结构和游戏策略探讨
在坦克大战游戏中,我们需要一个平面的数据结构来展示空中的俯视效果,这主要是通过二维数组来实现。我们需要不断的刷新己方飞机的位置和随机出现的敌机的位置,然后就是双方的子弹飞行轨道设计等。
下面先讨论己方坦克的数据结构和敌方坦克的数据结构,首先统一成下面的设计,然后在分别调用处理双方不同的坦克。
用图片直接表示坦克是比较简洁的编程方式,参数image表示照片,x,y表示出现在屏幕上的位置,width和height表示图片的宽度和高度,frm表示当前第几帧,dis表示时间间隔,hp表示坦克的护甲。图片不同和出现位置的不同就代表了双方不同的坦克。
然后是关于子弹类的设计,和坦克实际上是差不多的参数设计,不过相比于坦克类,多了一个speed表示子弹的速度。刚开始是初始速度,当达到一定条件后可以通过修改设置使得子弹速度变快。另外在复杂模式的情况下,考虑把子弹变成斜着飞,这样增添了设计的变化性。
在屏幕上画出坦克时,主要考虑敌方坦克要会自己移动,所以在画己方坦克的时候this.x和this.y这两行不要,使之受人工键盘控制移动即可。
Tank.prototype.draw = function(ctx){
ctx.save();
ctx.translate(this.x, this.y);
ctx.drawImage(this.image, this.frm*this.width, 0, this.width, this.height,
0, 0, this.width, this.height);
ctx.restore();
this.y++;
this.x = this.originX + 20*Math.sin(Math.PI / 100*this.y);
this.dis++;
if (this.dis >= 3){
this.dis = 0;
this.frm++;
if (this.frm >= this.n) this.frm = 0;
}
};
在坦克移动的设计方面,通过dx和dy两个偏移量的设计,可以达成移动,但是要注意不能移动到游戏边界外面去了,所以要设计一下意外处理,当坦克要移动出游戏边界时,使之原地不动。
Tank.prototype.move = function (dx, dy){
this.x += dx;
this.y += dy;
//下面处理脱离游戏界面的坦克
if (this.x < 0 || this.x > 645)
this.x -= dx;
if (this.y < 0 || this.y > 500)
this.y -= dy;
};
在屏幕刷新时需要不断地画下移的坦克,源码如下:
Tank.prototype.draw = function(ctx){
ctx.save();
ctx.translate(this.x, this.y);
ctx.drawImage(this.image, this.frm*this.width, 0, this.width, this.height,
0, 0, this.width, this.height);
ctx.restore();
this.y++;
this.x = this.originX + 20*Math.sin(Math.PI / 100*this.y);
this.dis++;
if (this.dis >= 3){
this.dis = 0;
this.frm++;
if (this.frm >= this.n) this.frm = 0;
}
};
如果坦克没有动,那么重画的过程只需要把this.y++;和this.x变化的两个语句删除即可。
在检测有没有两个坦克相互碰撞的设计上,主要考察两个坦克图片边界位置是否有相互重叠的部分,分为四个方向的相互重叠先行做一个通用的函数设计,然后调用它来判断两个坦克之间的边界是否重叠。
function isColliding(ax, ay, aw, ah, bx, by, bw, bh)
{
if(ay > by + bh || by > ay + ah || ax > bx + bw || bx > ax + aw)
return false;
else
return true;
}
Tank.prototype.hitTestObject = function(tankobj){
if(isColliding(this.x, this.y, this.width, this.height,
tankobj.x, tankobj.y, tankobj.width, tankobj.height))
return true;
else
return false;
};
在第二模式中,需要考慮子弹的左偏,下面是相关源码,而子弹右偏时只需要把this.x-=0.5;中的减法改为加法即可。
Bullet.prototype.draw3 = function (ctx){
ctx.save(); //保存当前环境的状态
ctx.translate(this.x, this.y); //重新映射画布上的位置
ctx.drawImage(this.image, this.frm*this.width, 0, this.width, this.height,
0, 0, this.width, this.height);
ctx.restore(); //返回之前保存过的路径状态和属性
this.x-=0.5;
this.y-=this.speed;
this.dis++;
if (this.dis >= 10){
this.dis = 0;
this.frm++;
if (this.frm >= 2) this.frm = 0;
}
};
在游戏过程中,用户的按键决定了坦克的移动和射击等功能,主要是通过每个键码来进行判断的。键码为32时,说明是空格,于是需要处理子弹的射击,而键码为37、38、39和40时分别对应左右上下的处理。
function onkeydown(e) {
if (e.keyCode==32){
if (score < 100)
bullets.push(new Bullet(image4, mytank.x+37, mytank.y-10));
else{
bullets.push(new Bullet(image4, mytank.x+25, mytank.y-10));
bullets.push(new Bullet(image4, mytank.x+49, mytank.y-10));
}
}
else if (e.keyCode==37)
{
if (score < 100)
mytank.move(-10, 0);
else
mytank.move(-20, 0);
}
}
其他几个方向的处理源码非常类似,就不在这里列出了。限于篇幅,诸如主界面设计、背景音乐设计等其他设计细节也不深入探讨了。
三、总结
要想设计好一款速度快、效果好、界面优的游戏,必须对数据结构有着充分的了解和实际应用的能力。二维数组是游戏开发中特别有用的一种数据结构。通过坦克大战这个游戏软件的讨论可以增加对二维数组更深的理解。
作者简介
马春江(1963.01-),男,籍贯河南,研究生班,讲师,湖北汽车工业学院计算机系,研究方向为数据结构、算法设计、图形处理、动画设计等。