Bombman是我仿造红白机上经典游戏爆破小人,用Canvas制作的一款网页版单机游戏, 自我感觉还是有一定的可玩性。

本游戏的胜利条件是用雷消灭所有怪物,但被怪物即使是擦边碰到或是炸弹火焰炸到就算失败。

操作方法是用方向键或是aswd健来控制人物移动,用空格键放雷炸怪物,吃红五星则雷的爆炸范围增加。

点此下载程序1.04版,用浏览器打开,并点“开始”按钮开始游戏,游戏结束后可以点“再来一次”按钮重新玩。

程序的1.06版本由此下载,此版本增加了怪物跟随主角的功能。

本作Github url:https://github.com/horn19782016/Bombman

有空可以玩玩,注意不要操之过急哦。

图例:

近千行源码:

<!DOCTYPE html>
<html lang="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<head>
     <title>炸弹人v1.06 19.3.14 6:34 by:逆火狂飙 horn19782016@163.com</title>
     <!--1.06版本启用了怪物跟随英雄的功能 -->

     <style>
     #canvas{
        background:#ffffff;
        cursor:pointer;
        margin-left:10px;
        margin-top:10px;
        -webkit-box-shadow:3px 3px 6px rgba(0,0,0,0.5);
        -moz-box-shadow:3px 3px 6px rgba(0,0,0,0.5);
        box-shadow:3px 3px 6px rgba(0,0,0,0.5);
     }

     #controls{
        margin-top:10px;
        margin-left:15px;
     }
     </style>

    </head>

     <body onload="init()">
        <div id="controls">
            <input id='animateBtn' type='button' value='开始'/>
            <input id='playAgainBtn' type='button' value='再来一盘' style='visibility:hidden'/>
        </div>

        <canvas id="canvas" width="160px" height="160px" >
            出现文字表示你的浏览器不支持HTML5
        </canvas>
     </body>
</html>
<script type="text/javascript">
<!--

// 暂停/开始游戏
animateBtn.onclick=function(e){
    paused=! paused;

    if(paused){
        animateBtn.value="开始";
        screenMsg="Paused!";
    }else{
        animateBtn.value="停止";
        window.requestAnimationFrame(animate);
    }
}

// 重来游戏
playAgainBtn.onclick=function(e){
    init();
    playAgainBtn.style.visibility="hidden";
    animateBtn.value="停止";
    paused=false;
    window.requestAnimationFrame(animate);
}

var paused=true;// 是否暂停
var BL=32;        // Block length,边长
var ctx;        // 绘图环境
var terrain;    // 地形
var hero;        // 英雄/主角
var monsterMng;    // 怪物管理类
var bombs;        // 炸弹数组
var fires;        // 火焰数组
var screenMsg;    // 显示在屏幕中央的话语
var bonusMng;     // 奖励管理类

function init(){
    // init Canvas
    var canvas=document.getElementById('canvas');
    canvas.width =40*BL;
    canvas.height=18*BL;
    ctx=canvas.getContext('2d');

    terrain=new Terrain();
    hero=new Hero();
    monsterMng=new MonsterMng();
    bombs=new Array();
    fires=new Array();

    bonusMng=new BonusMng();

    // 响应键盘事件
    canvas.addEventListener('keydown', doKeyDown, true);
    canvas.focus();
    window.addEventListener('keydown', doKeyDown, true);
};

//------------------------------------
// 响应键盘事件
//------------------------------------
function doKeyDown(e) {
    var keyID = e.keyCode ? e.keyCode :e.which;
    if(keyID === 38 || keyID === 87)  { // up arrow and W
        hero.move('up');
        e.preventDefault();
    }
    if(keyID === 39 || keyID === 68)  { // right arrow and D
        hero.move('right');
        e.preventDefault();
    }
    if(keyID === 40 || keyID === 83)  { // down arrow and S
        hero.move('down');
        e.preventDefault();
    }
    if(keyID === 37 || keyID === 65)  { // left arrow and A
        hero.move('left');
        e.preventDefault();
    }

    if(keyID === 32 )  { // SpaceBar
        var b=new Bomb(hero.x,hero.y,hero.explosionRange);
        bombs.push(b);
        //console.log('arrows.length='+arrows.length);
        e.preventDefault();
    }
}

