从零开始手把手教你使用javascript+canvas开发一个塔防游戏06塔发射子弹

简介: 从零开始手把手教你使用javascript+canvas开发一个塔防游戏06塔发射子弹

项目演示



image.png


项目演示地址:

体验一下

项目源码:

项目源码

代码结构

image.png

本节做完效果

image.png


新增bullet.js

//子弹类
 function Bullet(cxt,img,type,enemy,level,x,y,radius){
    this.cxt = cxt;
    this.img = img;
    this.x = x;
    this.y = y;
    this.radius = radius;
    this.radiusAll = radius * 2;
    //子弹类型
    this.type = type;
    this.enemy = enemy;
    //子弹级别
    this.level = level;
    this.sp = 5;
    //穿刺弹的移动速度
    this.lineAngle = this.getLineAngle();
    if(x > enemy.x+20)this.lineAngle.xsp *= -1;
    if(y > enemy.y+20)this.lineAngle.ysp *= -1;
    this.lineEnemy = {};
}
Bullet.prototype = {
    //子弹在图像中的对应
    bulletMap : [{x:0,y:0},{x:10,y:0},{x:20,y:0},{x:30,y:0},{x:40,y:0}],
    //画子弹
    draw : function(){
        Canvas.drawImg(this.cxt,this.img,this.bulletMap[this.type].x,this.bulletMap[this.type].y,this.radiusAll,this.radiusAll,this.x,this.y,this.radiusAll,this.radiusAll);
    },
    //更新子弹信息
    update : function(enemyList){
        var bulletInfo = BulletType[this.type]["level_"+this.level],
            enemy;
        //判断是否是穿刺子弹
        if(this.type == 3){
            this.x += this.lineAngle.xsp;
            this.y += this.lineAngle.ysp;
            if(this.y >= 500 || this.y <= 0 || this.x >= 500 || this.x <= 0){
                return this.over();
            }
            //遍历每个敌人
            for(var i=0,l=enemyList.length;i<l;i++){
                enemy = enemyList[i];
                if(!enemy)continue;
                //如果穿刺过,跳过
                if(this.lineEnemy[enemy.num])continue;
                //判断子弹击中敌人
                if(T.circleInCircle(this,{x:enemy.x+20,y:enemy.y+20,radius:20})){
                    this.lineHurt(enemy);
                    //将穿刺过的敌人保存
                    this.lineEnemy[enemy.num] = true;
                }
            }
        }
        //非穿刺弹
        else{
            //敌人死了,子弹结束
            if(!this.enemy.islive)return this.over();
            //移动的速度
            var sp = this.getLineAngle();
            if(this.x > this.enemy.x+20)this.x -= sp.xsp;
            else if(this.x < this.enemy.x +20)this.x += sp.xsp;
            if(this.y > this.enemy.y+20)this.y -= sp.ysp;
            else if(this.y < this.enemy.y + 20)this.y += sp.ysp;
            if(this.y >= 500 || this.y <= 0 || this.x >= 500 || this.x <= 0){
                return this.over();
            }
            //判断子弹击中敌人
            if(T.circleInCircle(this,{x:this.enemy.x+20,y:this.enemy.y+20,radius:20})){
                return this.over(this.enemy);
            }
        }
    },
    //获取子弹对于敌人的移动速度
    getLineAngle : function(){
        var ydif = Math.abs(this.y-this.enemy.y-15),
            xdif = Math.abs(this.x-this.enemy.x-15),
            xsp,ysp;
        if(ydif >= xdif){
            ysp = this.sp;
            xsp = Math.floor(this.sp * (xdif / ydif));
        }
        else {
            xsp = this.sp;
            ysp = Math.floor(this.sp * (ydif / xdif));
        }
        return {xsp:xsp,ysp:ysp};
    },
    //子弹结束
    over : function(enemy){
        //判断子弹是因为击中敌人而结束
        if(enemy){
            var effer = {},bType = BulletType[this.type]["level_"+this.level];
            //设置子弹的效果
            if(bType.forzen){
                effer = {effer:"frozen",num:bType.forzen};
            }
            else if(bType.steal){
                effer = {effer:"steal",num:bType.steal};
            }
            else if(bType.kill){
                effer = {effer:"kill",num:bType.kill};
            }
            else effer = {effer:"nomal"};
            //较少鸡蛋的生命
            enemy.reduceLife(bType.hurt,effer);
        }
        //移除子弹
        Game.bulletList.remove(this);
        //设置穿刺子弹的击中敌人列表为空
        this.lineEnemy = null;
        return false;
    },
    //穿刺子弹的伤害敌人
    lineHurt : function(enemy){
        enemy.reduceLife(BulletType[this.type]["level_"+this.level].hurt,{effer:"normal"});
    }
}
//子弹类型
var BulletType = [
    {
        level_1:{
            hurt:10,steal:0
        },
        level_2:{
            hurt:12,steal:0
        },
        level_3:{
            hurt:12,steal:1
        }
    },
    {
        level_1:{
            hurt:5,forzen:3000
        },
        level_2:{
            hurt:8,forzen:4000
        },
        level_3:{
            hurt:10,forzen:4000
        }
    },
    {
        level_1:{
            hurt:12
        },
        level_2:{
            hurt:15
        },
        level_3:{
            hurt:20
        }
    },
    {
        level_1:{
            hurt:100
        },
        level_2:{
            hurt:200
        },
        level_3:{
            hurt:300
        }
    },
    {
        level_1:{
            hurt:15,kill:5
        },
        level_2:{
            hurt:20,kill:8
        },
        level_3:{
            hurt:30,kill:10
        }
    }
]
//更新所有子弹信息
function updateBullet(){
    var bullet;
    for(var i=0,l=Game.bulletList.length;i<l;i++){
        bullet = Game.bulletList[i];
        if(!bullet)continue;
        bullet.update(Game.enemyList);
    }
}
//画出所有子弹
function drawBullet(){
    var bullet;
    for(var i=0,l=Game.bulletList.length;i<l;i++){
        bullet = Game.bulletList[i];
        if(!bullet)continue;
        bullet.draw();
    }
}



