用JS写一篇飞机大战

网盘链接:https://pan.baidu.com/s/1gaSsG8fCfLlMhLDcK7pPYQ?pwd=6666
提取码:6666

链接里面有项目代码和图片资源,需要者自取,如果满意,还请关住、点赞、收藏,一键三连。

我们先上效果图
在这里插入图片描述
整体效果:点击画布开始游戏,鼠标移出画布,画面将暂停,鼠标移动控制飞机,乙方飞机与敌方飞机碰撞,life-1
游戏阶段分析过程

第一阶段分析过程

1.实现HTML页面布局<canvas>元素

  • 问题 - 元素不能实现页面居中?
    2.实现游戏背景
  • 背景使用图片 - drawImage()方法
  • 如何实现背景图片动态效果?
    3.实现显示游戏LOGO
  • 使用图片内容 - drawImage()方法

分析第二阶段:

  1. 加载游戏的背景,让背景循环播放
  2. 动画效果
    • 加载用于显示动画效果的所有图片 - 4张
    • 定义一个数组,用于存储动画效果的4张图片
    • 初始化动画效果的图片数据
    • 定义动画效果的构造器
    • 绘制图片 - drawImage(img) - 绘制的图片从第一张切换到第四章
    • 当动画效果执行完毕之后,游戏进入到运行阶段

分析第三阶段

我方飞机

  1. 绘制我方飞机
  • 加载我方飞机所使用的图片 - 2张
  • 使用数组格式存储我方飞机图片
  • 初始化我方飞机的数据
  • 创建我方飞机的构造器
  • 绘制我方飞机 - drawImage(imgs[0],x,y)
  1. 让我方飞机跟随鼠标移动而移动
  • 绑定onmousemove事件 - 元素
  • 根据当前鼠标的坐标值,修改我方飞机的(x,y)
    在这里插入图片描述

我方飞机的子弹

问题 - 子弹到底是独立对象,还是作为我方飞机对象的一个属性存在?

  • 面向对象 - 黑板与黑板擦的关系
  • 选择独立对象 - 画布中可能同时存在多个子弹(对象),选择使用数组格式
  1. 加载子弹图片 - 1张
  2. 初始化子弹的数据
  3. 创建子弹的构造器
    • 绘制子弹的方法 paint()
    • 控制子弹运动方法 step()
  4. 创建子弹对象 - 为我方飞机增加射击的功能
    • 使用数组格式
      在这里插入图片描述

操作敌方飞机(小、中、大三种飞机)

  1. 加载敌方飞机的图片
  • 小飞机 - 1张
  • 中飞机 - 1张
  • 大飞机 - 2张
  1. 初始化敌方飞机的数据
  2. 创建敌方飞机的构造器 - 针对每种敌方飞机创建一个构造器,还是统一创建一个构造器?
  • 绘制方法
  • 运动方法
  1. 创建敌方飞机对象 - 画布中同时存在多个敌方飞机
  • 使用数组存储多个敌方飞机
  1. 专门定义函数
    1)创建敌方飞机;
    2)绘制所有的敌方飞机;
    3)控制所有的敌方飞机运动
    4)当敌方飞机飞出画布时,删除操作

撞击逻辑

  1. 画布中具有我方飞机、我方子弹以及敌方飞机
  • 我方子弹撞击敌方飞机
  • 我方飞机撞击敌方飞机
    在这里插入图片描述
    在这里插入图片描述

分析我方飞机与敌方飞机撞击后的逻辑

  1. 敌方飞机
  • 爆破动画效果
  • 当爆破动画效果执行完毕后,将该敌方飞机删除
  1. 我方飞机
  • 爆破动画效果
  • 当爆破动画效果执行完毕后,进入第五阶段

分析敌方飞机被撞击后的逻辑

0.正常 - 标示

1.被撞击后,开始执行爆破动画 - 标示(down)

2.当爆破动画执行完毕,删除 - 标示(canDelete)
函数enemiesHit() - 用于检查敌方飞机是否被撞击
在这里插入图片描述
在这里插入图片描述

分析第四阶段 - 游戏暂停

  1. 当鼠标离开游戏的画布时,暂停游戏
  • 画布的元素 - 元素
  • 鼠标离开画布元素 - onmouseout事件
    • 判断当前处在运行阶段时 - state
      • 背景依旧具有动画效果
      • 我方飞机和子弹 - 只绘制,不移动 paint()
  1. 当鼠标重新进入到游戏的画布中时,继续游戏
  • 画布的元素 - 元素
  • 鼠标重新进入到画布元素 - onmouseover事件
    • 判断当前处在暂停阶段时 - state

