前言
大家好, 我是 webfansplz. 本文要分享的是如何应用 Vue.js 实现一个命令行贪吃蛇游戏(temir-snake-game). 对于贪吃蛇游戏想必大家都不生疏了, 应用 Vue.js 实现一个 Web 版的贪吃蛇游戏仿佛没什么难度, 那如果是命令行版的呢? 是不是你会对它的实现原理感兴趣呢? 让咱们开始吧!
装置
npm install temir-snake-game -g
开始游戏
在终端窗口运行temir-sg
.
对于 Windows 零碎, 举荐应用 hyper 终端进行体验.
将 Vue 渲染到命令行界面
应用 Vue.js 实现命令行贪吃蛇游戏, 首先意味着咱们要将 Vue.js 渲染到命令行界面, 能力开始具体的游戏实现. 咱们常常用 Vue.js 来编写 Web 利用, 然而 Vue 的能力却不仅仅局限于此, 它的舞台也不只有浏览器.Vue3 领有杰出的跨平台能力, 咱们能够通过 createRenderer API 创立一个自定义渲染器, 通过创立宿主环境中对应的 Node 和 Element, 并对元素进行增删改查操作.
得益于 Vue3 杰出的跨平台能力, 我实现了 Temir, 一个用 Vue 组件来编写命令行界面利用的工具. 开发者只须要应用 Vue 就能够编写命令行利用, 不须要任何额定的学习老本. 顺便值得一提的是, 它还反对 HMR~
对于 Temir 就不在这里进行具体的介绍了, 有趣味的童鞋能够上 Github 查看介绍或者看应用 Vue.js 编写命令行界面这篇文章.
贪吃蛇游戏实现
有了 Temir, 咱们就具备了应用 Vue.js 编写命令行游戏的条件, 接下来咱们来看看游戏的具体实现:
实现拆解
首先咱们对游戏实现进行一下简略的拆解, 从元素 + 逻辑的维度来看, 能够简略分为几局部:
元素初始化
竞技台
蛇的匍匐与食物的生成都须要依赖坐标, 最简略的坐标其实只须要一个索引值. 因而竞技台的组成也很简略, 就是由很多个小盒子 (这里以⬛示意) 组成, 每一个盒子对应一个坐标(索引), 咱们要做的是一个 28*28 的竞技台, 因而它的索引汇合就是(0~783).
const basic = 28
const backgroundIcon = '⬛'
const arena = ref<string[]>([])
function initArena() {arena.value = Array.from({ length: basic * basic}, () => backgroundIcon)
}
蛇
后面咱们提到了坐标的概念, 蛇身的组成就是一串有法则的坐标.
const snakeIcon = '🟧'
// 坐标(索引)30,29 长度为 2 的蛇身
const snakeBody = ref([30, 29])
食物
食物的生成其实也就是随机一个坐标(索引), 只不过要留神的是, 咱们须要避开蛇身自身的坐标.
const foodIcon = '🍗'
// 食物坐标
const foodCoord = ref(77)
// 生成食物
function generateFood() {const food = Math.floor(Math.random() * basic * basic)
// 与蛇身抵触, 从新生成
if (snakeBody.value.includes(food)) {generateFood()
return
}
foodCoord.value = food
}
初始化后的元素长这样 👇 :
蛇的匍匐
蛇的匍匐逻辑有两个根底元素, 方向 + 步数. 后面咱们提到了竞技台的组成是一个 28*28 的行列式构造, 那么对于方向和步数的映射, 就比拟清晰了:
const map = {
left: -1,
right: 1,
top: -28,
bottom: 28
}
有了两个根本元素, 咱们就能够得出咱们每一次匍匐的下一个坐标. 咱们只须要在每次匍匐的时候往蛇头增加对应的坐标, 并移除蛇尾所在的坐标就能够达到蛇匍匐的成果.
function move() {const h = snakeBody.value[0]
// 计算下一次匍匐坐标, 并增加至蛇头
head.value = h + direction.value
snakeBody.value.unshift(head.value)
// 吃到食物, 从新生成
if (head.value === foodCoord.value) {generateFood()
}
// 只有在未吃到食物的时候, 才须要移除蛇尾
else {snakeBody.value.pop() }
}
越界逻辑
贪吃蛇的游戏完结规定判断就是匍匐时蛇头越界 (这里的界线指的是超出竞技台的范畴) 或者碰到蛇身.
function isOutOfRange(h: number) {
// 1. 蛇头碰到蛇身
return snakeBody.value.indexOf(h, 1) > 0
// 2. 蛇头超出竞技台上方
|| h < 0
// 3. 蛇头超出竞技台下方
|| h > basic * basic - 1
// 4. 蛇头超出竞技台右方
|| (direction.value === 1 && h % basic === 0)
// 5. 蛇头超出竞技台左方
|| (direction.value === -1 && h % basic === basic - 1)
}
方向管制
贪吃蛇游戏外围的操作逻辑在于操纵蛇的方向进行食物的捕获. 所以咱们须要做的就是捕获用户方向键的输出进行方向的切换.Temir 提供了 useInput 函数监听用户的输出.
import {useInput} from '@temir/core'
useInput(onKeyBoard, { isActive: true})
function onKeyBoard(_, keys) {const { upArrow, downArrow, leftArrow, rightArrow} = keys
const d = {[+leftArrow]: -1,
[+rightArrow]: 1,
[+upArrow]: -basic,
[+downArrow]: basic,
}[1] ?? direction.value
direction.value = (snakeBody.value[1] - snakeBody.value[0] === d) ? direction.value : d
}
UI 绘制
对于 UI 的绘制与出现 Temir 提供了一些 Vue 组件, 咱们只须要像构建 Flexbox 布局那样构建终端 UI:
<script lang="ts" setup>
import {computed} from 'vue'
import {TBox, TText} from '@temir/core'
import {useGame} from './composables'
import Header from './components/Header.vue'
import Home from './components/Home.vue'
import Game from './components/Game.vue'
import GameOver from './components/GameOver.vue'
import Exit from './components/Exit.vue'
const {playStatus} = useGame()
const activeComponent = computed(() => {
return {
unplayed: Home,
playing: Game,
over: GameOver,
exit: Exit,
}[playStatus.value]
})
</script>
<template>
<TBox
:width="100"
justify-content="center"
align-items="center"
flex-direction="column"
border-style="double"
>
<Header />
<component :is="activeComponent" />
</TBox>
</template>
到这里, 贪吃蛇的实现就完结了, 对具体实现感兴趣的能够戳源码查看.
演示
结语
如果我的文章和我的项目对你有所启发和帮忙, 请给一个 star 反对作者 ✌!