//------------------------------------
// 更新各实例状态
//------------------------------------
function update(){
    monsterMng.move();

    var heroX=parseInt(hero.x/BL,10);
    var heroY=parseInt(hero.y/BL,10);

    // 看英雄碰到怪物没
    if(monsterMng.isTouched(heroX,heroY)){
        hero.setDead();
    }

    // 计算爆炸范围
    var explosions=[];
    for(var i=0;i<fires.length;i++){
        var f=fires[i];

        if(f.lifeTime>0){
            explosions.push(new Point(f.x,f.y));
        }
    }

    // 爆炸对怪物的伤害
    monsterMng.makeDeadInRange(explosions);

    // 爆炸对英雄的伤害
    hero.makeDeadInRange(explosions);

    // 奖励
    bonusMng.enlargeExplosionRange(hero);

    // 让怪物吃奖励提升速度
    var aliveMonsters=monsterMng.getAliveMonsters();
    bonusMng.raiseSpeed(aliveMonsters);

    // 游戏结束条件
    if(hero.alive==false){ // 英雄死亡
        paused=true;
        screenMsg="You lost!";
        animateBtn.value="开始";
        playAgainBtn.style.visibility="visible";
    }

    // 游戏胜利条件
    if(monsterMng.noMonster() && hero.alive==true){ // 怪物全清除 及 英雄存活
        paused=true;
        screenMsg="You win!";
        animateBtn.value="开始";
        playAgainBtn.style.visibility="visible";
    }
}

//------------------------------------
// 在ctx里画出各实例
//------------------------------------
function draw(){
    // Clear Canvas
    ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height);
    ctx.fillStyle="#ffffff";
    ctx.fillRect(0,0,ctx.canvas.width,ctx.canvas.height);

    terrain.paint(ctx);    // 画地形
    bonusMng.paint(ctx);// 画奖励

    monsterMng.paint(ctx);
    hero.paint(ctx);    // 画英雄

    // 画炸弹
    for(var i=0;i<bombs.length;i++){
        var b=bombs[i];
        b.paint(ctx);
    }

    // 画火焰
    for(var i=0;i<fires.length;i++){
        var b=fires[i];
        b.paint(ctx);
    }
}

//------------------------------------
// 运行动画
//------------------------------------
function animate(){
    if(!paused){
        update();
        draw();
        window.requestAnimationFrame(animate); /// 让浏览器自行决定帧速率
    }else{
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        ctx.font="bold 48px 宋体";
        ctx.fillStyle='Red';
        ctx.fillText(screenMsg,ctx.canvas.width/2,ctx.canvas.height/2);
    }
}

//------------------------------------
//  常规函数:角度得到弧度
//------------------------------------
function getRad(degree){
    return degree/180*Math.PI;
}

//------------------------------------------------------------
// 常规函数:得到0到N(包括N)的随机整数
// 如果要得到数组里面的一个要把数组长度减一
//------------------------------------------------------------
function getRndFromZeroToN(n){
    return Math.floor(Math.random()*(n+1));
}

//------------------------------------------------------------
// 常规函数:得到lowerLimit到upperlimit(包括端点值)的随机整数
//------------------------------------------------------------
function getRndBetween(lowerLimit,upperlimit){
    return Math.floor(Math.random()*(upperlimit-lowerLimit+1))+lowerLimit;
}

