从零开始手把手教你使用javascript+canvas开发一个塔防游戏02敌人自动寻路

项目演示

项目演示地址:

体验一下

项目源码:

项目源码

代码结构

本节做完效果

image.png

Enemy.js

//敌人类

function Enemy(cxt,img,type,x,y,width,height){

    this.cxt = cxt;
    this.img = img;
    this.x = x;//55
    this.y = y;//0
    this.width = width;
    this.height = height;
    //敌人类型
    this.type = type;

    this.sp = 2;
    //移动的方向
    this.dir = null;
    //下个移动位置
    this.nextPosition = null;

    //记录已经走过的位置
    this.hadWalk = {};
}

Enemy.prototype = {
    //敌人在图片中对应的位置
    enemyMap : [{x:0,y:0},{x:40,y:0},{x:80,y:0},{x:120,y:0},{x:160,y:0},{x:200,y:0},{x:240,y:0},{x:280,y:0},{x:320,y:0},{x:360,y:0},
        {x:400,y:0},{x:440,y:0},{x:480,y:0},{x:520,y:0},{x:560,y:0},{x:600,y:0},{x:640,y:0},{x:680,y:0},{x:720,y:0},{x:760,y:0}],
    //画出敌人
    draw : function(){
        //冰冻中,画出冰冻图
        if(this.frozenTime > 0){

            Canvas.drawImg(this.cxt,this.img,this.enemyMap[this.type].x,this.enemyMap[this.type].y+40,this.width,this.height,this.x,this.y,this.width,this.height);
        }
        //画出正常图
        else Canvas.drawImg(this.cxt,this.img,this.enemyMap[this.type].x,this.enemyMap[this.type].y,this.width,this.height,this.x,this.y,this.width,this.height);
        //计算血量百分比
        var persen = Math.floor(this.life / this.maxLife * 100) / 2;
        //画出血量
        Canvas.fillRect(this.cxt,this.x-5,this.y-5,persen,3,"rgba(38,223,116,0.8)");
    },

    //更新敌人信息
    update : function(){
        //超出坐标
        if(this.x >= 500){
            return false;
        }

        var xIndex = parseInt(this.x / 50,10),//1
            yIndex = parseInt(this.y / 50,10);//0
        //判断是否有下个移动位置信息,或者下哥移动位置信息是否已经走到了
        if(!this.nextPosition ||
            ((this.x >= this.nextPosition.x - 5 && this.x <= this.nextPosition.x)
                && (this.y >= this.nextPosition.y - 5 && this.y <= this.nextPosition.y))
        ){
            //走到最右侧
            if(xIndex + 1 >= 10){
                xIndex = -1;
            }
            else{
                //判断往下能否走
                if(MapData[xIndex][yIndex+1] && !this.hadWalk[xIndex+"_"+(yIndex+1)]){

                    this.dir = "down";
                    yIndex += 1;
                }
                //判断往右能否走
                else if(MapData[xIndex+1][yIndex]  && !this.hadWalk[(xIndex+1)+"_"+yIndex]){
                    this.dir = "right";
                    xIndex += 1;
                }
                else if(MapData[xIndex][yIndex-1] && !this.hadWalk[xIndex+"_"+(yIndex-1)]){
                    this.dir = "up";
                    yIndex -= 1;
                }
                else if(MapData[xIndex-1][yIndex] && !this.hadWalk[(xIndex-1)+"_"+yIndex]){
                    this.dir = "left";
                    xIndex -= 1;
                }
            }
            //是否走到最右侧
            if(xIndex == -1){
                this.nextPosition = {x:500,y:yIndex*50+5};
            }
            //设置下个移动位置
            else {
                this.nextPosition = {x:xIndex*50+5,y:yIndex*50+5};
                //记录已经走过的位置
                this.hadWalk[xIndex+"_"+yIndex] = true;
            }

        }


        //移动
        switch(this.dir){

            case "down":
                this.y += this.sp;
                break;
            case "up":
                this.y -= this.sp;
                break;
            case "left":
                this.x -= this.sp;
                break;
            case "right":
                this.x += this.sp;
                break;
            default:

                break;
        }

    }

}

