飞机大战(微信小游戏)

依旧是熟悉的期末作业,是微信小游戏官网的Demo来着,然后我边百度边对它做了一些改动

说起来好久没更新博客了......最近在用unity做游戏准备参加计算机设计大赛,花了好久终于做完初稿了哈哈哈哈嗝

顺祝看到这篇博客的各位新年快乐~

PS:完整工程下载及相关说明:https://download.csdn.net/download/weixin_41918712/10954323


用例图


效果图

      


步骤

PS:图片/音频的素材下载:https://pan.baidu.com/s/1NrSonSJnSjm3VxiEUD4iVw    提取码: q7ya

  1. 打开微信开发者工具,新建一个微信小游戏Demo,然后进入编辑界面。    

  2. 打开项目文件夹,将素材中的两个文件夹复制进项目文件夹里(替换)。
  3. 我们先把runtime里的3个脚本做些改动   ,分别是背景、文字UI显示和音乐控制。脚本详见下面【源码】。
  4. player文件夹里的index.js需要做些改动,主要是添加双发子弹的相关方法。  
  5. npc文件夹需要增加的内容就有些多了,主要是增加了血包、双发子弹的生成方法,以及分数达到88时会开始产生能够发射子弹的敌人。这些新加的脚本就在npc文件夹 右键 - 新建JS 就行。    
  6. 弄完上述这些,就可以改main.js、databus.js以及添加菜单脚本menu.js   
  7. 设置游戏默认入口为菜单页  
  8. 版本号  

源码

runtime文件夹

background.js

import Sprite from '../base/sprite'

const screenWidth  = window.innerWidth
const screenHeight = window.innerHeight

const BG_IMG_SRC   = 'images/bg.jpg'
const BG_WIDTH     = 512
const BG_HEIGHT    = 768

/**
 * 游戏背景类
 * 提供update和render函数实现无限滚动的背景功能
 */
export default class BackGround extends Sprite {
  constructor(ctx) {
    super(BG_IMG_SRC, BG_WIDTH, BG_HEIGHT)

    this.render(ctx)

    this.top = 0
  }

  update() {
    this.top += 2

    if ( this.top >= screenHeight )
      this.top = 0
  }

  /**
   * 背景图重绘函数
   * 绘制两张图片,两张图片大小和屏幕一致
   * 第一张漏出高度为top部分,其余的隐藏在屏幕上面
   * 第二张补全除了top高度之外的部分,其余的隐藏在屏幕下面
   */
  render(ctx) {
    ctx.drawImage(
      this.img,
      0,
      0,
      this.width,
      this.height,
      0,
      -screenHeight + this.top,
      screenWidth,
      screenHeight
    )

    ctx.drawImage(
      this.img,
      0,
      0,
      this.width,
      this.height,
      0,
      this.top,
      screenWidth,
      screenHeight
    )
  }
}

 

gameinfo.js

const screenWidth  = window.innerWidth
const screenHeight = window.innerHeight

let atlas = new Image()
atlas.src = 'images/Common.png' /*面板组件构成*/
let menupic = new Image()
menupic.src = 'images/menu.jpg' /*菜单构成*/