//---------------------------------------------------地形类定义开始------------------------------------------------------------------->>
Terrain=function(){
    this.files=["road.png",           //  0
                "tree.png",            //  1
                "farmerHouse.png",    //  2
                "twoBuilding.png",    //  3
                "threeBuilding.png",//  4
                "bank.png",            //  5
                "villa.png",        //  6
                "blackWhiteRoad.png",];        //  7

    /*this.maps=new Array(
        [0,0,0,0,0],
        [0,0,1,0,0],
        [0,0,0,0,0],
        [0,2,0,0,0],
        [0,0,0,3,0],
    );*/
    this.maps=new Array(
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,],
        [0,1,2,1,2,1,0,1,2,1,0,0,1,1,1,1,1,0,],
        [0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,3,1,0,],
        [0,1,2,1,2,1,0,1,2,1,0,0,5,0,0,3,1,0,],
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,1,0,],
        [0,1,2,1,2,1,0,1,2,1,0,0,3,3,3,3,1,0,],
        [0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,],
        [0,1,2,1,2,1,0,1,2,1,0,0,0,0,0,0,0,0,],
        [0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,],
        [0,1,2,1,2,1,0,1,2,1,0,1,4,4,4,4,1,0,],
        [0,0,0,0,0,0,0,0,0,0,0,1,4,0,0,0,1,0,],
        [0,1,2,1,2,1,0,1,2,1,0,1,5,0,0,0,0,0,],
        [0,0,0,0,0,0,0,0,0,0,0,1,4,0,0,0,1,0,],
        [0,1,2,1,2,1,0,1,2,1,0,1,4,4,4,4,1,0,],
        [0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,],
        [0,1,2,1,2,1,0,1,2,1,0,0,0,0,0,0,0,0,],
        [0,0,0,0,0,0,0,0,0,0,0,1,6,1,6,1,6,0,],
        [0,1,2,1,2,1,0,1,2,1,0,6,0,0,0,0,1,0,],
        [0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,0,6,0,],
        [0,1,2,1,2,1,0,1,2,1,0,0,0,5,0,0,1,0,],
        [0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,0,6,0,],
        [0,1,2,1,2,1,0,1,2,1,0,6,0,0,0,0,1,0,],
        [0,0,0,0,0,0,0,0,0,0,0,1,6,1,6,1,6,0,],
        [0,1,2,1,2,1,0,1,2,1,0,0,0,0,0,0,0,0,],
        [0,0,0,0,0,0,0,0,0,0,0,1,6,1,6,1,0,0,],
        [0,1,2,1,2,1,0,1,2,1,0,0,0,0,0,0,0,0,],
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,],
        [0,1,2,1,2,1,0,1,2,1,6,1,0,0,0,0,0,0,],
        [0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,],
        [0,1,2,1,2,1,0,1,2,1,6,1,0,4,0,0,0,0,],
        [0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,3,0,0,],
        [0,1,2,1,2,1,0,1,2,1,6,1,0,0,0,4,0,0,],
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,],
        [0,1,2,1,2,1,0,1,2,1,6,1,0,0,0,0,0,0,],
        [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,],
        [0,1,2,1,2,1,0,1,2,1,0,0,0,0,0,0,0,0,],
        [0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,2,0,0,],
        [0,1,2,1,2,1,0,1,2,1,0,2,2,2,0,2,2,2,],
        [0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,2,],
        [0,1,2,1,2,1,0,1,2,1,0,2,0,0,0,0,0,2,],
    );
}
Terrain.prototype={
    files:[],
    maps:[],

    // method
    paint:function(ctx){
        for(var i=0;i<this.maps.length;i++){
            var arr=this.maps[i];

            for(var j=0;j<arr.length;j++){
                var value=arr[j];

                var img=new Image();
                img.src=this.files[value];

                ctx.drawImage(img,i*BL,j*BL);
            }
        }
    },

    // 是否有障碍
    hasObstacle:function(i,j){
        if(i<0 || i>=this.maps.length){
            return true;
        }
        var arr=this.maps[i];
        if(j<0 || j>=arr.length){
            return true;
        }
        var value=arr[j];
        if(value==0){
            return false;
        }else{
            return true;
        }
    },

    getValue:function(i,j){
        if(i<0 || i>=this.maps.length){
            return undefined;
        }
        var arr=this.maps[i];
        if(j<0 || j>=arr.length){
            return undefined;
        }
        var value=arr[j];
        return value;
    },

    setValue:function(i,j,value){
        if(i<0 || i>=this.maps.length){
            return undefined;
        }
        var arr=this.maps[i];
        if(j<0 || j>=arr.length){
            return undefined;
        }
        arr[j]=value;
    },

    // 取得空点数组
    getEmptyPoints:function(count){
        var allEmptyPoints=[];

        for(var i=0;i<this.maps.length;i++){
            var arr=this.maps[i];

            for(var j=0;j<arr.length;j++){
                var value=arr[j];

                if(value==0){
                    allEmptyPoints.push(new Point(i*BL,j*BL));
                }
            }
        }

        var retval=[];
        //var n=allEmptyPoints.length;
        //console.log("allEmptyPoints.length="+allEmptyPoints.length);
        for(var i=0;i<count;i++){
            var seed=this.getRndFromZeroToN(allEmptyPoints.length-1);
            var p=allEmptyPoints[seed];
            retval.push(p);
        }

        return retval;
    },

    //------------------------------------------------------------
    // 得到0到N(包括N)的随机整数
    // 如果要得到数组里面的一个要把数组长度减一
    //------------------------------------------------------------
    getRndFromZeroToN:function(n){
        return Math.floor(Math.random()*(n+1));
    },
}
//---------------------------------------------------地形类定义结束-------------------------------------------------------------------<<

