IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> 使用canvas编写飞机大战游戏 -> 正文阅读

[游戏开发]使用canvas编写飞机大战游戏

使用canvas编写飞机大战小游戏

本文章讲述如何使用canvas实现一个飞机大战的小游戏,游戏我取名为fight-AO(点击查看源码)实现可以发射子弹、击败怪兽、自动生成怪兽…怪兽名字为AO,击败AO的叫做Superman。点击试玩



前言

在了解如何编写游戏过程时,你需要知道vue框架element ui帮助你快速快速开发。


一、组件版本

├─┬ @vue/cli-plugin-babel@4.5.13
│ └─┬ @vue/babel-preset-app@4.5.13
│   └── vue@2.6.14 deduped
├─┬ element-ui@2.15.3 -> ./node_modules/_element-ui@2.15.3@element-ui
│ └── vue@2.6.14 deduped
└── vue@2.6.14

二、项目结构

.
├── README.md
├── babel.config.js
├── package-lock.json
├── package.json
├── public						#开放的资源
│   ├── favicon.ico
│   ├── fight-AO
│   │   ├── AO.png
│   │   └── superman.png
│   └── index.html
└── src							#源文件
    ├── App.vue
    ├── assets					#项目资源文件
    │   └── logo.png
    ├── components				#vue组件
    │   └── fight-AO.vue		#本文章讲解的文件
    ├── main.js					#项目主配置
    └── router					#vue组件路由
        └── router.js

三、代码&思路讲解

以下讲解对编写游戏过程的讲解,不会深入剖析底层代码。

1. 使用canvas

1.1 代码

<canvas id="canvas"></canvas>

//样式
<style scoped>
	#canvas {
	  margin: 0;
	  background-image: url("../../public/fight-AO/sky.jpeg");
	}
</style>

//获取canvas
this.canvas = document.getElementById('canvas');
this.ctx = this.canvas.getContext('2d');
//宽和高指定为页面布局大小
this.canvas.width = window.innerWidth;
this.canvas.height = window.innerHeight - 50;



1.2 知识点


2. 移动超人

2.1 代码

	
	//超人外形
    this.supermanImg.src = '/fight-AO/superman.png';
    
    //超人初始位置
    this.supermanX = (this.canvas.width - this.supermanW) / 2;
    this.supermanY = (this.canvas.height - this.supermanH) / 1.2;

    //外形加载完成后设置superman初始位置
    this.supermanImg.onload = () => {
      this.supermanMove();
    }

    //监听走位
    window.onkeydown = this.keyDown;
    window.ontouchmove = this.touchmove;
    
    /**
     * 移动超人
     */
    supermanMove() {
      this.moveArr.push({x: this.supermanX, y: this.supermanY, w: this.supermanW, h: this.supermanH})
      //如果有移动就删除指定位置
      if (this.moveArr.length > 1) {
        let m = this.moveArr[this.moveArr.length - 2];
        this.ctx.clearRect(m.x, m.y, m.w, m.h);
      }
      let m = this.moveArr[this.moveArr.length - 1];
      this.ctx.drawImage(this.supermanImg, m.x, m.y, m.w, m.h);

      //清除以前步数
      for (let i = 0; i < this.moveArr.length - 3; i++) {
        delete this.moveArr[i];
      }
    }
    /**
     * 键盘事件
     */
    keyDown(event) {
      if (!this.checkActionStartStatus()) return;
      switch (event.keyCode) {  // 获取当前按下键盘键的编码
        case 32 :  // 按空格暂停
          break;
        case 37 :  // 按下左箭头键,向左移动
          this.supermanX -= this.speed;
          break;
        case 39 :  // 按下右箭头键,向右移动
          this.supermanX += this.speed;
          break;
        case 38 :  // 按下上箭头键,向上移动
          this.supermanY -= this.speed;
          break;
        case 40 :  // 按下下箭头键,向下移动
          this.supermanY += this.speed;
          break;
          // default:
          //     return;
      }
      //是否超出边界
      if (this.supermanX >= this.canvas.width - (this.supermanW / 2)) {
        this.supermanX -= this.speed;
      } else if (this.supermanX + (this.supermanW / 2) <= 0) {
        this.supermanX += this.speed;
      } else if (this.supermanY >= this.canvas.height - this.supermanW) {
        this.supermanY -= this.speed;
      } else if (this.supermanY <= 0) {
        this.supermanY += this.speed;
      }
      this.supermanMove();
    },
    /**
     * 手指摁下
     * @param event
     */
    touchmove(event) {
      if (!this.checkActionStartStatus()) return;
      let moveX = event.changedTouches[0].pageX;
      let moveY = event.changedTouches[0].pageY;
      this.supermanX = moveX;
      this.supermanY = moveY;
      this.supermanMove();
    }