export default class GameInfo {
  renderGameScore(ctx, score) {
    ctx.fillStyle = "#ffffff"
    ctx.font      = "20px Arial"
    ctx.fillText(
      '? ',
      10,
      30
    )
    ctx.fillText(
      score,
      40,
      30
    )
  }
  //画出血量
  renderGameHp(ctx, hp) {
    ctx.fillStyle = "#ff0000"
    ctx.font = "20px Arial"
    ctx.fillText(
      '❤  ',
      13,
      70
    )
    ctx.fillText(
      hp,
      40,
      70
    )
    ctx.fillText(
      ' / 3',
      50,
      70
    )
  }
  renderGameOver(ctx, score) //游戏结束
  {
    ctx.drawImage(atlas, 0, 0, 119, 108, screenWidth / 2 - 150, screenHeight / 2 - 100, 300, 300)

    ctx.fillStyle = "#ffffff"
    ctx.font    = "20px Arial"

    ctx.fillText(
      '游戏结束',
      screenWidth / 2 - 40,
      screenHeight / 2 - 100 + 50
    )

    ctx.fillText(
      '歼敌数:  ' + score,
      screenWidth / 2 - 40,
      screenHeight / 2 - 100 + 130
    )

    /*面板绘制---红火火恍恍惚惚*/
    ctx.drawImage( 
      atlas,
      120, 6, 39, 24,
      screenWidth / 2 - 60,
      screenHeight / 2 - 100 + 150,
      120, 40
    )

    ctx.fillText  (
      '重新开始',
      screenWidth / 2 - 40,
      screenHeight / 2 - 100 + 175
    )

    ctx.drawImage(
      atlas,
      120, 6, 39, 24,
      screenWidth / 2 - 60,
      screenHeight / 2 - 100 + 200,
      120, 40
    )
    //返回菜单的ui布局
    ctx.fillText(
      '返回菜单',
      screenWidth / 2 - 40,
      screenHeight / 2 - 100 + 225
    )

    /**
     * 重新开始按钮区域
     * 方便简易判断按钮点击
     */
    this.btnArea_restart = {
      startX: screenWidth / 2 - 40,
      startY: screenHeight / 2 - 100 + 150,
      endX  : screenWidth / 2  + 50,
      endY  : screenHeight / 2 - 100 + 175
    }
    //返回菜单的按钮区域判断
    this.btnArea_remean = {
      startX: screenWidth / 2 - 40,
      startY: screenHeight / 2 - 100 + 200,
      endX: screenWidth / 2 + 50,
      endY: screenHeight / 2 - 100 + 225
    }
  }
  renderMenu(ctx) //k开始菜单
  {
    ctx.drawImage(menupic, 0, 0, screenWidth, screenHeight)
    /* *
     * 重新开始按钮区域
     * 方便简易判断按钮点击
     */
    this.btnArea_start = {
      startX: screenWidth / 2 - 100,
      startY: screenHeight / 2 - 100 + 225,
      endX: screenWidth / 2 + 100,
      endY: screenHeight / 2 - 100 + 400
    }
  }
}

music.js

let instance //变量定义

/**
 * 统一的音效管理器
 */
export default class Music {
  constructor() {
    if ( instance )
      return instance

    instance = this
/**音效 */
    this.bgmAudio = new Audio()
    this.bgmAudio.loop = true
    this.bgmAudio.src  = 'audio/bgm.mp3'

    this.shootAudio     = new Audio()
    this.shootAudio.src = 'audio/bullet.mp3'

    this.boomAudio     = new Audio()
    this.boomAudio.src = 'audio/boom.mp3'

    this.dieAudio = new Audio()
    this.dieAudio.src = 'audio/die.mp3'

    this.menuAudio = new Audio()
    this.menuAudio.loop = true
    this.menuAudio.src = 'audio/menu.mp3'

    this.bossAudio = new Audio()
    this.bossAudio.src = 'audio/bossbuttle.mp3'

    this.playBgm()
  }

  playBgm() 
  {
    this.bgmAudio.currentTime = 0
    this.bgmAudio.play()
    this.dieAudio.pause()
    this.menuAudio.pause()
  }
  playmenu() //播放菜单音乐
  {
    this.menuAudio.currentTime = 0
    this.menuAudio.play()
    this.bgmAudio.pause()
    this.dieAudio.pause()
  }
  stopBgm()
  {
    this.bgmAudio.pause()
  }

  playShoot() {
    this.shootAudio.currentTime = 0
    this.shootAudio.play()
  }
  playboss()
  {
    this.bossAudio.currentTime = 0
    this.bossAudio.play()
  }

  playExplosion() {
    this.boomAudio.currentTime = 0
    this.boomAudio.play()
  }

  playDie() {
    this.dieAudio.currentTime = 0
    this.dieAudio.play()
  }
}

runtime文件夹

index.js

import Sprite   from '../base/sprite'
import Bullet   from './bullet'
import DataBus  from '../databus'
//import main from '../main'

const screenWidth    = window.innerWidth
const screenHeight   = window.innerHeight
let skill = 0;  //获取15发双弹头
// 玩家相关常量设置
const PLAYER_IMG_SRC = 'images/hero.png'
const PLAYER_WIDTH   = 80
const PLAYER_HEIGHT  = 80