//---------------------------------------------------英雄类定义开始------------------------------------------------------------------->>
Hero=function(){
    this.pngs=[
        {left:0,top:10,width:40,height:40},
        {left:0,top:68,width:40,height:40},
        {left:0,top:123,width:40,height:40},
        {left:0,top:180,width:40,height:40},
    ];
}
Hero.prototype={
    pngs:[],
    x:19*BL,
    y:10*BL,
    xTo:19*BL,
    yTo:10*BL,
    step:BL,
    direction:'up',        // 面向
    alive:true,            // 是否活着
    explosionRange:4,    // 爆炸范围

    setDead:function(){
        //console.log('Hero dead');
        this.alive=false;
    },

    paint:function(ctx){
        var img=new Image();
        img.src='bowman.png';

        var index=0;
        if(this.direction=='up'){
            index=3;
        }
        if(this.direction=='down'){
            index=0;
        }
        if(this.direction=='left'){
            index=1;
        }
        if(this.direction=='right'){
            index=2;
        }
        var pos=this.pngs[index];
        ctx.drawImage(img,pos.left,pos.top,pos.width,pos.height,this.x,this.y,32,32);
    },

    move:function(direction){
        this.direction=direction;

        if(this.direction=='up'){
            this.yTo-=this.step;
        }
        if(this.direction=='down'){
            this.yTo+=this.step;
        }
        if(this.direction=='left'){
            this.xTo-=this.step;
        }
        if(this.direction=='right'){
            this.xTo+=this.step;
        }

        if(terrain.getValue(this.xTo/this.step,this.yTo/this.step)==0){
            this.x=this.xTo;
            this.y=this.yTo;
        }else{
            this.xTo=this.x;
            this.yTo=this.y;
        }
    },

    // 爆炸火焰一样对英雄造成伤害
    makeDeadInRange:function(explosions){
       for(var j=0;j<explosions.length;j++){
            var point=explosions[j];

            //  爆炸范围判断符合实际情况
            var xx=Math.pow((this.x-point.x),2);
            var yy=Math.pow((this.y-point.y),2);
            var distance=Math.sqrt(xx+ yy);
            if(distance<BL/2){
                this.setDead();
            }
       }
    },
}
//---------------------------------------------------英雄类定义结束-------------------------------------------------------------------<<

//---------------------------------------------------点类定义开始------------------------------------------------------------------->>
Point=function(x,y){
    this.x=x;
    this.y=y;
}
Point.prototype={
    x:0,            // 横坐标
    y:0,            // 纵坐标
}
//---------------------------------------------------点类定义结束-------------------------------------------------------------------<<

//---------------------------------------------------怪物类定义开始------------------------------------------------------------------->>
Monster=function(x,y){
    this.x=x;
    this.y=y;
    steps=[1,2,4,8];

    var rndSeed=this.getRndFromZeroToN(steps.length-1);

    this.step=steps[rndSeed];

    this.files=["monster1.png",               //  0
                "monster1Dead.png",            //  1
                "monster2.png",               //  2
                "monster2Dead.png",            //  3
                "monster3.png",               //  4
                "monster3Dead.png",            //  5
                "monster4.png",               //  6
                "monster4Dead.png",            //  7
                ];
}
Monster.prototype={
    x:0,            // 横坐标,乘以32等于真正的横坐标
    y:0,            // 纵坐标,乘以32等于真正的纵坐标
    step:1,            // 行走速度
    steps:[],        // 行走速度数组
    direction:'',    // 方向
    files:[],        // 显示图片
    alive:true,        // 是否活着
    thinking:true,    // 是否在找路
    targetPoint:[],

    // 设置死亡
    setDead:function(){
        this.alive=false;
    },

    // 成倍提升速度
    doubleStep:function(){
        this.step*=2;

        if(this.step>=512){
            this.step=512;
        }
    },

    // 移动
    move:function(){
        if(this.alive){
            if(this.thinking==true){
                // 思考下一步往哪里走
                targetPoint=null;

                // 选择偏向英雄的方向
                var xDiff=this.x-hero.x;
                var yDiff=this.y-hero.y;

                var emptyPlaces=[];
                var monsterX=parseInt(this.x/BL,10);
                var monsterY=parseInt(this.y/BL,10);
                var pointsTmp=[];

                if(xDiff>=0){
                    // 左边方格
                    for(var i=1;i<=this.step;i++){
                        var hasObstacle=terrain.hasObstacle(monsterX-i,monsterY);
                        if(hasObstacle==false){
                            pointsTmp.push(new Point(monsterX-i,monsterY));
                        }else{
                            break;
                        }
                    }
                    if(pointsTmp.length>0){
                        emptyPlaces.push(pointsTmp[pointsTmp.length-1]);
                        pointsTmp=[];
                    }
                }

                if(xDiff<0){
                    // 右边方格
                    for(var i=1;i<=this.step;i++){
                        var hasObstacle=terrain.hasObstacle(monsterX+i,monsterY);
                        if(hasObstacle==false){
                            pointsTmp.push(new Point(monsterX+i,monsterY));
                        }else{
                            break;
                        }
                    }
                    if(pointsTmp.length>0){
                        emptyPlaces.push(pointsTmp[pointsTmp.length-1]);
                        pointsTmp=[];
                    }
                }

                if(yDiff>=0){
                    // 上边方格
                    for(var i=1;i<=this.step;i++){
                        var hasObstacle=terrain.hasObstacle(monsterX,monsterY-i);
                        if(hasObstacle==false){
                            pointsTmp.push(new Point(monsterX,monsterY-1));
                        }else{
                            break;
                        }
                    }
                    if(pointsTmp.length>0){
                        emptyPlaces.push(pointsTmp[pointsTmp.length-1]);
                        pointsTmp=[];
                    }
                }

                if(yDiff<0){
                    // 下边方格
                    for(var i=1;i<=this.step;i++){
                        var hasObstacle=terrain.hasObstacle(monsterX,monsterY+i);
                        if(hasObstacle==false){
                            pointsTmp.push(new Point(monsterX,monsterY+1));
                        }else{
                            break;
                        }
                    }
                    if(pointsTmp.length>0){
                        emptyPlaces.push(pointsTmp[pointsTmp.length-1]);
                        pointsTmp=[];
                    }
                }

                //  下面是随机选择方向
                var rndSeed=this.getRndFromZeroToN(emptyPlaces.length-1);
                this.targetPoint=emptyPlaces[rndSeed];

                //console.log(targetPoint);

                if(this.targetPoint!=null){
                    //console.log(this.targetPoint);
                    this.thinking=false;
                }
            }else{
                // 思考好了就走
                var targetX=this.targetPoint.x*BL;
                var targetY=this.targetPoint.y*BL;

                //console.log(this.targetPoint,this.x,this.y);

                if(targetX==this.x && targetY<this.y){
                    this.y-=this.step;
                }
                if(targetX==this.x && targetY>this.y){
                    this.y+=this.step;
                }
                if(targetX<this.x && targetY==this.y){
                    this.x-=this.step;
                }
                if(targetX>this.x && targetY==this.y){
                    this.x+=this.step;
                }
                if(targetX==this.x && targetY==this.y){
                    this.thinking=true;
                }
            }

        }
    },

    //------------------------------------------------------------
    // 得到0到N(包括N)的随机整数
    // 如果要得到数组里面的一个要把数组长度减一
    //------------------------------------------------------------
    getRndFromZeroToN:function(n){
        return Math.floor(Math.random()*(n+1));
    },

    // 画怪物
    paint:function(ctx){
        var img=new Image();

        if(this.alive==true && this.step==1){
            img.src=this.files[0];
        }
        if(this.alive==false && this.step==1){
            img.src=this.files[1];
        }
        if(this.alive==true && this.step==2){
            img.src=this.files[2];
        }
        if(this.alive==false && this.step==2){
            img.src=this.files[3];
        }
        if(this.alive==true && this.step==4){
            img.src=this.files[4];
        }
        if(this.alive==false && this.step==4){
            img.src=this.files[5];
        }
        if(this.alive==true && this.step>=8){
            img.src=this.files[6];
        }
        if(this.alive==false && this.step>=8){
            img.src=this.files[7];
        }

        ctx.drawImage(img,this.x,this.y);
    },

}
//---------------------------------------------------怪物类定义结束-------------------------------------------------------------------<<

