javascript 模拟坦克大战游戏(html5版)

一、总结关键点和遇到的问题

    1.javascript中的继承,最好父类只提供方法共享,属性写到各自子类中,避免父类和子类的构造函数混杂。

    2.prototype模拟继承的代码,应写在所有方法定义之前,否则原型对象被改变,方法就变成了未定义,如:

Hero.prototype = new Tank (0, 0, 0);
Hero.prototype.constructor = Hero;
Hero.prototype.addLife = function(){
    this.lifetimes++;
    document.querySelector("#life").innerHTML = hero.lifetimes;
}

   3.canvas画图形时,除了画矩形,其他的都要加上 ctx.beginPath();、ctx.closePath();,否则会出现意想不到的错误。

   4.concat函数可以合并数组,或者是元素返回一个新的数组

   5.Image的src属性赋值后就会加载图片,但如果没有加载完毕就画图片,会导致失效,所以使用onload事件处理

   6.扩展Array功能,删除指定元素

//扩展 删除指定元素
        Array.prototype.deleteElement = function (obj) {
            if (obj) {
                for (var i = 0; i < this.length; i++) {
                    if (this[i] === obj) {
                        this.splice (i, 1);
                    }
                }
            }
        }

  7.定时器设置,setInterval(“fun”,1000)方法的第一个参数,可以是字符串,如"hero.say()",类似eval会去执行这串代码,所以它可以给函数带上参数,并且也指定了这个函数的运行上下文。但如果传入是函数的句柄,则不能带参数,并且不能指定上下文,除了第一种方式解决外,我用了闭包来解决这个问题

 //定时器,自行运动
    this.timer = setInterval ((function (context) {
        return function () {
            Bullet.prototype.move.call (context)
        }
    }) (this), 30);

我保存了当前的执行环境,并调用call方法手动执行。

  8.方法的功能设计,除了功能外,应该包括执行此功能的条件检测,如move,就应该包括什么情况下可以移动,移动到什么地方就不能移动了。此检测不应该放在外部。

  9.写代码时不应该去想设计或者优化的问题,先实现功能,再谈优化,或者先设计再实现。思路要清晰,别混乱,着重于一点。

  10.javascript中没有sleep的功能,可以创建一个变量作为缓冲,来达到间隔执行的目的


二、代码实现

     1.本程序分为Bomb.js,Bullet.js,Draw.js,Tank.js,index.html,img,music,

     2.最终效果




3.代码


1.index.html

<!DOCTYPE html>
<html>
<head>
    <title></title>
    <meta charset="utf-8">
    <style type="text/css">
        body {
            font: 14px "sans-serif"
        }

        #Map {
            background-color: #000000;
        }

        .show {
            float: left
        }

        #guide {
            float: left;
            width: 200px;
            height: 390px;
            margin-left: 5px;
            background: #CCCCCC;
            padding: 5px;
        }
    </style>
    <script type="text/javascript" src="Tank.js"></script>
    <script type="text/javascript" src="Bullet.js"></script>
    <script type="text/javascript" src="Bomb.js"></script>
    <script type="text/javascript" src="Draw.js"></script>

    <script type="text/javascript">
        window.onload = function () {
            //画布信息
            width = document.getElementById ('Map').width;
            height = document.getElementById ('Map').height;
            ctx = document.getElementById ('Map').getContext ('2d');
            //初始页面
            var starImg = new Image ();
            starImg.src = "img/star.jpg";
            starImg.onload = function () {
                ctx.drawImage (starImg, 0, 0, width, height);
            }

            //键盘监听 回车开始游戏
            document.body.onkeydown = function () {
                var keycode = event.keyCode;
                switch (keycode) {
                    case 13:
                        //初始化参数
                        init ()
                        //刷新页面
                        setInterval (draw, 30);
                        document.body.onkeydown = gameControl;
                        break;
                }
            }
        }

        function init () {
            //玩家和电脑
            hero = new Hero (100, 300, 0);
            enemys = [];
            for (var i = 0; i < 3; i++) {
                enemys.push (new Enemy (100 + i * 50, 0, 2));
            }
            //合并数组
            allTank = enemys.concat (hero);

            //炸弹
            Bombs = [];
            im = new Image ();
            im2 = new Image ();
            im3 = new Image ();
            im.src = "img/bomb_3.gif";
            im2.src = "img/bomb_2.gif";
            im3.src = "img/bomb_1.gif";
        }

        function gameControl () {
            var keycode = event.keyCode;
            switch (keycode) {
                case 65:
                    hero.moveLeft ();
                    break;//左
                case 83:
                    hero.moveDown ();
                    break;//下
                case 87:
                    hero.moveUp ();
                    break;//上
                case 68:
                    hero.moveRight ();
                    break;//右
                case 74:
                    hero.shot ();
                    break;
                case 49:
                    hero.addLife ()
                    break;
            }
        }

        //扩展 删除指定元素
        Array.prototype.deleteElement = function (obj) {
            if (obj) {
                for (var i = 0; i < this.length; i++) {
                    if (this[i] === obj) {
                        this.splice (i, 1);
                    }
                }
            }
        }

    </script>