分析第五阶段 - gameover

  1. 我方飞机的生命值为 0

  2. 进入到gameover阶段时,需要完成的事情

代码部分:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        canvas{
            display: block;
            border: 1px solid;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    <canvas id="myCanvas" width="480" height="650"></canvas>
    <script>
        var canvas = document.getElementById("myCanvas");
        var ctx = canvas.getContext("2d")

        // 0.游戏初始化
        // 0.1 定义将游戏划分为5个阶段的常量
        const START = 0;//游戏开始前
        const STARTING = 1;//游戏加载阶段
        const RUNNING = 2;//游戏运行阶段
        const PAUSE = 3;//游戏暂停阶段
        const GAMEOVER = 4;//游戏结束阶段
        // 0.2 定义一个标识,表示当前游戏处于第几个阶段
        var state = START;
        // 0.3 定义画布的宽度和高度
        const WIDTH = 480;
        const HEIGHT = 650;
        // 0.4 定义游戏得分
        var score = 0;
        // 0.5 定义我方飞机
        var life = 3;


        // 1.游戏欢迎阶段
        // 1.1 加载背景图片
        var bg = new Image();
        bg.src = "./images/background.png";
        // 1.2 初始化背景的数据
        var BG = {
            imgs : bg,
            width : 480,
            height : 852
        }
        // 1.3 定义背景对象的构造函数
        function Bg(config){
            this.imgs = config.imgs;
            this.width = config.width;
            this.height = config.height;
            // 定义绘制背景的坐标值
            this.x1 = 0;
            this.y1 = 0;
            this.x2 = 0;
            this.y2 = -this.height;
            // 定义绘制背景的方法
            this.paint = function(){
                ctx.drawImage(this.imgs,this.x1,this.y1)
                ctx.drawImage(this.imgs,this.x2,this.y2)
            }
            // 定义背景运动的方法
            this.step = function(){
                // 控制两张图片向下运动
                this.y1++;
                this.y2++;
                if(this.y1 == this.height){
                    this.y1 = -this.height;
                }
                if(this.y2 == this.height){
                    this.y2 = -this.height;
                }
            }

        }
        // 1.4 利用构造函数创建背景对象
        var sky = new Bg(BG)
        console.log(sky)
        // 1.5 加载的LOGO图片
        var logo = new Image()
        logo.src = "./images/start.png";

        // 2. 游戏过渡阶段
        // 2.1 加载过度动画的图片
        var loadings = []
        loadings[0] = new Image()
        loadings[0].src = "./images/game_loading1.png"
        loadings[1] = new Image()
        loadings[1].src = "./images/game_loading2.png"
        loadings[2] = new Image()
        loadings[2].src = "./images/game_loading3.png"
        loadings[3] = new Image()
        loadings[3].src = "./images/game_loading4.png"
        // 2.2 初始化图片的数据
        var LOADINGS = {
            imgs : loadings,
            length : loadings.length,
            width : 186,
            height : 38
        }
        // 2.3 定义加载动画的构造函数
        function Loading(config){
            this.imgs = config.imgs;
            this.length = config.length
            this.width = config.width;
            this.height = config.height;
            // 定义一个表示图片的索引值
            this.startIndex = 0;
            // 定义绘制图片
            this.paint = function(){
                ctx.drawImage(this.imgs[this.startIndex],147,550)
            }
            // 定义表示速度
            this.time = 0;
            // 定义动画方法
            this.step= function(){
                this.time++;
                if(this.time % 3 == 0){
                    this.startIndex ++;
                }
                if(this.startIndex == this.length){
                    // 加载动画执行完毕,游戏进入下一阶段
                    state = RUNNING
                }
            }
        }
        // 2.4 创建动画效果的对象
        var loading = new Loading(LOADINGS)
        // 2.5 为canvas元素绑定click事件
        canvas.onclick = function(){
            state = STARTING;
        }

        // 3.游戏运行阶段
        // 3.1 绘制我方飞机
        // 3.1.1 加载我方飞机图片
        var heros = []
        heros[0] = new Image()
        heros[0].src = "./images/hero1.png";
        heros[1] = new Image()
        heros[1].src = "./images/hero2.png";
        heros[2] = new Image()
        heros[2].src = "./images/hero_blowup_n1.png";
        heros[3] = new Image()
        heros[3].src = "./images/hero_blowup_n2.png";
        heros[4] = new Image()
        heros[4].src = "./images/hero_blowup_n3.png";
        heros[5] = new Image()
        heros[5].src = "./images/hero_blowup_n4.png";
        // 3.1.2 初始化我方飞机的数据
        var HEROS = {
            imgs : heros,
            length : heros.length,
            width : 99,
            height : 124,
            frame : 2
        }
        // 3.1.3 创建我方飞机的构造函数
        function Hero(config){
            this.imgs = config.imgs;
            this.length = config.length
            this.width = config.width;
            this.height = config.height;
            this.frame = config.frame;
            // 定义我方飞机的坐标值
            this.x = WIDTH / 2 - this.width / 2;
            this.y = HEIGHT - 150;
            // 定义一个属性表示图片的索引值
            this.startIndex = 0
            // 新增一个标识-表示是否被撞击
            this.down = false;
            // 新增一个标识-表示是否执行完毕
            this.candel = false;
            // 定义绘制方法
            this.paint = function(){
                ctx.drawImage(this.imgs[this.startIndex],this.x,this.y)
            }
            // 定义动画方法
            this.step = function(){
                if(!this.down){// 没被撞
                    // if(this.startIndex == 0){
                    //     this.startIndex = 1
                    // }else{
                    //     this.startIndex = 0
                    // }
                    this.startIndex++;
                    this.startIndex = this.startIndex % 2;
                }else{//被撞
                    this.startIndex++;
                    // 判断是否执行完毕
                    if(this.startIndex == this.length){
                        life--
                        if(life == 0){
                            state = GAMEOVER;
                            this.startIndex = this.length - 1;
                        }else{
                            hero = new Hero(HEROS)
                        }
                    }
                }
            }
            // 增加我方飞机射击的方法
            this.time = 0;
            this.shoot = function(){
                this.time++;
                if(this.time % 3 == 0){
                    bullets.push(new Bullet(BULLET))
                }
            }
            // 新增一个方法,用于处理撞击后的逻辑
            this.bang = function(){
                this.down = true;
            }
        }
        // 3.1.4 创建我方飞机对象
        var hero = new Hero(HEROS)
        // 3.1.5 为canvas绑定mousemove事件
        canvas.onmousemove = function(event){
            // console.log(event)
            var x = event.offsetX;
            var y = event.offsetY;
            // 修改我方飞机坐标值
            hero.x = x - hero.width / 2;
            hero.y = y - hero.height / 2;
        }
        // 3.2 绘制子弹
        // 3.2.1 加载子弹图片
        var bullet = new Image()
        bullet.src = "./images/bullet1.png"
        // 3.2.2 初始化子弹的数据
        var BULLET = {
            imgs : bullet,
            width : 9,
            height : 21
        }
        // 3.2.3 创建子弹的构造器
        function Bullet(config){
            this.imgs = config.imgs;
            this.width = config.width;
            this.height = config.height;
            // 定义子弹绘制的坐标值
            this.x = hero.x + hero.width / 2 - this.width / 2;
            this.y = hero.y - this.height - 10
            // 绘制
            this.paint = function(){
                ctx.drawImage(this.imgs,this.x,this.y)
            }
            // 运动方法
            this.step = function(){
                this.y -= 10;
            }
            // 新增方法 - 用于处理撞击后的逻辑
            this.candel = false;//没有撞击
            this.bang = function(){
                this.candel = true;
            }
        }
        // 3.2.4 创建存储子弹的数组
        var bullets = [];
        // 3.2.5 创建函数,用于绘制所有子弹
        function bulletsPaint(){
            for(var i = 0 ; i < bullets.length;i++){
                bullets[i].paint()
            }
        }
        // 3.2.6 创建函数,用于控制所有子弹运动
        function bulletsStep(){
            for(var i = 0 ; i < bullets.length;i++){
                bullets[i].step()
            }
        }
        // 3.2.7 当子弹移出画布,删除子弹
        function bulletsDel(){
            for(var i = 0 ; i < bullets.length ; i++){
                if(bullets[i].y < -bullets[i].height || bullets[i].candel){
                    bullets.splice(i,1)
                }
            }
            // console.log(bullets.length)
        }
        // 3.3 创建敌方飞机
        // 3.3.1 加载敌方飞机的图片
        var enemy1 = []//小飞机
        enemy1[0] = new Image()
        enemy1[0].src = "./images/enemy1.png"
        enemy1[1] = new Image()
        enemy1[1].src = "./images/enemy1_down1.png"
        enemy1[2] = new Image()
        enemy1[2].src = "./images/enemy1_down2.png"
        enemy1[3] = new Image()
        enemy1[3].src = "./images/enemy1_down3.png"
        enemy1[4] = new Image()
        enemy1[4].src = "./images/enemy1_down4.png"
        var enemy2 = []//中飞机
        enemy2[0] = new Image()
        enemy2[0].src = "./images/enemy2.png"
        enemy2[1] = new Image()
        enemy2[1].src = "./images/enemy2_down1.png"
        enemy2[2] = new Image()
        enemy2[2].src = "./images/enemy2_down2.png"
        enemy2[3] = new Image()
        enemy2[3].src = "./images/enemy2_down3.png"
        enemy2[4] = new Image()
        enemy2[4].src = "./images/enemy2_down4.png"
        var enemy3 = []//大飞机
        enemy3[0] = new Image()
        enemy3[0].src = "./images/enemy3_n1.png"
        enemy3[1] = new Image()
        enemy3[1].src = "./images/enemy3_n2.png"
        enemy3[2] = new Image()
        enemy3[2].src = "./images/enemy3_down1.png"
        enemy3[3] = new Image()
        enemy3[3].src = "./images/enemy3_down2.png"
        enemy3[4] = new Image()
        enemy3[4].src = "./images/enemy3_down3.png"
        enemy3[5] = new Image()
        enemy3[5].src = "./images/enemy3_down4.png"
        enemy3[6] = new Image()
        enemy3[6].src = "./images/enemy3_down5.png"
        enemy3[7] = new Image()
        enemy3[7].src = "./images/enemy3_down6.png"
        // 3.3.2 初始化敌方飞机的数据
        var ENEMY1 = {
            imgs : enemy1,
            length : enemy1.length,
            width : 57,
            height : 51,
            type : 1,//飞机类型
            frame : 1,
            life : 3,
            score : 1
        }
        var ENEMY2 = {
            imgs : enemy2,
            length : enemy2.length,
            width : 69,
            height : 95,
            type : 2,//飞机类型
            frame : 1,//正常状态下的形态
            life : 5,
            score : 3
        }
        var ENEMY3 = {
            imgs : enemy3,
            length : enemy3.length,
            width : 165,
            height : 361,
            type : 3,//飞机类型
            frame : 2,
            life : 20,
            score : 10
        }
        // 3.3.3 创建敌方飞机的构造器
        function Enemy(config){
            this.imgs = config.imgs 
            this.length = config.length 
            this.width = config.width 
            this.height = config.height
            this.type = config.type
            this.frame = config.frame
            this.life = config.life
            this.score = config.score
            // 定义敌方飞机的坐标值
            this.y = -this.height;
            this.x = Math.random() * ( WIDTH - this.width )//0 - (画布宽-飞机宽)
            // 定义数组的索引
            this.startIndex = 0
            // 新增属性 - 表示当前绘制的图片
            this.imgTarget = this.imgs[this.startIndex]
            // 新增状态 - 表示敌机是否被撞击
            this.down = false;
            // 新增状态 - 表示敌机是否被删除
            this.candel = false;
            // 绘制方法
            this.paint = function(){
                ctx.drawImage(this.imgs[this.startIndex],this.x,this.y)
            }
            // 运动方法
            this.step = function(){
                if(!this.down){//正常状态
                    // 小中飞机 下标为0
                    // 大飞机 下标在01切换
                    this.startIndex ++;
                    this.startIndex = this.startIndex % this.frame;
                    this.y += 2;
                }else{//爆破
                    // 小中飞机 下标为1
                    // 大飞机 下标在2
                    this.startIndex ++;
                    // 问题 - 进入爆破,只能执行一次
                    if(this.startIndex == this.length){
                        this.candel = true;
                        this.startIndex = this.length - 1
                    }
                }
            }
            // 增加用于检查敌机是否被撞击
            this.checkHit = function(wo){
                // 参数wo  1)我方飞机 2)子弹
                return wo.x + wo.width > this.x 
                && wo.x < this.x + this.width
                && wo.y + wo.height > this.y
                && wo.y < this.y + this.height
            }
            // 增加用于被撞击后的逻辑
            this.bang = function(){
                this.life--;
                if(this.life == 0){
                    this.down = true
                    score += this.score
                }
            }
        }
        // 3.3.4 创建数组 - 用于存储所有的地方飞机
        var enemies = []
        // 3.3.5 创建函数 - 用于创建敌方飞机
        function enterEnemies(){
            /*
                小飞机  - 数量最多
                中飞机 - 数量居中
                大飞机 - 数量最少
            */
            var rand = Math.floor(Math.random() * 100)
            if(rand <= 7){
                enemies.push(new Enemy(ENEMY1))
            }else if(rand == 8 || rand == 9){
                enemies.push(new Enemy(ENEMY2))
            }else if(rand == 10){
                // 只允许数组中的第一个元素为大飞机
                if(enemies[0].type !=3 && enemies.length >0){
                    enemies.splice(0,0,new Enemy(ENEMY3))
                }
            }
        }
        // 3.3.6 用于绘制敌方飞机
        function paintEnemies(){
            for(var i = 0 ; i < enemies.length;i++){
                enemies[i].paint()
            }
        }
        // 3.3.6 用于敌方飞机运动
        function stepEnemies(){
            for(var i = 0 ; i < enemies.length;i++){
                enemies[i].step()
            }
        }
        // 3.3.7 用于删除敌方飞机
        function delEnemies(){
            for(var i = 0 ; i < enemies.length;i++){
                if(enemies[i].y > HEIGHT || enemies[i].candel){
                    enemies.splice(i,1)
                }
            }
            // console.log(enemies.length)
        }
        // 3.3.8 用于检测敌机被撞击
        function hitEnemies(){
            for(var i = 0 ; i < enemies.length;i++){
                if(enemies[i].checkHit(hero)){//敌机和我方飞机
                    // 处理敌机撞击后的逻辑
                    enemies[i].bang()
                    // 处理我方飞机撞击后的逻辑
                    hero.bang()
                }
                for(var j = 0 ; j < bullets.length;j++){
                    // console.log(enemies[i].checkHit(bullets[j]))
                    if(enemies[i].checkHit(bullets[j])){//子弹和我方飞机
                        // 处理敌机撞击后的逻辑
                        enemies[i].bang()
                        // 处理子弹撞击后的逻辑
                        bullets[j].bang()
                    }
                }
            }
        }
        // 3.4 绘制游戏得分和我方飞机的生命值
        function paintText(){
            ctx.font = "bold 24px 微软雅黑"
            ctx.fillText("SCORE:"+score,10,30)
            ctx.fillText("LIFE:" + life,390,30)
        }
        // 4.游戏暂停
        canvas.onmouseover = function(){
            if(state == PAUSE){
                state = RUNNING
            }
        }
        canvas.onmouseout = function(){
            if(state == RUNNING){
                state = PAUSE
            }
        }
        var pause = new Image()
        pause.src = "./images/game_pause_nor.png"

        // 5.绘制gameover文本
        function paintOver(){
            ctx.font = "bold 48px 微软雅黑"
            ctx.fillText("GAME OVER",100,320)
        }
        // 游戏核心控制器
        setInterval(function(){
            sky.paint()
            sky.step()
            switch (state) {
                case START:
                    // 绘制logo
                    ctx.drawImage(logo,40,0)
                    break;
                case STARTING:
                    // 绘制加载动画效果
                    loading.paint()
                    // 加载动画
                    loading.step()
                    break;
                case RUNNING:
                    // 绘制我方飞机
                    hero.paint()
                    // 我方飞机动画
                    hero.step()
                    // 我方飞机射击
                    hero.shoot()
                    // 绘制子弹们
                    bulletsPaint()
                    // 子弹们运动
                    bulletsStep()
                    // 子弹删除
                    bulletsDel()
                    // 地方飞机创建
                    enterEnemies()
                    // 敌方飞机绘制
                    paintEnemies()
                    // 敌方飞机运动
                    stepEnemies()
                    // 敌机被删除
                    delEnemies()
                    // 检测是否被撞击
                    hitEnemies()
                    paintText()
                    break;
                case PAUSE :
                    hero.paint()
                    bulletsPaint()
                    paintEnemies()
                    paintText()
                    ctx.drawImage(pause,WIDTH / 2 - 30,HEIGHT / 2 - 20)
                    break;
                case GAMEOVER :
                    hero.paint()
                    bulletsPaint()
                    paintEnemies()
                    paintText()
                    paintOver()
                    break;
                default:
                    break;
            }
        },100)
    </script>
</body>
</html>
  • 25
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值