//---------------------------------------------------怪物管理类定义开始------------------------------------------------------------------->>
MonsterMng=function(){
    this.addMonster(9);
}
MonsterMng.prototype={
    monsters:[],

    // 生成怪物,测试用
    generateMonsters:function(){
        this.monsters=new Array();
        this.monsters.push(new Monster(1*BL,1*BL,3));
        this.monsters.push(new Monster(1*BL,10*BL,3));
        this.monsters.push(new Monster(16*BL,16*BL,2));
        this.monsters.push(new Monster(10*BL,13*BL,5));
        this.monsters.push(new Monster(10*BL,10*BL,6));
        this.monsters.push(new Monster(24*BL,1*BL,1));
    },

    // 画怪物
    paint:function(ctx){

        for(var i=0;i<this.monsters.length;i++){
            var m=this.monsters[i];
            m.paint(ctx);
        }

        // 写怪物数目
        ctx.textAlign = 'left';
        ctx.textBaseline = 'middle';
        ctx.font="bold 24px 宋体";
        ctx.fillStyle='maroon';
        ctx.fillText("Monsters:"+this.getAliveMonsterNum(),ctx.canvas.width-5*BL,BL/2);
    },

    // 让怪物移动
    move:function(ctx){
        for(var i=0;i<this.monsters.length;i++){
            var m=this.monsters[i];
            m.move();
        }
    },

    // 得到活着的怪兽数量
    getAliveMonsterNum:function(){
        var count=0;

        for(var i=0;i<this.monsters.length;i++){
            var m=this.monsters[i];
            if(m.alive){
                count++;
            }
        }

        return count;
    },

    // 得到活着的怪兽数组
    getAliveMonsters:function(ctx){
        var arr=[];

        for(var i=0;i<this.monsters.length;i++){
            var m=this.monsters[i];
            if(m.alive==true){
                arr.push(m);
            }
        }

        return arr;
    },

    // 判断怪物还是否存在
    hasMonster:function(){
        var count=this.getAliveMonsterNum();

        return count==0;
    },

    // 看英雄是否碰到怪物
    isTouched:function(heroX,heroY){
        for(var i=0;i<this.monsters.length;i++){
            var monster=this.monsters[i];

            var monsterX=parseInt(monster.x/BL,10);
            var monsterY=parseInt(monster.y/BL,10);

            //console.log('isTouched',heroX,heroY,monsterX,monsterY);
            if(heroX==monsterX && heroY==monsterY && monster.alive==true){
                //console.log('touched',heroX,heroY,monsterX,monsterY);
                return true;
            }
        }

        return false;
    },

    // 看有没有怪物了
    noMonster:function(heroX,heroY){
        var count=0;

        for(var i=0;i<this.monsters.length;i++){
            var monster=this.monsters[i];

            if(monster.alive){
                count++;
            }
        }

        return count==0;
    },

    // 将爆炸范围内的怪物致死
    makeDeadInRange:function(explosions){
        //console.log(explosions);
        for(var i=0;i<this.monsters.length;i++){
            var monster=this.monsters[i];

            if(monster.alive){
               for(var j=0;j<explosions.length;j++){
                    var point=explosions[j];
                    //console.log(monster.x,monster.y,point.x,point.y);
                    /*if(monster.x==point.x && monster.y==point.y){  // 精确判断不符合实际情况
                        monster.setDead();
                    }*/

                    // 范围判断符合实际情况
                    var xx=Math.pow((monster.x-point.x),2);
                    var yy=Math.pow((monster.y-point.y),2);
                    var distance=Math.sqrt(xx+ yy);
                    if(distance<BL/2){
                        monster.setDead();
                    }
               }
            }
        }
    },

    // 添加怪物
    addMonster:function(n){
        this.monsters=new Array();
        var points=terrain.getEmptyPoints(n);

        for(var i=0;i<points.length;i++){
            var p=points[i];

            this.monsters.push(new Monster(p.x,p.y));
            //this.bonusArr.push(new Bonus(p.x,p.y));
        }

        console.log(this.monsters);
    },
}
//---------------------------------------------------怪物管理类定义结束-------------------------------------------------------------------<<