//更新所有敌人信息
function updateEnemy(){

    var enemy;

    for(var i=0,l=Game.enemyList.length;i<l;i++){

        enemy = Game.enemyList[i];

        if(!enemy)continue;

        enemy.update();
    }

}

//画出所有敌人
function drawEnemy(){

    var enemy;

    for(var i=0,l=Game.enemyList.length;i<l;i++){

        enemy = Game.enemyList[i];

        if(!enemy)continue;

        enemy.draw();
    }

}

game.js修改

每50次循环出一个敌人

tool.js新增

自动寻路算法解析

坐标系

向右为x轴
向下为y轴

整个地图为500*500

   // 游戏对象
        var hero = {
            speed: 2, // 每秒移动的像素
            x: 55,
            y: 0,
            srcx:120,
            srcy:40,
           flood:50,
         
        //移动的方向
        dir:null,
        //下个移动位置
        nextPosition: null,

        //记录已经走过的位置
        hadWalk:{}
        };

英雄像素转换为网格(10*10)坐标

 var xIndex = parseInt(hero.y / 50, 10),
 yIndex = parseInt(hero.x / 50, 10);

最开始时,
xIndex = 1
yIndex = 0

初始时满足!hero.nextPosition

  if (!hero.nextPosition ||
                 ((hero.x >= hero.nextPosition.x - 5 && hero.x <= hero.nextPosition.x) && (hero.y >= hero.nextPosition.y - 5 && hero.y <= hero.nextPosition.y))
             ) 

进入后,满足往下走

 else {
                //判断往下能否走
                if(MapData[xIndex][yIndex+1] && !this.hadWalk[xIndex+"_"+(yIndex+1)]){

                    this.dir = "down";
                    yIndex += 1;
                }

此时
xIndex = 1
yIndex = 1
然后,

 //设置下个移动位置
                 else {
                     hero.nextPosition = {y: xIndex * 50 + 5, x: yIndex * 50 + 5};
                     //记录已经走过的位置
                     hero.hadWalk[xIndex + "_" + yIndex] = true;
                 }

hero.nextPosition = {
x:55,y:55
}

然后,

 //移动
            switch(hero.dir){

                case "down":
                    hero.y += hero.speed;
                    break;
                case "up":
                    hero.y -= hero.speed;
                    break;
                case "left":
                    hero.x -= hero.speed;
                    break;
                case "right":
                    hero.x += hero.speed;
                    break;
                default:

                    break;
            }

此时,

hero = {
           speed: 2, // 每秒移动的像素
           x: 2,
           y: 55,

循环回去:
然后在hero.x未满足条件:

  if (!hero.nextPosition ||
                ((hero.x >= hero.nextPosition.x - 5 && hero.x <= hero.nextPosition.x) && (hero.y >= hero.nextPosition.y - 5 && hero.y <= hero.nextPosition.y))
            ) 

时,有25次(50/2),跳过上面条件里的代码,直接执行以下代码:

 //移动
            switch(hero.dir){

                case "down":
                    hero.y += hero.speed;
                    break;
                case "up":
                    hero.y -= hero.speed;
                    break;
                case "left":
                    hero.x -= hero.speed;
                    break;
                case "right":
                    hero.x += hero.speed;
                    break;
                default:

                    break;
            }

当再次hero.x满足条件:

  if (!hero.nextPosition ||
                ((hero.x >= hero.nextPosition.x - 5 && hero.x <= hero.nextPosition.x) && (hero.y >= hero.nextPosition.y - 5 && hero.y <= hero.nextPosition.y))
            ) 

时,重复之前的逻辑。
如此循环。
such that

项目源码:

项目源码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值