canvas雷霆战机

这个游戏大家肯定都玩过,今天分享一个用canvas写的一个小demo。
效果图:


QQ20180103-204422-HD.gif

一、首先要做的肯定是图片预加载

var imgArr = {
            "bg":"img/background.png",
            "herofly":"img/herofly.png",
            "bullet1":"img/bullet1.png",
            "bullet2":"img/bullet2.png",
            "enemy1":"img/enemy1.png",
            "enemy2":"img/enemy2.png",
            "enemy3":"img/enemy3.png",
            "prop":"img/prop.png"
        }
        var imgLength = 0;
        for(var i in imgArr){
            imgLength++;
        }
        var num = 0;
        var loadArr = {};
        for(let i in imgArr){
            var img = new Image();
            img.src = imgArr[i];
            img.onload = function(){
                loadArr[i] = this;
                num++;
                if(num>=imgLength){
                    main(loadArr);
                }
            }
        }

二、分析游戏中都几个对象

对象一:主机;
对象二:敌机;
对象三:奖励;
每个对象都有move移动,draw绘画,isClear是否清除的方法
以子弹为例:其余对象的构造函数类似

// 子弹构造函数
            function Bullet(x,y,power,obj,speed){
                this.x = x;
                this.y = y;
                this.power = power;
                this.obj = obj;
                this.w = obj.width;
                this.h = obj.height;
                this.speed = speed;
            }
            Bullet.prototype.draw = function(){
                context.drawImage(this.obj,0,0,this.w,this.h,this.x,this.y,this.w,this.h);
                // 子弹与敌机的碰撞检测
                for(var j = 0 ; j < enemy.length;j++){
                    if((this.x<enemy[j].x+enemy[j].w && this.x+this.w>enemy[j].x) && (this.y<enemy[j].y+enemy[j].h && this.y+this.h>enemy[j].y)){   
                        enemy[j].blood -= 25;
                        if(enemy[j].i == 5){
                            music2.src = "audio/enemy1_down.mp3";
                        }else if(enemy[j].i == 6){
                            music2.src = "audio/enemy2_down.mp3";
                        }else{
                            music2.src = "audio/enemy3_down.mp3";
                        }   
                        this.y = -10;   
                    }   
                }   
            }
            Bullet.prototype.move = function(){
                this.y -= this.speed;   
            }
            Bullet.prototype.isClear = function(){
                if(this.y<0){
                    return true;
                }else{
                    return false;
                }
            }

三、定义几个数组存放实例化的子弹,敌机,奖励

方便后面做碰撞判断

// 存放子弹的容器
            var bullet = [];
            var num = 0;
            var bgY = 0;
            // 存放敌机的容器
            var enemy = [];
            var enemyBlood = 100;
            // 存放奖励的容器
            var award = [];
            // 子弹的颗数
            var bulletNum = loadArr.bullet1;
            var bulletX = 30;
            // 敌机与主机碰撞的开关
            var isCarld = true;

四、动画函数

创建一个act函数做动画,函数里实例化子弹,敌机,以及做各种碰撞检测,这里注意:碰撞检测不要全部在act方法里判断,可以在构造函数的draw方法里判断,优化性能,使游戏画面流畅。

  1. 背景图滚动
                bgY--;
                context.drawImage(loadArr.bg,0,0+bgY,canvas.width,canvas.height);
                context.drawImage(loadArr.bg,0,canvas.height+bgY,canvas.width,canvas.height);
                if(Math.abs(bgY) >= canvas.height){
                    bgY = 0;
                }
  1. 绘画主机和实例化敌机,奖励,Rand()是一个封装好取随机数的函数,使用随机数出现的概率来决定不同敌机和不同奖励出现的概率。