//---------------------------------------------------炸弹类定义开始------------------------------------------------------------------->>
Bomb=function(x,y,explosionRange){
    this.x=x;
    this.y=y;

    // 放下炸弹,让其起障碍作用
    var bombX=parseInt(this.x/BL,10);
    var bombY=parseInt(this.y/BL,10);
    terrain.setValue(bombX,bombY,7);

    this.explosionRange=explosionRange;
    this.files=['bomb1.png','bomb2.png','bomb3.png','bomb4.png'];
}
Bomb.prototype={
    x:0,            // 横坐标
    y:0,            // 纵坐标
    files:[],        // 表现炸弹的图片
    level:0,        // 等级,越高越解决爆炸
    exploded:false, // 是否爆炸
    explosionRange:1,// 爆炸范围

    // 画炸弹
    paint:function(ctx){
        if(this.exploded==false){
            var img=new Image();
            this.level+=0.75;

            if(this.level<20){
                img.src=this.files[0];
            }else if(this.level>=20 && this.level<40){
                img.src=this.files[1];
            }else if(this.level>=40 && this.level<60){
                img.src=this.files[2];
            }else if(this.level>=60 && this.level<80){
                img.src=this.files[3];
            }else if(this.level>=80 && this.level<100){
                img.src=this.files[4];
            }else if(this.level>=100){
                // 起爆
                this.exploded=true;

                // 爆炸完毕,让其恢复通行
                var bombX=parseInt(this.x/BL,10);
                var bombY=parseInt(this.y/BL,10);
                terrain.setValue(bombX,bombY,0);

                // 创建火焰对象
                this.makeFire();
            }

            ctx.drawImage(img,this.x,this.y);
        }
    },

    // 制造火焰
    makeFire:function(){
        var emptyPlaces=[];
        var bombX=parseInt(this.x/BL,10);
        var bombY=parseInt(this.y/BL,10);
        //console.log(bombX,bombY);

        // Left
        for(var i=1;i<=this.explosionRange;i++){
            var hasObstacle=terrain.hasObstacle(bombX-i,bombY);
            if(hasObstacle==false){
                emptyPlaces.push(new Point(bombX-i,bombY));
            }else{
                break;
            }
        }

        // Right
        for(var i=1;i<=this.explosionRange;i++){
            var hasObstacle=terrain.hasObstacle(bombX+i,bombY);
            if(hasObstacle==false){
                emptyPlaces.push(new Point(bombX+i,bombY));
            }else{
                break;
            }
        }

        // Up
        for(var i=1;i<=this.explosionRange;i++){
            var hasObstacle=terrain.hasObstacle(bombX,bombY-i);
            if(hasObstacle==false){
                emptyPlaces.push(new Point(bombX,bombY-i));
            }else{
                break;
            }
        }

        // down
        for(var i=1;i<=this.explosionRange;i++){
            var hasObstacle=terrain.hasObstacle(bombX,bombY+i);
            if(hasObstacle==false){
                emptyPlaces.push(new Point(bombX,bombY+i));
            }else{
                break;
            }
        }

        // Center
        var hasObstacle=terrain.hasObstacle(bombX,bombY);
        if(hasObstacle==false){
            emptyPlaces.push(new Point(bombX,bombY));
        }

        //console.log(emptyPlaces);
        // 添加火焰对象
        for(var i=0;i<emptyPlaces.length;i++){
            var p=emptyPlaces[i];

            var f=new Fire(p.x*BL,p.y*BL);
            fires.push(f);
        }
    },
}
//---------------------------------------------------炸弹类定义结束-------------------------------------------------------------------<<