</head>
<body>
<div class="show">
    <canvas id="Map" width="500px" height="400px">
    </canvas>
    <audio id="music" autoplay="autoplay">
        <source src="music/111.wav">
    </audio>
</div>
<div id="guide">

    <p>按下回车键开始游戏</p>

    <p>按下1键增加生命,默认是1</p>

    <p>剩余生命数 :<label id="life">1</label></p>

    <div id="data">

    </div>
</div>
</body>
</html>

2.Draw.js

/**
 * Created by Alane on 14-3-18.
 */

function draw(){
    //检测子弹和坦克生死
    checkDead();
    //清空画布
    ctx.clearRect(0,0,500,400);
    //画玩家
    if(!hero.isdead){
        drawTank(hero);
    }else{
        hero.cutLife();
    }
    //画敌人坦克
    for (var i = 0; i < enemys.length; i++) {
        drawTank(enemys[i]);
    }
    //画敌人子弹
    for(var j=0;j<enemys.length;j++){
        var temp = enemys[j].bulletsList;
        for (var i = 0; i < temp.length; i++) {
            drawBullet(temp[i]);
        }
    }
    //画玩家子弹
    var temp = hero.bulletsList;
    for (var i = 0; i < temp.length; i++) {
        drawBullet(temp[i]);
    }

    //画炸弹
    for(var i=0;i<Bombs.length;i++){
        drawBown(Bombs[i]);
    }

}

function drawTank(tank){
    var x = tank.x;
    var y = tank.y;
    ctx.fillStyle = tank.color;

    if(tank.direct == 0 || tank.direct ==2){
        ctx.fillRect(x, y, 5,30);
        ctx.fillRect(x+15, y, 5,30);

        ctx.fillRect(x+6, y+8, 8,15);

        ctx.strokeStyle = tank.color;
        ctx.lineWidth = '1.5';
        if(tank.direct == 0){
            ctx.beginPath();
            ctx.moveTo(x+10,y-2);
            ctx.lineTo(x+10,y+8);
            ctx.closePath();
        }else{
            ctx.beginPath();
            ctx.moveTo(x+10,y+24);
            ctx.lineTo(x+10,y+32);
            ctx.closePath();
        }

        ctx.stroke();
    }else{
        ctx.fillRect(x, y, 30,5);
        ctx.fillRect(x, y+15, 30,5);

        ctx.fillRect(x+8, y+6, 15,8);

        ctx.strokeStyle = '#FF0000';
        ctx.lineWidth = '1.5';
        if(tank.direct == 3){
            ctx.beginPath();
            ctx.moveTo(x-2,y+10);
            ctx.lineTo(x+8,y+10);
            ctx.closePath();
        }else{
            ctx.beginPath();
            ctx.moveTo(x+24,y+10);
            ctx.lineTo(x+32,y+10);
            ctx.closePath();
        }

        ctx.stroke();
    }

}
function drawBullet(bullet){
    ctx.fillStyle = bullet.color;
    ctx.beginPath();
    ctx.arc(bullet.x,bullet.y,2,360,true);
    ctx.closePath();
    ctx.fill();
}

