从零开始手把手教你使用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


项目源码:


项目源码

目录
打赏
0
0
0
0
32
分享
相关文章
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
127 72
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
Node.js开发
Node.js开发
103 13
深入浅出Node.js后端开发
本文将带你领略Node.js的魅力,从基础概念到实践应用,一步步深入理解并掌握Node.js在后端开发中的运用。我们将通过实例学习如何搭建一个基本的Web服务,探讨Node.js的事件驱动和非阻塞I/O模型,以及如何利用其强大的生态系统进行高效的后端开发。无论你是前端开发者还是后端新手,这篇文章都会为你打开一扇通往全栈开发的大门。
深入理解Node.js事件循环及其在后端开发中的应用
本文旨在揭示Node.js的核心特性之一——事件循环,并探讨其对后端开发实践的深远影响。通过剖析事件循环的工作原理和关键组件,我们不仅能够更好地理解Node.js的非阻塞I/O模型,还能学会如何优化我们的后端应用以提高性能和响应能力。文章将结合实例分析事件循环在处理大量并发请求时的优势,以及如何避免常见的编程陷阱,从而为读者提供从理论到实践的全面指导。
深入浅出Node.js后端开发
本文将带领读者从零基础开始,一步步深入到Node.js后端开发的精髓。我们将通过通俗易懂的语言和实际代码示例,探索Node.js的强大功能及其在现代Web开发中的应用。无论你是初学者还是有一定经验的开发者,这篇文章都将为你提供新的见解和技巧,让你的后端开发技能更上一层楼。
测试驱动javascript开发 -- 1.单元测试
  从今天开始,我将以读书笔记的方式向大家介绍《Test-Driven JavaScript Development》相关内容。我不太清楚这本书是否已经有了中文的译本,有兴趣的朋友可以去搜索下,或者直接读英文原版。
917 0
测试驱动javascript开发 -- 2.单元测试一例:学习断言、测试用例函数的编写
  本篇我们将通过对Date.strftime编写单元测试的例子,学会断言、测试用例函数的相关知识。   首先我们先来看Date.strftime的实现代码。 Date.prototype.strftime = (function () {   function strftime(form...
890 0
JavaScript中的原型 保姆级文章一文搞懂
本文详细解析了JavaScript中的原型概念,从构造函数、原型对象、`__proto__`属性、`constructor`属性到原型链,层层递进地解释了JavaScript如何通过原型实现继承机制。适合初学者深入理解JS面向对象编程的核心原理。
63 1
JavaScript中的原型 保姆级文章一文搞懂
基于springboot+vue.js+uniapp的客户关系管理系统附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的客户关系管理系统附带文章源码部署视频讲解等
155 2
|
5月前
JS+CSS3文章内容背景黑白切换源码
JS+CSS3文章内容背景黑白切换源码是一款基于JS+CSS3制作的简单网页文章文字内容背景颜色黑白切换效果。
42 0

热门文章

最新文章