//---------------------------------------------------火焰类定义开始------------------------------------------------------------------->>
Fire=function(x,y){
    this.x=x;
    this.y=y;
    this.files=['fire1.png','fire2.png'];
}
Fire.prototype={
    x:0,            // 横坐标
    y:0,            // 纵坐标
    files:[],        // 火焰图片
    lifeTime:50,    // 显示时间

    paint:function(ctx){
        if(this.lifeTime>0){
            this.lifeTime--;

            var img=new Image();

            if(this.lifeTime>25){
                img.src=this.files[0];
            }else{
                img.src=this.files[1];
            }

            ctx.drawImage(img,this.x,this.y);
        }
    },
}
//---------------------------------------------------火焰类定义结束-------------------------------------------------------------------<<

//---------------------------------------------------奖励类定义开始------------------------------------------------------------------->>
Bonus=function(x,y){
    this.x=x;
    this.y=y;
    this.files=['bonus.png'];
}
Bonus.prototype={
    x:0,            // 横坐标
    y:0,            // 纵坐标
    files:[],        // 图片
    used:false,        // 是否使用过

    paint:function(ctx){
        if(this.used==false){

            var img=new Image();
            img.src=this.files[0];

            ctx.drawImage(img,this.x,this.y);
        }
    },
}
//---------------------------------------------------奖励类定义结束-------------------------------------------------------------------<<

//---------------------------------------------------奖励管理类定义开始------------------------------------------------------------------->>
BonusMng=function(x,y){
    this.bonusArr=new Array();

    this.addBonus(2);
}
BonusMng.prototype={
    bonusArr:[],        // 奖励数组

    paint:function(ctx){
        for(var i=0;i<this.bonusArr.length;i++){
            var b=this.bonusArr[i];
            b.paint(ctx);
        }
    },

    // 提升英雄的爆炸范围
    enlargeExplosionRange:function(hero){
        for(var i=0;i<this.bonusArr.length;i++){
            var b=this.bonusArr[i];

            if(b.used==false && b.x==hero.x && b.y==hero.y){
                hero.explosionRange+=1;
                b.used=true;
                break;

            }
        }

        this.refresh();
    },

    // 提升怪物速度
    raiseSpeed:function(monsters){
        for(var i=0;i<monsters.length;i++){
            var monster=monsters[i];

            if(monster.alive==true){
                for(var j=0;j<this.bonusArr.length;j++){
                    var b=this.bonusArr[j];

                    if(b.used==false && b.x==monster.x && b.y==monster.y){
                        monster.doubleStep();
                        b.used=true;

                        console.log(this.bonusArr);

                    }
                }
            }
        }

        this.refresh();
    },

    // 更新bonus
    refresh:function(){
        var count=0;
        for(var i=0;i<this.bonusArr.length;i++){
            var b=this.bonusArr[i];

            if(b.used==false){
                count++;// 统计未被使用的bonus个数
            }
        }

        if(count==0){
            this.addBonus(1);// 如果没有bonus了再动态加几个
        }
    },

    addBonus:function(n){
        var points=terrain.getEmptyPoints(n);

        for(var i=0;i<points.length;i++){
            var p=points[i];
            this.bonusArr.push(new Bonus(p.x,p.y));
        }
    },
}
//---------------------------------------------------奖励管理类定义结束-------------------------------------------------------------------<<
//-->
</script>

2019年3月9日21点28分 (PS:今天在博客园的排名从六百多上升到四百多,不知怎么回事)

1.04版 让怪物吃奖励,改进了怪物寻路算法。2019年3月11日11点23分