function drawBown (obj){
    if(obj.life>8){
        ctx.drawImage(im,obj.x,obj.y,50,50);
    }else if(obj.life>4){
        ctx.drawImage(im2,obj.x,obj.y,50,50);
    }else{
        ctx.drawImage(im3,obj.x,obj.y,50,50);
    }

    obj.lifeDown();
    if(obj.life<=0){
        Bombs.deleteElement(obj);
    }
}

function checkDead(){
    //检测敌人子弹生死
    for(var j=0;j<enemys.length;j++){
        var temp = enemys[j].bulletsList;
        for (var i = 0; i < temp.length; i++) {
            var o = temp[i];
            if(o.isdead){
                temp.deleteElement(o);
            }
        }
    }
    //检测玩家子弹生死
    var temp = hero.bulletsList;
    for (var i = 0; i < temp.length; i++) {
        var o = temp[i];
        if(o.isdead){
            temp.deleteElement(o);
        }
    }

    //检测敌人坦克生死
    for (var i = 0; i < enemys.length; i++) {
        var o = enemys[i];
        if(o.isdead){
            enemys.deleteElement(o);
        }
    }
}

Bomb.js

/**
 * Created by Alane on 14-3-18.
 */
function Bomb(x,y){
    this.life = 12;
    this.x = x;
    this.y = y;
}
Bomb.prototype.lifeDown = function(){
    this.life--;
}

Tank.js

/**
 * Created by Alane on 14-3-7.
 */
/**
 * direct 0 上
 *        1 右
 *        2 下
 *        3 左
 * @param x
 * @param y
 * @param direct
 * @constructor
 */
//******************************************************************************************/
//坦克父类
function Tank (x, y, direct) {
    this.speed = 2;

}
Tank.prototype.moveUp = function () {
    //边界检测
    if (this.y < 0) {
        //换方向
        this.changeDirect ();
        return;
    }
    this.y -= this.speed;
    this.direct = 0;

}
Tank.prototype.moveDown = function () {
    if (this.y > height - 30) {
        this.changeDirect ();
        return;
    }
    this.y += this.speed;
    this.direct = 2;
}
Tank.prototype.moveLeft = function () {
    if (this.x < 0) {
        this.changeDirect ();
        return;
    }
    this.x -= this.speed;
    this.direct = 3;

}
Tank.prototype.moveRight = function () {
    if (this.x > width - 30) {
        this.changeDirect ();
        return;
    }
    this.x += this.speed;
    this.direct = 1;

}

//变换方向
Tank.prototype.changeDirect = function () {
    while (true) {
        var temp = Math.round (Math.random () * 3);
        if (this.direct != temp) {
            this.direct = temp;
            break;
        }
    }
    //alert("x="+this.x+" y="+this.y+" direct="+this.direct)
}

//射击子弹
Tank.prototype.shot = function () {
    if(this.isdead){
        return;
    }
    if (this.bulletsList.length < this.maxBulletSize) {
        //新建子弹
        var bullet = null;
        switch (this.direct) {
            case 0:
                bullet = new Bullet (this.x + 10, this.y - 2, 0, this.color);
                break;
            case 1:
                bullet = new Bullet (this.x + 32, this.y + 10, 1, this.color);
                break;
            case 2:
                bullet = new Bullet (this.x + 10, this.y + 32, 2, this.color);
                break;
            case 3:
                bullet = new Bullet (this.x - 2, this.y + 10, 3, this.color);
                break;
        }
        //放入弹夹
        this.bulletsList.push (bullet);
    }
}
//******************************************************************************************/
//玩家
function Hero (x, y, direct) {
    this.lifetimes = 5;
    this.isdead = false;
    this.color = '#FF0000';
    this.x = x;
    this.y = y;
    this.direct = direct;
    this.bulletsList = [];
    this.maxBulletSize = 10;
    this.newlife = null;
}
Hero.prototype = new Tank (0, 0, 0);
Hero.prototype.constructor = Hero;
Hero.prototype.addLife = function(){
    this.lifetimes++;
    document.querySelector("#life").innerHTML = hero.lifetimes;
}
Hero.prototype.cutLife = function(){
    if(this.lifetimes>=1 && !this.newlife){
        this.lifetimes--;
        this.newlife = setTimeout("hero.newLife()",2000);
    }
}
Hero.prototype.newLife = function(){
    this.isdead = false;
    clearTimeout(hero.newlife);
    hero.newlife = null;
    document.querySelector("#life").innerHTML = hero.lifetimes;
}


