继上一周开始做的模拟飞机大战游戏项目之后,本周一直持续在做坦克大战这个游戏项目上,虽然看似简单的游戏,但真正对于我们这刚学了JS的初学者来讲,还有点挑战,因为里面涉及的javascript代码颇多,具有大量的逻辑思维想象,值得研究。好吧,下面一如既往的直奔正题:
一、需求分析
首先我们对这个游戏做了简单的需求分析,要实现一个坦克大战游戏,游戏中包括我方坦克(玩家)、对方坦克(坏坦克),在游戏窗体中有障碍物,包括土墙、钢砖、水面和草地、我方的基地(Boss老巢)。双方坦克都不能穿越除草地以外的障碍物;游戏的玩家通过键盘控制我方坦克的前进、后退、向左、向右移动 ;对方坦克可以在窗体内自主移动,撞墙或边界后自动改变方向;我方坦克与对方坦克都可以发射子弹,双方坦克被敌方的子弹打中都会产生爆炸。双方子弹打到墙上都有爆炸产生,打中后子弹被移除,可摧毁的墙被子弹打中后也会被移除,土墙被子弹打中后也会被移除;敌方子弹或我方子弹击中我方基地及老巢,玩家输,游戏结束。当对方坦克被全部子弹打中,玩家赢,游戏结束。
二、程序设计
因为里面大部分都涉及到的是javascrip编程,所以采用面向对象的设计方法,首先在JS的文件里建立项目中的以下类:
坦克:我方坦克(玩家Tank)、敌方坦克(Tank001)。
障碍物:土墙(Wall)、钢砖(SteelWall)、水面(Water)、草地(Grass)。
子弹: 子弹(Bullet)。
老巢: 鸟(Boss)。
爆炸: 爆炸(Boom)。
三、编码实现
1、主屏幕:创建一个主屏幕,设置其宽和高,并通过鼠标点击事件让游戏的主窗体在下个窗口的屏幕上显示出来。代码如下:
<div id="game" class="star">
<div class="single" onclick="Game()"></div>
<div class="coupe" onclick="Game()"></div>
<div class="map" onclick="Game()"></div>
<img src="images/select.gif" alt="" style="position: absolute; left: 300px;top: 300px"/> </div>
<script type="text/javascript">
/*全局变量 */
var background_position = 0;//定义一个变量,赋值为0;
//进入游戏界面
function Game() {
/*清空内容区域*/
var game = document.getElementById("game");
var len = game.children.length;
for (var i = 0; i < len; i++)
{ game.removeChild(game.children[0]); //从第一个开始,每次都删除一个子节点 }
/*创建新的子节点*/
winDom = document.createElement("div");//创建一个新的div块
winDom.setAttribute("id", "windows"); //设置background属性
winDom.setAttribute("class", "background");
game.appendChild(winDom);//把background节点插入到game里面去
2、游戏地图(map):
在主窗体及游戏地图绘制的时候,如果单个画很费力,而且程序代码比较繁琐,所以采用二维数组的方法来绘制。根据窗体的大小来定义一个二维数组,数组的每一个元素值来确定绘制哪种障碍物,这里的0代表Boss老巢、1代表土墙,2代表钢砖,3代表草地、4代表水面、5就是空白的,采用这种方法方便更快捷,容易修改,设计地图。。。其代码如下:
//定义地图
// 列:13 行:10
var winDom;
var world = new Array();
var map = [
[5, 3, 3, 1, 1, 5, 5, 5, 1, 1, 3, 3, 5],
[5, 1, 1, 2, 1, 1, 1, 1, 4, 1, 1, 1, 5],
[5, 1, 4, 1, 3, 1, 1, 1, 1, 2, 1, 1, 5],
[5, 1, 3, 1, 1, 1, 3, 1, 1, 1, 3, 1, 5],
[3, 4, 1, 2, 4, 1, 1, 1, 3, 1, 1, 4, 2],
[1, 1, 2, 1, 1, 1, 2, 1, 1, 2, 1, 1, 3],
[1, 3, 4, 1, 3, 1, 1, 1, 1, 1, 3, 1, 3],
[1, 1, 2, 1, 5, 1, 1, 1, 5, 2, 2, 1, 1],
[1, 3, 3, 1, 5, 2, 2, 2, 5, 1, 2, 4, 1],
[1, 3, 3, 5, 5, 2, 0, 2, 5, 5, 5, 1, 1]
];
// 渲染地图
for(var i=0;i<map.length; i++){
for(var j=0; j< map[i].length; j++){
var cell = map[i][j]; // 1
var x = j * 60;
var y = i * 60;
var obj;
if(cell == 1){// 土墙
var obj = new Wall(winDom);
obj.setLocation(x,y);
}else if(cell == 2){// 钢砖
var obj = new SteelWall(winDom);
obj.setLocation(x, y);
}else if(cell == 3){ //草地
var obj = new Grass(winDom);
obj.setLocation(x, y);
}else if(cell == 4){ //水面
var obj = new Water(winDom);
obj.setLocation(x,y);
}else if(cell == 0){ // Boss老巢
var obj = new Boss(winDom);
obj.setLocation(x,y);
}
world.push(obj);
}
}
3、分别对项目中类的JS代码进行编写:主要是属性和行为两部分(PS:这里只写了我方坦克(玩家)的代码,其余跳过哈!!)
我方坦克(玩家)——
属性:位置、大小、颜色、生命值、步伐、方向;
行为:显示、开火、移动、爆炸、捡道具 、碰撞检测;
/*
坦克类
*/
function Tank(dom){
//属性
this.keyU = false;// 描述上键盘被按下为true,抬起为false
this.keyD = false;
this.keyL = false;
this.keyR = false;
this.oldX;
this.oldY;
this.direction = Direction.STOP// 方向
this.ptdirection = Direction.TU// 方向
this.element = document.createElement("div");
this.element.setAttribute("class","tank");
this.element.setAttribute("style","left:0;top:0;width:60px; height:60px;");
dom.appendChild(this.element);
/*设置坦克位置*/
this.setLocation = function(x , y){
this.element.style.left = x + "px";
this.element.style.top = y + "px";
}
/* 监听keydown事件 */
this.keyDown = function(e){
var key = e.keyCode;
switch (key){
case 87: this.keyU = true; break;
case 83: this.keyD = true; break;
case 65: this.keyL = true; break;
case 68: this.keyR = true; break;
}
this.localdirection();
}
/* 监听keyUp事件 */
this.keyUp = function(e){
var key = e.keyCode;
switch (key){
case 87: this.keyU = false; break;
case 83: this.keyD = false; break;
case 65: this.keyL = false; break;
case 68: this.keyR = false; break;
}
this.localdirection();
}
/* 确定坦克和炮筒的方向 */
this.localdirection = function(){
if(this.keyU && !this.keyD && !this.keyL && !this.keyR ){// 向上
this.ptdirection = this.direction = Direction.TU;
}else if(!this.keyU && this.keyD && !this.keyL && !this.keyR){//向下
this.ptdirection = this.direction = Direction.TD;
}else if(!this.keyU && !this.keyD && this.keyL && !this.keyR){//向左
this.ptdirection = this.direction = Direction.TL;
}else if(!this.keyU && !this.keyD && !this.keyL && this.keyR){//向右
this.ptdirection = this.direction = Direction.TR;
}else{
this.direction = Direction.STOP;
}
}
/* 回到之前的位置 */
this.stay = function(){
this.element.style.left = this.oldX + "px";
this.element.style.top = this.oldY + "px";
}
//行为
this.move = function(){ //左97 上119 右100 下115
var x = parseInt(this.element.style.left);
var y = parseInt(this.element.style.top);
this.oldX = x;
this.oldY = y;
switch (this.direction){// 判断坦克的方向,决定行走方向
case Direction.TU :
// 调整坦克放向图
this.element.style.backgroundPosition = -60 + "px 0px";
// 限制坦克移动范围
if( y > 0 )
this.element.style.top = y -5+"px";
break;
case Direction.TD :
this.element.style.backgroundPosition = -120+"px 0px";
if(y<540)
this.element.style.top = y+5+"px";
; break;
case Direction.TL :
this.element.style.backgroundPosition = "0px 0px";
if(x>0)
this.element.style.left = x-5+"px";
; break;
case Direction.TR :
this.element.style.backgroundPosition = -180+"px 0px";
if(x<720)
this.element.style.left = x+5+"px";
; break;
}
}
/* 碰撞检测 */
this.hit = function(obj){
if(obj != undefined){
if(obj.pass != undefined){ // 草
return false;// 如果是草,那么可以直接通过,不用做碰撞检测
}else{
var r1 = obj.getRectangle(); // 被碰撞对象的矩形区域
var r2 = this.getRectangle();// 子弹矩形区域
return r2.intersects(r1);
}
}
return false;
}
/* 发射子弹 */
this.fire = function(){
var b = new Bullet(dom);
var x;
var y;
//把炮筒方向设置给子弹
b.setDirection(this.ptdirection);
// 解决子弹的初始位置
x = parseInt(this.element.style.left) + 30 - 3;
y = parseInt(this.element.style.top);
// 计算子弹出现位置x,y
switch (this.ptdirection){
case Direction.TU :
x = parseInt(this.element.style.left) + 30 - 3;
y = parseInt(this.element.style.top);
break;
case Direction.TD :
x = parseInt(this.element.style.left) + 30 - 3;
y = parseInt(this.element.style.top) + 60;
; break;
case Direction.TL :
x = parseInt(this.element.style.left);
y = parseInt(this.element.style.top) + 30 -3;
; break;
case Direction.TR :
x = parseInt(this.element.style.left) + 60;
y = parseInt(this.element.style.top) + 30 -3;
; break;
}
b.setLocation(x ,y);
// 把子弹交给存放子弹的数组
bullets.push(b);
}
/* 获取碰撞检测对象 */
this.getRectangle = function(){
var x = parseInt(this.element.style.left);// "111px"
var y = parseInt(this.element.style.top);// "111px"
var width = parseInt(this.element.style.width);
var height = parseInt(this.element.style.height);
return new Rectangle(x, y, width, height);
}
}
障碍物: 土墙、钢砖、水面、草地——
属性:位置、大小、颜色;
行为: 碰撞检测、显示;
子弹——
属性:位置、大小、颜色、生命值、步伐、方向;
行为:显示、移动、爆炸 、碰撞检测 ;
boss(老巢)——
属性:位置、大小、颜色、生命值;
行为:爆炸 、碰撞检测 、显示;
爆炸——
属性:位置、大小、颜色;
行为:爆炸;
上面的各类的分析已经写出来了,至于JS代码就没有敲出来了,大体上是按照属性和行为分别进行具体编写的。。。
三、游戏效果图:
1、主屏幕:
2、游戏中:
四、心得体会:
这次的坦克大战游戏只做了个半成品,还有好多效果都没有能实现,没有办法,JS学得不是很懂,所以就只能凑合做点初成品。。。下面在谈谈我做的坦克大战半成品的一些小小感悟吧!!!
在坦克大战中我负责做最简单的障碍物(土墙,钢砖,Boss)这一块,老师举了个其他障碍物的范例,我就照着那代码敲,算是举一反三才模仿出来的吧。至于里面其他的东西,遇到的第一个问题算是通过键盘控制我方坦克(玩家坦克)因为JS学的很垃圾,很多地方没懂,所以完全不知道怎么下手去写,后来老师讲解了思路,再老师的带动下,勉强写完,其实算是模仿老师的代码,敲了下来,自己应该写不出来,但是能略看懂点点。后面那些复杂类的面向对象也一样,比如子弹的发射,当时我完全没有思路,老师也讲了子弹发射的思路,可我就是不会写,来回纠结了一天,直到同桌作出了我方子弹的发射,我看了代码才慢慢开始写,最后也算是逼写出来了。我觉得这就是我的问题,遇到问题还是比较毛躁,不敢下手去写。老师教导我们遇到问题时把问题记下来然后慢慢分析,先把思路想好,再下手去写,大胆想象,要注意逻辑关系。这种方法很好,但由于我的毛躁,这种方法这次没能得到很好的实施,下回一定注意,心态一定要平和。还是觉得自己还没有真正学懂JS,没有清晰的条理和思路,所以遇到问题也就只能想到方法,但是具体的代码写不出来,估计是代码练少了,掌握不熟练。。。
第一次写坦克大战还有一个致命错误就是笔误。我记得这次遇到好几次,其中就像我方坦克发不出子弹,草地和水墙画不出来都是笔误的问题,这是一个细节问题,也是一个细心问题,要引起高度的重视。项目中团队合作很重要,就像项目类的分工就是充分的证明,小组进行讨论,最后定出最终分工。在遇到问题自己想不出解决办法时一定要向他人请教,不能不问,敢问才能解决问题,才能进步。坦克大战是在飞机项目后做的第一个项目,总得还是感觉一般般,以后要更加努力,做的更好!拿什么拯救你,我的JS,只有自己多练习,勤能补拙。