likes
comments
collection
share

「小游戏系列」之vue3+uniapp的飞机大战(构建篇)

作者站长头像
站长
· 阅读数 51

本着怎么简单怎么来的原则,我们采用了面向对象开发的机制,想用3个类来结束战斗。涉及初衷是有交互的对象都归为一个类,方便各自设计api,有小伙伴就会问了,为什么不把游戏引擎也归为一个类,这里就只有一个解释,就是偷懒了,引擎在这里只提供了一个开始和结束的输出方法。然后每个类的细节和定义的方法在下篇详细介绍,这里我们快速过一下思路,下面开始搭建项目。

「小游戏系列」之vue3+uniapp的飞机大战(构建篇)


创建项目uniapp官方教程,脚手架都会用吧,这里就不说这么入门的介绍了,看图完事:

「小游戏系列」之vue3+uniapp的飞机大战(构建篇)

然后进到项目装好依赖,该删的删,把目录建好,pages/index就是我们的游戏页面,model.ts就是装我们游戏模型类,static和assets放游戏的素材。如图:

「小游戏系列」之vue3+uniapp的飞机大战(构建篇)

码页面容器: 我们这就很简单,放一个canvas完事,但是相关配置请参考uniapp的文档,因为uniapp为了兼容小程序的canvasApi,所以在api层面会跟官方的api有所区别,这里是个小坑,上代码:

<template>
  <view class="content">
    <canvas
      :style="{ width: width + 'px', height: height + 'px' }"
      canvas-id="canvasWrapper"
      id="canvasWrapper"
      @touchstart="handleClick"
      @touchmove="move"
      @touchend="end"
    ></canvas>
  </view>
</template>

码引擎变量: 因为使用ts,在码变量的时候要先申明一个interface,然后在使用vue3的组合api setup去创建state,然后export出来。这样在template就可以使用有响应式的变量了。

<script lang="ts">
import { reactive, toRefs } from 'vue'
interface stateIt {
  width: number
  height: number
  ctx: any
  phase: number
  liveEnemy: enemyIf[]
  hero: heroIf | null
  hullets: hulletIf[]
  score: number
  bgY: number
}

export default {
  setup() {
    const state = reactive<stateIt>({
      width: window.innerWidth, // canvas宽度
      height: window.innerHeight, // canvas高度
      ctx: null, // canvas的上下文
      phase: 1, // 游戏状态
      liveEnemy: [], // 敌机数组
      hero: null, // 主角飞机对象
      hullets: [], // 子弹数组
      score: 0, // 游戏分数
      bgY: 0, // 背景版的y轴偏移量
    })
    return {
      ...toRefs(state)
    }
}

加载素材: 这里将使用到的图片通过canvasAPI画到画布上,代码如下:

 // 画背景
    function paintBg(ctx: any) {
      ctx.drawImage('/static/img/background.png', 0, state.bgY)
      ctx.draw(true)
      ctx.drawImage('/static/img/background.png', 0, state.bgY - 852)
      ctx.draw(true)
      state.bgY++ === 852 && (state.bgY = 0)
    }
    // 画logo
    function paintLogo(ctx: any) {
      ctx.drawImage('/static/img/start.png', 0, 0)
    }
    // 画敌机
    function drawEnemy() {
      for (var i = 0; i < state.liveEnemy.length; i++) {
        if (state.liveEnemy[i].removable) {
          state.liveEnemy.splice(i, 1)
        }
      }
      for (var i = 0; i < state.liveEnemy.length; i++) {
        state.liveEnemy[i].draw(
          state.ctx,
          state.height,
          state.width,
          state.hullets,
          state.score,
          changeScore
        )
      }
    }
    // 画分数
    function drawScore() {
      state.ctx.fillText('分数:' + state.score, 10, 30)
      state.ctx.draw(true)
    }

然后在vue3的setup中使用onMounted钩子函数去调用上述画图的方法,可以看到加载的效果图,如图:

「小游戏系列」之vue3+uniapp的飞机大战(构建篇)

游戏主引擎 引擎是游戏的灵魂,鉴于作者是游戏小白,只懂得编码最原始方式:轮询,所以主引擎是采用定时器是实现的,每50ms去轮询一下游戏状态,然后更新游戏的视图。码农不多说,直接上代码:

 // 结束操作
    function end() {
      if (state.phase == StateEnum.PLAY) {
        state.phase = StateEnum.PAUSE
      }
    }
    // 游戏暂停
    function drawPause() {
      uni.getImageInfo({
        src: '/static/img/game_pause_nor.png',
        success: (res) => {
          state.ctx.drawImage(
            '/static/img/game_pause_nor.png',
            (state.width - res.width) / 2,
            (state.height - res.height) / 2
          )
          state.ctx.draw(true)
        },
      })
    }
    //游戏结束
    function gameover() {
      alert('游戏结束,成绩' + state.score)
      state.score = 0
      state.phase = StateEnum.READY
      state.hero = null
      state.hero = new Hero(state.width, state.height)
    }
    // 游戏主引擎
    function gameEngine() {
      switch (state.phase) {
        case StateEnum.READY:
          paintBg(state.ctx)
          paintLogo(state.ctx)
          break

        case StateEnum.PLAY:
          paintBg(state.ctx)
          drawEnemy()
          Hullet.drawHullet(state.ctx, state.hullets)
          state.hero &&
            state.hero.draw(
              state.ctx,
              changePhase,
              state.hullets,
              state.score,
              state.liveEnemy
            )
          break
        case StateEnum.PAUSE:
          drawPause()
          break
        case StateEnum.OVER:
          gameover()
          break
      }
      drawScore()
    }

飞机动起来传送门-下篇


最后,如果你是一个游戏小白,又因为公司业务需要快速完成一款小游戏,本文提供的快速的开发思路不失一种好的方式,好了,本文就给大家介绍到这里,感觉有帮助的,留下个赞或评论再走吧!谢啦~ 💐

转载自:https://juejin.cn/post/7088122638630912014
评论
请登录