2.2 知识点

  • 通过速度this.speed改变超人移动的速度,只有在键盘事件有用,玩家操作上下左右分别对应y-speed, y+speed, x-speed, x+speed。改变this.supermanX,this.supermanY的值超人就可以轻易的移动。
  • supermanMove()函数封装了记录this.moveArr超人位置,this.ctx.clearRect清除上一次显示位置,this.ctx.drawImage画出当前移动位置,清除历史移动delete this.moveArr[i];

3. 发射炮弹cannonball

3.1 代码


    //启动炮弹
    setInterval(() => {
      if (this.checkActionStartStatus()) {
        this.cannonball();
        this.upup = 10;
      }
    }, 300)

    /**
     * 炮弹
     */
    cannonball() {
      let cannonballArr = []

      //先把y画出来 当前y-upup直到小于0 y越小就越往上 直到小于-AO的高度
      while (this.supermanY - this.upup > -this.aoHeight) {
        this.upup += this.cannonballSpace;//加上每个炮弹的间距
        //记录当前这组炮弹发射的轨迹
        cannonballArr.push({x: this.supermanX + (this.supermanW / 2), y: this.supermanY - this.upup});
      }

      let index = 0;//炮弹位置
      let clearCannonball = 0;//清除多余炮弹
      let cannonballNum = this.cannonballNum;//炮弹数量
      //通过定时器实现炮弹动起来的效果
      let interval = setInterval(() => {
        //炮弹发完了 只有发射一次炮弹才会走这个判断 避免浪费资源
        if (cannonballArr.length === 0) {
          clearInterval(interval);
          return;
        }

        //到顶了炮弹看不见了
        if (cannonballNum >= cannonballArr.length) {
          cannonballNum = cannonballArr.length - 1;
        }

        //清除历史炮弹 
        // console.log(index + '===' + cannonballNum + '====' + clearCannonball + '====' + cannonballArr.length)
        if (index >= cannonballNum && clearCannonball < cannonballArr.length) {
          let arrElement = cannonballArr[clearCannonball];
          this.ctx.clearRect(arrElement.x, arrElement.y + this.cannonballHeight,
              this.cannonballWidth + this.cannonballWidth / 2,
              this.cannonballSpace)
          clearCannonball++;
        }

        //炮弹发完了
        if (index >= cannonballArr.length - 1) {
          if (clearCannonball > cannonballArr.length - 1) clearInterval(interval);
        } else {
          //发射炮弹
          let arrElement = cannonballArr[index];
          if (!arrElement) return;
          //炮弹的样式
          this.ctx.fillStyle = "rgb(200,100,200)"
          this.ctx.fillRect(arrElement.x, arrElement.y, this.cannonballWidth, this.cannonballHeight)

          //清除啊哦 这段判断主要是消灭AO
          if (this.aoArr.length > 0) {
            // let arrElement = cannonballArr[cannonballArr.length - 1 - index];
            //查找啊哦的位置
            let aoObj = this.aoArr.find(o =>
                //把区间放大
                (o.x + this.aoWidth) >= arrElement.x && (o.x) <= arrElement.x
                && o.y + this.aoWidth >= arrElement.y && o.y - this.aoHeight <= arrElement.y
            );

            if (aoObj) {
              this.aoDieNum++;
              // console.log(index + '===' + arrElement.x + '===' + arrElement.y)
              this.ctx.clearRect(aoObj.x, aoObj.y, this.aoWidth, this.aoHeight)
              aoObj.x = 1000;
              aoObj.y = 1000;
              this.resetAO();
            }
          }
          index++;
        }
      }, 30);
    }

3.2 知识点

  • this.upup一组炮弹移动间隙,通过它来改变y值的变化让AO动起来
  • this.cannonballSpace每个炮弹之间的空间,让炮弹看起来更加有节奏
  • this.cannonballNum每次发射的炮弹数量

4. AO小怪兽