// 画主机
                newHero.draw();
                newHero.move();
                // 实例化敌机和奖励
                if(num%50 == 0){
                    var randNum = Rand(0,34);
//                  console.log(randNum);
                    if(randNum>=0 && randNum<20){
                        // 随机位置
                        var randX = Rand(0,canvas.width-loadArr.enemy1.width/5);
                        // 敌机的血量
                        var blood = enemyBlood -20;
                        var newEnemy = new Enemy(randX,-loadArr.enemy1.height,blood,20,loadArr.enemy1,1.5,5);
                        enemy.push(newEnemy);
                    }else if(randNum >= 20 && randNum <28){
                        var randX = Rand(0,canvas.width-loadArr.enemy2.width/6);
                        var blood = enemyBlood -50;
                        var newEnemy = new Enemy(randX,-loadArr.enemy2.height,blood,35,loadArr.enemy2,1,6);
                        enemy.push(newEnemy);
                    }else if(randNum >= 28 && randNum <30){
                        var blood = enemyBlood;
                        var randX = Rand(0,canvas.width-loadArr.enemy3.width/10);
                        var newEnemy = new Enemy(randX,-loadArr.enemy3.height,blood,50,loadArr.enemy3,0.5,10);
                        enemy.push(newEnemy);
//                      console.log(newEnemy.x,newEnemy.y);
                    }else if(randNum >= 30 && randNum <31){
                        // 奖励1
                        var randX = Rand(0,canvas.width-loadArr.prop.width/3);
                        var newRaward = new Award(randX,-loadArr.prop.height,loadArr.prop,1,0);
                        console.log(newRaward);
                        award.push(newRaward);
                    }else if(randNum >= 31 && randNum <33){
                        // 奖励2
                        var randX = Rand(0,canvas.width-loadArr.prop.width/3);
                        var newRaward = new Award(randX,-loadArr.prop.height,loadArr.prop,1,1);
                        console.log(newRaward);
                        award.push(newRaward);
                    }else{
                        // 奖励3
                        var randX = Rand(0,canvas.width-loadArr.prop.width/3);
                        var newRaward = new Award(randX,-loadArr.prop.height,loadArr.prop,1,2);
                        console.log(newRaward);
                        award.push(newRaward);
                    }
                }
  1. 敌机与主机的碰撞检测
    这里使用的事普通碰撞,大家有兴趣可以换成像素碰撞。
    遍历敌机enemy数组,使用isCarld控制只碰撞一次,主机血量减少。血量为0时主机爆炸游戏结束。
for(var i = 0 ; i < enemy.length ; i++){
                    enemy[i].draw();
                    enemy[i].move();
                    // 敌机与主机的碰撞检测
                    if((newHero.x< enemy[i].x+enemy[i].w && newHero.x+newHero.w>enemy[i].x) && (newHero.y< enemy[i].y+enemy[i].h && newHero.y+newHero.h>enemy[i].y)){
                        if(isCarld){
                            isCarld = false;
                            newHero.blood -= enemy[i].power;    
                            if(newHero.blood < 0){
                                newHero.blood = 0;
                            }
                            bloodNum.innerHTML = "血量:"+ newHero.blood;
                            bloodPress.style.width = 200*newHero.blood/100 + "px";
                        }
                        enemy[i].isF = true;
                        console.log(newHero.blood);
//                      music2.src = "audio/enemy2_out.mp3";
                    }
                    if(enemy[i].isF && enemy[i].y >= canvas.height){
                        isCarld = true;
                    }
                    if(enemy[i].isClear()){
                        enemy.splice(i,1);
                    }
//                  console.log(enemy.length);
                }

像子弹和敌机的碰撞还有主机和奖励的碰撞代码大同小异,就是其中逻辑不同,这里就不一一展示了。

  1. 游戏结束
    使用requestAnimationFrame做动画,使用cancelAnimationFrame(Timer);来停止动画。
var Timer = window.requestAnimationFrame(act);
                if(newHero.index >= newHero.i-1){
                    console.log("dfsf");
                    music1.src = "audio/game_over.mp3";
                    music1.loop = "";
                    music2.remove();
                    cancelAnimationFrame(Timer);
                    // 游戏结束
                    btn.style.display = "block";
                    context.beginPath();
                    context.fillStyle = "grey";
                    context.fillRect(0,150,canvas.width,200);
                    context.beginPath();
                    context.textBaseline = "middle";
                    context.textAlign = "center";
                    context.font = "40px Arial";
                    context.fillStyle = "red";
                    context.fillText("gameover",canvas.width/2,275);
                }

游戏结束的页面图:


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

推荐阅读更多精彩内容