let databus = new DataBus()
//let main1=new main()


export default class Player extends Sprite {
  constructor() {

    super(PLAYER_IMG_SRC, PLAYER_WIDTH, PLAYER_HEIGHT)

    // 玩家默认处于屏幕底部居中位置
    this.x = screenWidth / 2 - this.width / 2
    this.y = screenHeight - this.height - 30

    // 用于在手指移动的时候标识手指是否已经在飞机上了
    this.touched = false

    this.bullets = []

    // 初始化事件监听
    this.initEvent()
  }

  /**
   * 当手指触摸屏幕的时候
   * 判断手指是否在飞机上
   * @param {Number} x: 手指的X轴坐标
   * @param {Number} y: 手指的Y轴坐标
   * @return {Boolean}: 用于标识手指是否在飞机上的布尔值
   */
  checkIsFingerOnAir(x, y) {
    const deviation = 30

    return !!(   x >= this.x - deviation
              && y >= this.y - deviation
              && x <= this.x + this.width + deviation
              && y <= this.y + this.height + deviation  )
  }

  /**
   * 根据手指的位置设置飞机的位置
   * 保证手指处于飞机中间
   * 同时限定飞机的活动范围限制在屏幕中
   */
  setAirPosAcrossFingerPosZ(x, y) {
    let disX = x - this.width / 2
    let disY = y - this.height / 2

    if ( disX < 0 )
      disX = 0

    else if ( disX > screenWidth - this.width )
      disX = screenWidth - this.width

    if ( disY <= 0 )
      disY = 0

    else if ( disY > screenHeight - this.height )
      disY = screenHeight - this.height

    this.x = disX
    this.y = disY
  }

  /**
   * 玩家响应手指的触摸事件
   * 改变战机的位置
   */
  initEvent() {
    canvas.addEventListener('touchstart', ((e) => { //封装的指控方法
      e.preventDefault() //匿名方法

      let x = e.touches[0].clientX
      let y = e.touches[0].clientY

      //
      if ( this.checkIsFingerOnAir(x, y) ) {
        this.touched = true

        this.setAirPosAcrossFingerPosZ(x, y)
      }

    }).bind(this))

    canvas.addEventListener('touchmove', ((e) => { //封装的指控方法
      e.preventDefault() //匿名方法

      let x = e.touches[0].clientX
      let y = e.touches[0].clientY

      if ( this.touched )
        this.setAirPosAcrossFingerPosZ(x, y)

    }).bind(this))

    canvas.addEventListener('touchend', ((e) => { //封装的指控方法
      e.preventDefault() //匿名方法

      this.touched = false
    }).bind(this))
  }

  /**
   * 玩家射击操作
   * 射击时机由外部决定
   */
  diffbuttle()  //双弹头每秒消耗一个
  {
    if(skill>0) skill--;
  }
  addbuttle() //增加双弹头
  {
    skill+=15;
  }
  defaultbuttle()
  {
    skill=0;
  }
  shoot() //射击
  {
    let bullets = []
    let bulletNum;
    if (skill > 0) {
      bulletNum = 2; //子弹数
    }
    else bulletNum = 1;
    for (let i = 0; i < bulletNum; i++)
      bullets.push(databus.pool.getItemByClass('bullet', Bullet))

    bullets.forEach((bullet, index) => {
      bullet.init(
        this.x + this.width * (index + 1) / (bulletNum + 1) - bullet.width / 2,
        this.y - 10,
        10 
      )
      databus.bullets.push(bullet)
    })

  }
}

npc文件夹

enemy.js

import Animation from '../base/animation'
import DataBus   from '../databus'

const ENEMY_IMG_SRC = 'images/enemy.png'
const ENEMY_WIDTH   = 60
const ENEMY_HEIGHT  = 60

/** 速度初始 */
const __ = {
  speed: Symbol('speed')
}


let databus = new DataBus()

function rnd(start, end){
  return Math.floor(Math.random() * (end - start) + start)
}

export default class Enemy extends Animation {
  constructor() {
    super(ENEMY_IMG_SRC, ENEMY_WIDTH, ENEMY_HEIGHT)
    this.initExplosionAnimation()
  }
  