[Canvas]Bombman v1.04的更多相关文章

  1. [Canvas]Bombman v1.00

    爆破小人Canvas版,请点此下载,并用浏览器打开试玩. 图例: 源码: <!DOCTYPE html> <html lang="utf-8"> <m ...

  2. T6跨账套辅助工具[v1.04]

    [v1.03] 增加自定义报表,用户可以自行设置报表所打开的数据表,然后设置查询条件 [v1.04]更改单据显示样式,直接以用友打印预览的方式显示,允许用自定义显示,打印样式 下图为新的显示样式 下图 ...

  3. HTML5用canvas绘制五星红旗

    在HTML5一览中,我们提到html 5被冠以很多高帽,其中最高的一顶.备受争议的就是"Flash杀手".IT评论界老喜欢用这个词了,杀手无处不在.不管是不是杀手,HTML 5引进 ...

  4. 【原创】使用HTML5+canvas+JavaScript开发的原生中国象棋游戏及源码分享

    目前已经实现的功能: V1.0 : 实现棋子的布局,画布及游戏场景的初始化V2.0 : 实现棋子的颜色改变V3.0 :实现所有象棋的走棋规则V4.0 : 实现所有棋子的吃子功能 GItHub源码下载地 ...

  5. 【JavaScript游戏开发】使用HTML5 canvas开发的网页版中国象棋项目

    //V1.0 : 实现棋子的布局,画布及游戏场景的初始化 //V2.0 : 实现棋子的颜色改变 //V3.0 :实现所有象棋的走棋规则 //V4.0 : 实现所有棋子的吃子功能 完整的项目源码已经开源 ...

  6. git代码库误操作还原记录

    先做一些前情提要: 我们项目使用git作为代码管理,同时为了操作更方便,安装了乌龟git(tortoiseGit)工具.以下几乎所有操作都是在乌龟git上进行. 我们的项目是分阶段完成的,在完成上一阶 ...

  7. snmp数据包分析

    今天看了一下snmp数据包的报文格式,用wireshark抓了两个数据包来分析. 先说说snmp get-request的书报包格式吧,get-next-request,get-response,se ...

  8. Centos6.5搭建bugzilla

    一.安装httpd. mod_ssl. mysql-server . mysql .php-mysql . gcc . perl* . mod-perl-devel [root@localhost ~ ...

  9. kubernetes入门(03)kubernetes的基本概念

    一.Pod 在Kubernetes集群中,Pod是创建.部署和调度的基本单位.一个Pod代表着集群中运行的一个进程,它内部封装了一个或多个应用的容器.在同一个Pod内部,多个容器共享存储.网络IP,以 ...

随机推荐

  1. Optimization algorithm----Deep Learning

    深度学习中的优化算法总结 以下内容简单的汇总了在深度学习中常见的优化算法,每个算法都集中回答:是什么?(原理思想)有什么用?(优缺点)怎么用?(在tensorflow中的使用) 目录 1.SGD 1. ...

  2. tomcat 输入学习

    Tomcat学习—Tomcat7 修改/webapps/ROOT发布路径(Linux和windows环境) https://blog.csdn.net/u010648555/article/detai ...

  3. ubuntu 语言设置

    1.ubuntu ibus 输入法无法切换拼音 原因未安装中文输入法 sudo apt install ibus-pinyin //安装pinyinwin + space(空格) 切换中文输入法 再用 ...

  4. php中foreach()跳出循环或者终止循环的实现方法

    $arr = array('a','b','c','d','e'); $html = ''; foreach($arr as $key => $value){ if($value=='b'){ ...

  5. web----ssl通信

    ssl通信 https://www.cnblogs.com/zhengah/p/5007753.html

  6. 里氏代换原则(Liskov Substitution Principle,LSP)

    第一种定义: 如果对每一个类型为S的对象o1,都有类型为T的对象o2,使得以T定义的所有程序P在所有的对象o1都代换为o2,程序P的行为没有发生变化,那么类型S是类型T的子类型. 第二种定义: 所有引 ...

  7. POJ 1017 Packets【贪心】

    POJ 1017 题意: 一个工厂制造的产品形状都是长方体,它们的高度都是h,长和宽都相等,一共有六个型号,他们的长宽分别为 1*1, 2*2, 3*3, 4*4, 5*5, 6*6.  这些产品通常 ...

  8. java:矩阵面积

    实现一个矩阵类Rectangle,包含如下的一些成员变量与函数: 两个共有的成员变量 width 和 height 分别代表宽度和高度. 一个构造函数,接受2个参数 width 和 height 来设 ...

  9. jquery 中remove()与detach()的区别

    remove()与detach()方法都是从dom中删除所有的元素 两者的共同之处在于都不会把匹配的元素从jQuery对象中删除. 不同之处在于用remove()删除的元素,除了元素被保留,其他的在这 ...

  10. 014 view-controller标签

    1.说明 可以直接相应转发的页面, 而无需再经过 Handler 的方法. 这个时候可以使用mvc:view-controller标签. 但是以前的映射会出现问题,这个时候需要再配置一个标签<m ...