• 微信小游戏 demo 飞机大战 代码分析 (三)(spirit.js, animation.js)


    微信小游戏 demo 飞机大战 代码分析(三)(spirit.js, animation.js)

    微信小游戏 demo 飞机大战 代码分析(一)(main.js)

    微信小游戏 demo 飞机大战 代码分析(二)(databus.js)

    微信小游戏 demo 飞机大战 代码分析(四)(enemy.js, bullet.js, index.js)

    本博客将使用逐行代码分析的方式讲解该demo,本文适用于对其他高级语言熟悉,对js还未深入了解的同学,博主会尽可能将所有遇到的不明白的部分标注清楚,若有不正确或不清楚的地方,欢迎在评论中指正

    本文的代码均由微信小游戏自动生成的demo飞机大战中获取

    spirit.js

    游戏基础的精灵类

    代码

    /**
     * 游戏基础的精灵类
     */
    export default class Sprite {
      constructor(imgSrc = '', width=  0, height = 0, x = 0, y = 0) {
        this.img     = new Image()
        this.img.src = imgSrc
    
        this.width  = width
        this.height = height
    
        this.x = x
        this.y = y
    
        this.visible = true
      }
    
      /**
       * 将精灵图绘制在canvas上
       */
      drawToCanvas(ctx) {
        if ( !this.visible )
          return
    
        ctx.drawImage(
          this.img,
          this.x,
          this.y,
          this.width,
          this.height
        )
      }
    
      /**
       * 简单的碰撞检测定义:
       * 另一个精灵的中心点处于本精灵所在的矩形内即可
       * @param{Sprite} sp: Sptite的实例
       */
      isCollideWith(sp) {
        let spX = sp.x + sp.width / 2
        let spY = sp.y + sp.height / 2
    
        if ( !this.visible || !sp.visible )
          return false
    
        return !!(   spX >= this.x
                  && spX <= this.x + this.width
                  && spY >= this.y
                  && spY <= this.y + this.height  )
      }
    }
    
    

    Spirite类

    constructor

    构造器

    • 根据输入图片路径,高度,宽度,初始坐标(x,y)生成一个精灵
      • 这里的x,y 是指图片的左上角坐标
      • 应注意的一点是此处所有参数均有缺省值
    • 初始visible设置为true,即可见

    drawToCanvas(ctx)

    将精灵画于画布上

    • 如果不可见,那么不画到画布上
    • 如果可见,根据该精灵的x,y坐标

    isCollideWith(sp)

    • 根据传入物体的左上角的坐标和大小计算中心坐标
    • 若两个物体中任意一个不可见,则无需计算,直接返回失败
    • 判断传入物体的中心坐标有没有在该物体的方框之内

    animation.js

    动画类所在文件

    代码

    import Sprite  from './sprite'
    import DataBus from '../databus'
    
    let databus = new DataBus()
    
    const __ = {
      timer: Symbol('timer'),
    }
    
    /**
     * 简易的帧动画类实现
     */
    export default class Animation extends Sprite {
      constructor(imgSrc, width, height) {
        super(imgSrc, width, height)
    
        // 当前动画是否播放中
        this.isPlaying = false
    
        // 动画是否需要循环播放
        this.loop = false
    
        // 每一帧的时间间隔
        this.interval = 1000 / 60
    
        // 帧定时器
        this[__.timer] = null
    
        // 当前播放的帧
        this.index = -1
    
        // 总帧数
        this.count = 0
    
        // 帧图片集合
        this.imgList = []
    
        /**
         * 推入到全局动画池里面
         * 便于全局绘图的时候遍历和绘制当前动画帧
         */
        databus.animations.push(this)
      }
    
      /**
       * 初始化帧动画的所有帧
       * 为了简单,只支持一个帧动画
       */
      initFrames(imgList) {
        imgList.forEach((imgSrc) => {
          let img = new Image()
          img.src = imgSrc
    
          this.imgList.push(img)
        })
    
        this.count = imgList.length
      }
    
      // 将播放中的帧绘制到canvas上
      aniRender(ctx) {
        ctx.drawImage(
          this.imgList[this.index],
          this.x,
          this.y,
          this.width  * 1.2,
          this.height * 1.2
        )
      }
    
      // 播放预定的帧动画
      playAnimation(index = 0, loop = false) {
        // 动画播放的时候精灵图不再展示,播放帧动画的具体帧
        this.visible   = false
    
        this.isPlaying = true
        this.loop      = loop
    
        this.index     = index
    
        if ( this.interval > 0 && this.count ) {
          this[__.timer] = setInterval(
            this.frameLoop.bind(this),
            this.interval
          )
        }
      }
    
      // 停止帧动画播放
      stop() {
        this.isPlaying = false
    
        if ( this[__.timer] )
          clearInterval(this[__.timer])
      }
    
      // 帧遍历
      frameLoop() {
        this.index++
    
        if ( this.index > this.count - 1 ) {
          if ( this.loop ) {
            this.index = 0
          }
    
          else {
            this.index--
            this.stop()
          }
        }
      }
    }
    

    准备内容

    • 引入Spirit类和DataBus类
    • 生成一个databus对象
    • 确定一个Symbol对象

    Animation类

    继承于Sprite类

    constructor

    构造器

    • 先用图片路径和宽度高度初始化超类(spirit类)
    • 一些基本的动画设置参数,如备注所述作用

    initFrames(imgList)

    初始化帧动画的所有帧

    • 对传入参数imgList进行遍历
      • forEach是对js中对数组遍历的一种方式
      • =>是匿名函数的语法

    aniRender(ctx)

    把播放中的帧画到画布上

    • 通过调用drawImage画上动画在该时刻应该有的图像
    • 该函数在main.js中的render中有调用

    playAnimation(index = 0, loop = false)

    • 将精灵图的可见设为false
      • 在本例子中有一个敌机被击毁,发生了敌机爆炸,展示爆炸的动画
    • 设置正在播放
    • 将是否循环的情况设置为初始设置的(初始设置为不循环)
    • 判断是否有动画切换间隔和帧数
      • 有的话设置定时器,使用函数setInterval
      • setInterval函数
        • 第一个参数是回调函数,是在这个过程中不断调用的函数
        • 第二个参数是间隔
        • 整个函数的含义就是在该间隔内不断调用传入的回调函数
        • (博主猜测一般情况来说主函数中的图像切换频率大于该间隔,这样才能体现动画的变化)

    stop()

    停止帧动画播放

    • 将播放设置为false
    • 清除原本设置的定时器

    frameLoop()

    帧遍历

    • 帧计数变量index加加
    • 若帧数大于图片数-1(由于计数从0开始)
      • 如果要求循环,将index置0
      • 否则将index--,即设置为最后一张图片,并且调用stop()函数暂停
  • 相关阅读:
    软件工程的国家标准下载链接
    电子计算机机房设计规范
    建筑物防雷设计规范
    信息系统项目管理师考试大纲
    计算机信息系统安全保护等级划分准则
    信息系统工程监理单位资质管理办法
    信息系统工程监理工程师资格管理办法
    计算机软件保护条例
    信息系统工程监理暂行规定
    第一个Winform 程序 (附一个需求实现,望大家帮忙)
  • 原文地址:https://www.cnblogs.com/Phoenix-blog/p/10951804.html
Copyright © 2020-2023  润新知