//******************************************************************************************/
//敌人坦克
function Enemy (x, y, direct) {
    this.isdead = false;
    this.color = 'blue';
    this.x = x;
    this.y = y;
    this.direct = direct;
    this.bulletsList = [];
    this.maxBulletSize = 1;


    //定时器,自动移动
    this.timer1 = setInterval ((function (context) {
        return function () {
            //移动
            Enemy.prototype.move.call (context);
        }
    }) (this), 30);

    //定时器,射击
    this.timer2 = setInterval ((function (context) {
        return function () {
            //射击
            Tank.prototype.shot.call (context);
        }
    }) (this), 2000);

    //定时器,变换方向
    this.timer3 = setInterval ((function (context) {
        return function () {
            //射击
            Tank.prototype.changeDirect.call (context);
        }
    }) (this), 3000);
}

Enemy.prototype = new Tank (0, 0, 0);
Enemy.prototype.constructor = Enemy;
Enemy.prototype.move = function () {
    switch (this.direct) {
        case 0:
            this.moveUp ();
            break;
        case 1:
            this.moveRight ();
            break;
        case 2:
            this.moveDown ();
            break;
        case 3:
            this.moveLeft ();
            break;
    }
}

Bullet.js

/**
 * Created by Alane on 14-3-11.
 */
function Bullet (x, y, direct, color) {
    this.isdead = false;
    this.x = x;
    this.y = y;
    this.direct = direct;
    this.speed = 4;
    this.color = color;
    //定时器,自行运动
    this.timer = setInterval ((function (context) {
        return function () {
            Bullet.prototype.move.call (context)
        }
    }) (this), 30);
}
Bullet.prototype.move = function () {
    switch (this.direct) {
        case 0:
            this.y -= this.speed;
            break;
        case 1:
            this.x += this.speed;
            break;
        case 2:
            this.y += this.speed;
            break;
        case 3:
            this.x -= this.speed;
            break;
    }

    //边界检测
    if (this.y < 0 || this.x > width || this.y > height || this.x < 0) {
        clearInterval (this.timer);
        this.isdead = true;
    }

    //碰撞检测  检测敌人坦克
    for(var i=0;i<allTank.length;i++){
        var temp = allTank[i];
        if(temp.isdead){
            continue;
        }
        switch (temp.direct){
            case 0:
            case 2:if(this.x>temp.x && this.x<temp.x+20 && this.y>temp.y&& this.y<temp.y+30){
                if(this.color == temp.color){
                    break;
                }
                Bombs.push(new Bomb(temp.x-10,temp.y-10));
                clearInterval (this.timer);
                this.isdead = true;
                temp.isdead = true;
            }break
            case 1:
            case 3:if(this.x>temp.x && this.x<temp.x+30 && this.y>temp.y&& this.y<temp.y+20){
                if(this.color == temp.color){
                    break;
                }
                Bombs.push(new Bomb(temp.x-10,temp.y-10));
                clearInterval (this.timer);
                this.isdead = true;
                temp.isdead = true;
            }break;
        }
    }

}

完整程序可以到我网盘下载: 源码

提取码:ix9t


  • 0
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值