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

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

项目演示


image.png


项目演示地址:

体验一下

项目源码:

项目源码

代码结构


image.png


本节做完效果


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修改


image.png


每50次循环出一个敌人


image.png

image.png

tool.js新增


image.png


自动寻路算法解析


坐标系

向右为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


项目源码:


项目源码

目录
相关文章
|
23天前
|
Web App开发 JavaScript 前端开发
Node.js开发
Node.js开发
38 13
|
29天前
|
存储 JavaScript 前端开发
深入浅出Node.js后端开发
在数字化时代的浪潮中,后端开发作为连接用户与数据的桥梁,扮演着至关重要的角色。本文将以Node.js为例,深入探讨其背后的哲学思想、核心特性以及在实际项目中的应用,旨在为读者揭示Node.js如何优雅地处理高并发请求,并通过实践案例加深理解。无论你是初学者还是有一定经验的开发者,这篇文章都将为你提供新的视角和思考。
|
26天前
|
Web App开发 开发框架 JavaScript
深入浅出Node.js后端开发
本文将带你领略Node.js的魅力,从基础概念到实践应用,一步步深入理解并掌握Node.js在后端开发中的运用。我们将通过实例学习如何搭建一个基本的Web服务,探讨Node.js的事件驱动和非阻塞I/O模型,以及如何利用其强大的生态系统进行高效的后端开发。无论你是前端开发者还是后端新手,这篇文章都会为你打开一扇通往全栈开发的大门。
|
25天前
|
Web App开发 JavaScript 前端开发
深入浅出Node.js后端开发
本文将带领读者从零基础开始,一步步深入到Node.js后端开发的精髓。我们将通过通俗易懂的语言和实际代码示例,探索Node.js的强大功能及其在现代Web开发中的应用。无论你是初学者还是有一定经验的开发者,这篇文章都将为你提供新的见解和技巧,让你的后端开发技能更上一层楼。
|
29天前
|
JavaScript 前端开发 API
深入理解Node.js事件循环及其在后端开发中的应用
本文旨在揭示Node.js的核心特性之一——事件循环,并探讨其对后端开发实践的深远影响。通过剖析事件循环的工作原理和关键组件,我们不仅能够更好地理解Node.js的非阻塞I/O模型,还能学会如何优化我们的后端应用以提高性能和响应能力。文章将结合实例分析事件循环在处理大量并发请求时的优势,以及如何避免常见的编程陷阱,从而为读者提供从理论到实践的全面指导。
|
JavaScript 前端开发
65行 JavaScript 代码实现 Flappy Bird 游戏
  飞扬的小鸟(Flappy Bird)无疑是2014年全世界最受关注的一款游戏。这款游戏是一位来自越南河内的独立游戏开发者阮哈东开发,形式简易但难度极高的休闲游戏,很容易让人上瘾。   这里给大家分享一篇这款游戏的 HTML5 版制作教程,借助 Phaser 框架,只需65行 JavaScript 代码即可实现。
1147 0
|
1月前
|
JavaScript 前端开发
JavaScript中的原型 保姆级文章一文搞懂
本文详细解析了JavaScript中的原型概念,从构造函数、原型对象、`__proto__`属性、`constructor`属性到原型链,层层递进地解释了JavaScript如何通过原型实现继承机制。适合初学者深入理解JS面向对象编程的核心原理。
27 1
JavaScript中的原型 保姆级文章一文搞懂
|
5月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的客户关系管理系统附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的客户关系管理系统附带文章源码部署视频讲解等
107 2
|
1月前
JS+CSS3文章内容背景黑白切换源码
JS+CSS3文章内容背景黑白切换源码是一款基于JS+CSS3制作的简单网页文章文字内容背景颜色黑白切换效果。
20 0
|
5月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的小区物流配送系统附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的小区物流配送系统附带文章源码部署视频讲解等
155 4