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

项目演示

项目演示地址:

体验一下

项目源码:

项目源码

代码结构

本节做完效果

新增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();
    }
    
}

Enemy.js


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;
            }
        }
        
    }

项目源码:

项目源码

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,277评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,689评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,624评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,356评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,402评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,292评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,135评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,992评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,429评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,636评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,785评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,492评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,092评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,723评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,858评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,891评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,713评论 2 354

推荐阅读更多精彩内容