4.1 代码

    this.aoImg1.src = "/fight-AO/AO01.png"
    this.aoImg2.src = "/fight-AO/AO02.png"
    this.aoImg3.src = "/fight-AO/AO03.png"
    this.aoImg4.src = "/fight-AO/AO04.png"
    this.aoImg5.src = "/fight-AO/AO05.png"

    //初始化啊哦
    this.AO();

    //开启AO移动速度 50ms移动一次
    setInterval(() => {
      //检查游戏是否开始 如果以开始就判断超人是否已触碰到AO游戏结束
      if (this.checkActionStartStatus()) {
        //查找啊哦的位置
        let aoObj = this.aoArr.find(o =>
            //把区间放大
            o.x + (this.aoWidth / 2) >= this.supermanX && o.x - (this.aoWidth * 1.1) <= this.supermanX
            && o.y + this.aoHeight >= this.supermanY && o.y - this.aoHeight <= this.supermanY
        );
        //是否已触碰到超人
        if (aoObj) {
          //游戏暂停
          this.actionPause();
          //提示游戏结束
          this.$alert('游戏结束💀重新开始', '提示', {
            confirmButtonText: '确定',
            showClose: false,
            customClass: 'hintAlert',
            callback: () => {
              location.reload();
            }
          });
        }
        //重置AO 让它开起来是在移动的
        this.resetAO();
      }
    }, 50);


    // 被消灭者【啊哦】
    AO() {
      let AOAO = setInterval(() => {
        this.setDataStatus = false;
        if (!this.checkActionStartStatus()) return;
        //AO是否已经全部出现
        if (this.aoNum < 1) {
          clearInterval(AOAO);
          //ao已被初始化还完成可以设置
          this.setDataStatus = true;
          return;
        }
        //随机出现位置
        let numX = Math.round(Math.random() * (this.canvas.width - 5)) + 5;
        let numY = -this.aoHeight;//Math.round(Math.random() * 50)
        // console.log(numX + '==' + numY)
        let random = Math.round(Math.random() * 5);
        this.drawAO(random, numX, numY);
        this.aoArr.push({x: numX, y: numY, random: random})
        this.aoNum--;
      }, 500)
    }
    /**
     * 画啊哦 在任意一个case画一个AO 每个case的AO样式是不同的
     */
    drawAO(random, numX, numY) {
      switch (random) {
        case 1:
          this.ctx.drawImage(this.aoImg1, numX, numY, this.aoWidth, this.aoHeight)
          break;
        case 2:
          this.ctx.drawImage(this.aoImg2, numX, numY, this.aoWidth, this.aoHeight)
          break;
        case 3:
          this.ctx.drawImage(this.aoImg3, numX, numY, this.aoWidth, this.aoHeight)
          break;
        case 4:
          this.ctx.drawImage(this.aoImg4, numX, numY, this.aoWidth, this.aoHeight)
          break;
        case 5:
          this.ctx.drawImage(this.aoImg5, numX, numY, this.aoWidth, this.aoHeight)
          break;
      }
    },
    /**
     *让AO往左移动还是右
     * @param i ao位置
     * @param type 1加 右 , 2减 左
     */
    leftOrRight(i, type) {
      switch (type) {
        case 1:
          this.aoArr[i].x += Math.round(Math.random() * 2)
          break;
        case 2:
          this.aoArr[i].x -= Math.round(Math.random() * 2)
          break;
      }
    },
    /**
     * 1、补全未被消灭的啊哦 2、AO互相之间触碰他是被误伤的 3、让啊哦持续动起来
     */
    resetAO() {
      //先清当前位置的AO
      for (let i = 0; i < this.aoArr.length; i++) {
        this.ctx.clearRect(this.aoArr[i].x, this.aoArr[i].y, this.aoWidth, this.aoHeight)
      }
      //重新渲染新的AO
      for (let i = 0; i < this.aoArr.length; i++) {
        //改变x轴移动位置 计算x轴的位置占总宽的多少
        let xPosition = (this.aoArr[i].x + this.aoWidth) / this.canvas.width;
        //随机以下往左还是右
        let type = Math.round(Math.random() * 2);
        //定义了几种简单的移动规则
        if (xPosition > 0 && 0.1 > xPosition) {
          this.leftOrRight(i, type);
        } else if (xPosition > 0.1 && 0.2 > xPosition) {
          this.leftOrRight(i, type);
        } else if (xPosition > 0.2 && 0.3 > xPosition) {
          this.leftOrRight(i, type);
        } else if (xPosition > 0.3 && 0.5 > xPosition) {
          this.leftOrRight(i, type);
        } else if (xPosition > 0.5 && 0.7 > xPosition) {
          this.leftOrRight(i, 2);
        } else if (xPosition > 0.7 && 1 > xPosition) {
          this.leftOrRight(i, 2);
        } else {
          this.leftOrRight(i, 2);
        }
        //让AO在页面上显示出来
        this.drawAO(this.aoArr[i].random, this.aoArr[i].x, this.aoArr[i].y++)
      }
    }

4.2 知识点

  • this.aoNum AO生成数量
  • resetAO() 函数可以更友好地显示重叠在一起的AO不被互相清除,通过先清空当前显示的AO,在计算下一个位置的AO。AO()函数只有在第一次初始化AO的时候才是有用的。虽然两个函数都是可以生成AO的逻辑,但是他们虽执行的业务逻辑是不一样的。AO()初始化,resetAO()改变数据。
  • AO的行动轨迹只是一个和简单的区域划分,这个地方可以提前生成每一个AO的行动轨迹不应该每次都要重新判断AO下一步的位置应该是哪。TODO

四、总结

以上就是使用canvas来实现这个游戏的主要代码逻辑,使用到的知识都是通过查看canvas官网上的demo来进行学习,主要用的函数有ctx.fillRect绘制矩形,ctx.drawImage绘制图像,ctx.clearRect清除指定矩形区域(让清除部分完全透明),监听键盘事件,触屏摁下事件。这个游戏设计想法有参考微信小游戏一些的实现,AO的命名想法来源是AI Object,这个怪兽可以是任意一个对象通过AI让他赋予生命。一种简单的2d游戏还是可以通过js来实现的,在编写的过程中很好玩。

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2021-08-05 17:40:59  更:2021-08-05 17:41:51 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/6 7:13:33-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码