  /** 速度值初始化 */
  init(speed) {
    this.x = rnd(0, window.innerWidth - ENEMY_WIDTH)
    this.y = -this.height

    this[__.speed] = speed 

    this.visible = true
  }

  // 预定义爆炸的帧动画
  initExplosionAnimation() {
    let frames = []

    const EXPLO_IMG_PREFIX  = 'images/explosion'
    const EXPLO_FRAME_COUNT = 19

    for ( let i = 0;i < EXPLO_FRAME_COUNT;i++ ) {
      frames.push(EXPLO_IMG_PREFIX + (i + 1) + '.png')
    }

    this.initFrames(frames)
  }

  // 每一帧更新敌人位置
  update() {
    this.y += this[__.speed]

    // 对象回收
    if ( this.y > window.innerHeight + this.height )
      databus.removeEnemey(this)
  }
}

 

getskill.js


import Animation from '../base/animation'
import DataBus from '../databus'

const SKILL_IMG_SRC = 'images/upbull.png'
const SKILL_WIDTH = 30
const SKILL_HEIGHT = 40

/** 速度初始 */
const __ = {
  speed: Symbol('speed')
}

let databus = new DataBus()

function rnd(start, end) {
  return Math.floor(Math.random() * (end - start) + start)
}

export default class Getskill extends Animation {
  constructor() {
    super(SKILL_IMG_SRC, SKILL_WIDTH, SKILL_HEIGHT) //初始化技能
  }

  /** 速度值初始化 */
  init(speed) {
    this.x = rnd(0, window.innerWidth - SKILL_WIDTH)
    this.y = -this.height

    this[__.speed] = speed

    this.visible = true
  }

  // 每一帧更新敌人位置
  update() {
    this.y += this[__.speed]

    // 对象回收
    if (this.y > window.innerHeight + this.height)
      databus.removeSkill(this)
  }
}

 

blood.js


import Animation from '../base/animation'
import DataBus from '../databus'

const BLOOD_IMG_SRC = 'images/blood.png'
const BLOOD_WIDTH = 60
const BLOOD_HEIGHT = 60

/** 速度初始 */
const __ = {
  speed: Symbol('speed')
}

let databus = new DataBus()

function rnd(start, end) {
  return Math.floor(Math.random() * (end - start) + start)
}

export default class Blood extends Animation {
  constructor() {
    super(BLOOD_IMG_SRC, BLOOD_WIDTH, BLOOD_HEIGHT) //初始化技能
  }

  /** 速度值初始化 */
  init(speed) {
    this.x = rnd(0, window.innerWidth - BLOOD_WIDTH)
    this.y = -this.height

    this[__.speed] = speed

    this.visible = true
  }

  // 每一帧更新敌人位置
  update() {
    this.y += this[__.speed]

    // 对象回收
    if (this.y > window.innerHeight + this.height)
      databus.removeblood(this)
  }
}

 

smallboss.js

import Animation from '../base/animation'
import DataBus from '../databus'
import BoBullet from './BoBullet'

const SMB_IMG_SRC = 'images/boss.png'
const SMB_WIDTH = 80
const SMB_HEIGHT = 80

/** 速度初始 */
const __ = {
  speed: Symbol('speed')
}


let databus = new DataBus()
//let isdie=false

function rnd(start, end) {
  return Math.floor(Math.random() * (end - start) + start)
}

export default class SBoss extends Animation {
  constructor() {
    super(SMB_IMG_SRC, SMB_WIDTH, SMB_HEIGHT)
    this.initSMBAnimation()
  }

  /** 速度值初始化 */
  init(speed) {
    this.x = rnd(0, window.innerWidth - SMB_WIDTH)
    this.y = -this.height

    this[__.speed] = speed

    this.visible = true
  }

  // 预定义爆炸的帧动画
  initSMBAnimation() {
    let frames = []

    const SMB_IMG_PREFIX = 'images/explosion'
    const SMB_FRAME_COUNT = 19

    for (let i = 0; i < SMB_FRAME_COUNT; i++) {
      frames.push(SMB_IMG_PREFIX + (i + 1) + '.png')
    }

    this.initFrames(frames)
  }