image.png

image.png

image.png


Enemy.js


image.png


image.png

tower.js新增update函数

//更新塔的信息
    update : function(enemyList){
        //判断冷却时间
        if(this.cd > 0){
            this.cd -= 1;
            return false;
        }
        var towerInfo = TowerType[this.type]["level_"+this.level],
            canShot = towerInfo.bullet,
            enemy;
        this.cd = towerInfo.cd;
        //遍历敌人
        for(var i=0,l=enemyList.length;i<l;i++){
            enemy = enemyList[i];
            if(!enemy)continue;
            //判断敌人是否在塔的攻击范围内
            if(T.rectInCircle(enemy,{x:this.x+25,y:this.y+25,radius:towerInfo.scope})){
                //可发送的子弹数减少
                canShot -= 1;
                //新增一个子弹,加入到子弹列表中
                Game.bulletList.push(new Bullet(Game.canvasList.main,Game.imgList.bullet_img,this.type,enemy,this.level,this.x+20,this.y+20,5,5));
                //如果可用子弹没了,退出
                if(canShot <= 0)break;
            }
        }
    }


项目源码:


项目源码

目录
打赏
0
0
0
0
40
分享
相关文章
通义灵码2.5实战评测:Vue.js贪吃蛇游戏一键生成
通义灵码基于自然语言需求,快速生成完整Vue组件。例如,用Vue 2和JavaScript实现贪吃蛇游戏:包含键盘控制、得分系统、游戏结束判定与Canvas动态渲染。AI生成的代码符合规范,支持响应式数据与事件监听,还能进阶优化(如增加启停按钮、速度随分数提升)。传统需1小时的工作量,使用通义灵码仅10分钟完成,大幅提升开发效率。操作简单:安装插件、输入需求、运行项目即可实现功能。
140 4
 通义灵码2.5实战评测:Vue.js贪吃蛇游戏一键生成
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
198 72
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
Node.js开发
Node.js开发
153 13
深入浅出Node.js后端开发
在数字化时代的浪潮中,后端开发作为连接用户与数据的桥梁,扮演着至关重要的角色。本文将以Node.js为例,深入探讨其背后的哲学思想、核心特性以及在实际项目中的应用,旨在为读者揭示Node.js如何优雅地处理高并发请求,并通过实践案例加深理解。无论你是初学者还是有一定经验的开发者,这篇文章都将为你提供新的视角和思考。
深入浅出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开发中的应用。无论你是初学者还是有一定经验的开发者,这篇文章都将为你提供新的见解和技巧,让你的后端开发技能更上一层楼。
Fabric.js – 简单而强大的 JavaScript Canvas 库
  Fabric.js 是一个简单而强大的 JavaScript Canvas 库,在 HTML5 Canvas 元素之上提供了互动的对象模型,同时还包含 Canvas-to-SVG 解析器。 您可能感兴趣的相关文章 使用 Toolbar.
1510 0
基于springboot+vue.js+uniapp的客户关系管理系统附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的客户关系管理系统附带文章源码部署视频讲解等
205 2

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等