  // 每一帧更新敌人位置
  update() {
    this.y += this[__.speed]

    // 对象回收
    if (this.y > window.innerHeight + this.height)
      databus.removeSBoss(this)
  }
  bossshoot() //敌人攻击
  {
    let bullet = databus.pool.getItemByClass('bobullet', BoBullet)
    bullet.init //初始化子弹
      (
      this.x + this.width / 2 - bullet.width / 2,
      this.y + 20,
      10
      )
    databus.bossbullets.push(bullet)
  }
}

 

BoBullet.js

import Sprite from '../base/sprite'
import DataBus from '../databus'

const BossBULLET_IMG_SRC = 'images/bossbu.png'
const BossBULLET_WIDTH = 20
const BossBULLET_HEIGHT = 34

const __ = {
  speed: Symbol('speed')
}

let databus = new DataBus()

export default class BoBullet extends Sprite {
  constructor() 
  {
    super(BossBULLET_IMG_SRC, BossBULLET_WIDTH, BossBULLET_HEIGHT)
   // this.initSMBAnimation()
  }

  init(x, y, speed) {
    this.x = x
    this.y = y

    this[__.speed] = speed

    this.visible = true
  }
  /*// 预定义爆炸的帧动画
  initSMBAnimation() {
    let frames = []

    const SMB_IMG_PREFIX = 'images/explosion'
    const SMB_FRAME_COUNT = 19

    for (let i = 0; i < SMB_FRAME_COUNT; i++) {
      frames.push(SMB_IMG_PREFIX + (i + 1) + '.png')
    }

    this.initFrames(frames)
  }*/
  // 每一帧更新子弹位置
  update() {
    this.y += this[__.speed]

    // 超出屏幕外回收自身
    if (this.y > window.innerHeight + this.height)
      databus.removeBoBullets(this)
  }
}

主函数

databus.js

import Pool from './base/pool'

let instance

/**
 * 全局状态管理器
 */
export default class DataBus {
  constructor() {
    if ( instance )
      return instance

    instance = this

    this.pool = new Pool()

    this.reset()
  }

  reset() {
    this.frame      = 0
    this.score      = 0
    this.bullets    = []
    this.enemys     = []
    this.animations = []
    this.gameOver   = false
  }

  /**
   * 回收敌人,进入对象池
   * 此后不进入帧循环
   */
  removeEnemey(enemy) {
    let temp = this.enemys.shift()

    temp.visible = false

    this.pool.recover('enemy', enemy)
  }

  /**
   * 回收子弹,进入对象池
   * 此后不进入帧循环
   */
  removeBullets(bullet) {
    let temp = this.bullets.shift()

    temp.visible = false

    this.pool.recover('bullet', bullet)
  }
}

 

main.js

import Player from './player/index'
import Sboss from './npc/smallboss'
import Enemy from './npc/enemy'
import GetSkill from './npc/getskill'
import Blood from './npc/blood'
import BackGround from './runtime/background'
import GameInfo from './runtime/gameinfo'
import Music from './runtime/music'
import DataBus from './databus'
import BoBullet from './npc/BoBullet'
import Menu from './menu'

let ctx = canvas.getContext('2d')
let databus = new DataBus()
//let skill=0  //获取双弹头
/**
 * 游戏主函数
 */
export default class Main {
  constructor() {
    this.restart()
  }

  /**开始ui绘制 */

  /**重新开始的初始化 */
  restart() {
    databus.reset()
    canvas.removeEventListener(
      'touchstart',
      this.touchHandler
    )
    this.bg = new BackGround(ctx)
    this.player = new Player(ctx)
    this.gameinfo = new GameInfo()
    this.music = new Music()
    this.music.playBgm()
    this.player.defaultbuttle()
    window.requestAnimationFrame(
      this.loop.bind(this),
      canvas
    )
  }


  /**
   * 随着帧数变化的敌机生成逻辑
   * 帧数取模定义成生成的频率
   */
  enemyGenerate() { //每秒30帧出现一次
    if (databus.frame % 30 === 0)  //databus.frame % 30 === 0
    {
      let enemy = databus.pool.getItemByClass('enemy', Enemy)
      enemy.init(5) //speed初始化
      databus.enemys.push(enemy)
    }
  }
  sbossGenerate() { //每秒30帧1/3概率出现一次大怪
    if (databus.score == 88) wx.showToast({ title: '难度提高...' });
    if (databus.score > 88) {
      if (databus.frame % 30 === 0)  //databus.frame % 30 === 0
      {
        let num = Math.floor(Math.random() * 2) //0--3
        if (num == 1) {
          let sboss = databus.pool.getItemByClass('sboss', Sboss)
          sboss.init(4) //speed初始化
          databus.sboss.push(sboss)
        }
      }
    }
  }

  //技能生成
  skillGenerate() {
    let num = Math.floor(Math.random() * 1000) //0--10
    if (num < 2) {
      let skill = databus.pool.getItemByClass('skill', GetSkill)
      skill.init(6)
      databus.skills.push(skill)
    }
  }
  //血块生成
  bloodGenerate() {
    let num = Math.floor(Math.random() * 1000) //0--10
    if (num < 1) {
      let blood = databus.pool.getItemByClass('blood', Blood)
      blood.init(6)
      databus.bloods.push(blood)
    }
  }


  // 全局碰撞检测
  collisionDetection() {
    let that = this

    databus.bullets.forEach((bullet) => {
      for (let i = 0, il = databus.enemys.length; i < il; i++) //子弹打小怪
      {
        let enemy = databus.enemys[i]

        if (!enemy.isPlaying && enemy.isCollideWith(bullet)) {
          enemy.playAnimation()
          that.music.playExplosion()

          bullet.visible = false
          databus.score += 1
          break
        }
      }
      for (let i = 0, il = databus.sboss.length; i < il; i++)//子弹打大怪
      {
        let sboss = databus.sboss[i]

        if (!sboss.isPlaying && sboss.isCollideWith(bullet)) {
          sboss.playAnimation()
          that.music.playExplosion()
          bullet.visible = false
          databus.score += 2
          databus.removeSBoss(sboss)
          break
        }
      }
      for (let i = 0, il = databus.bossbullets.length; i < il; i++) //子弹打子弹
      {
        let enemy = databus.bossbullets[i]
        if (enemy.isCollideWith(bullet)) {
          that.music.playExplosion()
          bullet.visible = false
          enemy.visible = false
          break
        }
      }
    })
    //获得技能
    for (let i = 0, il = databus.skills.length; i < il; i++) {
      let skill = databus.skills[i]

      if (this.player.isCollideWith(skill)) {
        this.player.addbuttle()
        skill.visible = false
        wx.showToast({ title: '获得双倍火力!' })
        break
      }
    }
    //获得血块
    for (let i = 0, il = databus.bloods.length; i < il; i++) {
      let blood = databus.bloods[i]

      if (this.player.isCollideWith(blood)) {
        blood.visible = false
        if (databus.hp < 3) {
          wx.showToast({ title: '获得生命回复!' })
          databus.hp += 1
        }
        break
      }
    }

    /**游戏失败 */
    for (let i = 0, il = databus.enemys.length; i < il; i++) {
      let enemy = databus.enemys[i]

      if (this.player.isCollideWith(enemy)) {
        enemy.playAnimation()
        that.music.playExplosion()
        databus.hp -= 1
        if (databus.hp <= 0) databus.gameOver = true
        break
      }
    }
    for (let i = 0, il = databus.bossbullets.length; i < il; i++) //被敌人子弹打到
    {
      let enemy = databus.bossbullets[i]
      if (this.player.isCollideWith(enemy)) {
        //enemy.playAnimation()
        that.music.playExplosion()
        databus.hp -= 1
        enemy.visible = false;
        if (databus.hp <= 0) databus.gameOver = true
        break
      }
    }
    for (let i = 0, il = databus.sboss.length; i < il; i++) {
      let sboss = databus.sboss[i]

      if (this.player.isCollideWith(sboss)) {
        sboss.playAnimation()
        that.music.playExplosion()
        databus.hp -= 1
        databus.removeSBoss(sboss)
        if (databus.hp <= 0) databus.gameOver = true
        break
      }
    }
  }

  //游戏结束后的触摸事件处理逻辑

  touchEventHandler(e) {
    e.preventDefault()

    let x = e.touches[0].clientX
    let y = e.touches[0].clientY
    //获取结束时按钮面板信息
    let area = this.gameinfo.btnArea_restart
    let area_mean = this.gameinfo.btnArea_remean
    //按钮事件监听
    if (x >= area.startX
      && x <= area.endX
      && y >= area.startY
      && y <= area.endY)
      this.restart()
    if (x >= area_mean.startX
      && x <= area_mean.endX
      && y >= area_mean.startY
      && y <= area_mean.endY) {
      canvas.removeEventListener(
        'touchstart',
        this.touchHandler
      )
      new Menu()
    }

  }

  /**
   * canvas重绘函数
   * 每一帧重新绘制所有的需要展示的元素
   */
  render() {
    ctx.clearRect(0, 0, canvas.width, canvas.height)

    this.bg.render(ctx)

    databus.bullets
      .concat(databus.enemys)
      .concat(databus.skills)
      .concat(databus.bloods)
      .concat(databus.sboss)
      .concat(databus.bossbullets)
      .forEach((item) => {
        item.drawToCanvas(ctx)
      })

    this.player.drawToCanvas(ctx)

    databus.animations.forEach((ani) => {
      if (ani.isPlaying) {
        ani.aniRender(ctx)
      }
    })

    this.gameinfo.renderGameScore(ctx, databus.score)
    this.gameinfo.renderGameHp(ctx, databus.hp)
  }

  // 游戏逻辑更新主函数
  update() {
    this.bg.update()

    databus.bullets
      .concat(databus.enemys)
      .concat(databus.skills)
      .concat(databus.bloods)
      .concat(databus.bossbullets)
      .concat(databus.sboss)
      .forEach((item) => {
        item.update()
      })

    this.enemyGenerate()
    this.skillGenerate()
    this.bloodGenerate()
    this.collisionDetection()
    this.sbossGenerate()
  }

  // 实现游戏帧循环
  loop() {

    databus.frame++
    this.update()
    this.render()
    if (databus.frame % 20 === 0) {
      this.player.shoot()
      this.player.diffbuttle()
      this.music.playShoot()
      for (let i = 0, il = databus.sboss.length; i < il; i++) {
        //if (databus.sboss[i].IsDie()) continue;
        let num = Math.floor(Math.random() * 4) //0--3
        if (num == 1) {
          let sboss = databus.sboss[i]
          sboss.bossshoot()
          this.music.playboss()
        }
      }
    }
    // 游戏结束停止帧循环
    if (databus.gameOver) {
      this.gameinfo.renderGameOver(ctx, databus.score)
      this.music.playDie()
      this.music.stopBgm()
      this.touchHandler = this.touchEventHandler.bind(this)
      canvas.addEventListener('touchstart', this.touchHandler)
      return
    }

    window.requestAnimationFrame(
      this.loop.bind(this),
      canvas
    )
  }
}

 

menu.js

import GameInfo from './runtime/gameinfo'
import Music from './runtime/music'

import Main from './main'
let ctx = canvas.getContext('2d')

/**
 * 游戏主函数
 */
export default class Menu
{
  constructor() 
  {
    
    this.remenu()
  }
  /**菜单绘制 */
  remenu()
  {
    this.music = new Music()
    this.gameinfo = new GameInfo()
    this.music.playmenu()
    this.gameinfo.renderMenu(ctx)
    this.touchHandler = this.touchEventHandler.bind(this)
    canvas.addEventListener('touchstart', this.touchHandler)
  }
  /**菜单按钮监听 */
  //游戏结束后的触摸事件处理逻辑

  touchEventHandler(e) {
    e.preventDefault()

    let x = e.touches[0].clientX
    let y = e.touches[0].clientY
    //获取结束时按钮面板信息
    let area = this.gameinfo.btnArea_start
    //按钮事件监听
    if (x >= area.startX  && x <= area.endX  && y >= area.startY  && y <= area.endY)
    {
      canvas.removeEventListener(
        'touchstart',
        this.touchHandler
      )
      new Main()
    }
  }
}

入口

game.js

import './js/libs/weapp-adapter'
import './js/libs/symbol'

import Main from './js/main'
import Menu from './js/menu'


new Menu()

 

 


 ✎﹏﹏₯㎕《晴天》花落的那一天...﹍﹍﹍﹍﹍﹍

  • 4
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 11
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值