本文要分享的是如何使用Vue.js實(shí)現(xiàn)一個(gè)命令行貪吃蛇游戲(temir-snake-game).對(duì)于貪吃蛇游戲想必大家都不陌生了,使用Vue.js實(shí)現(xiàn)一個(gè)Web版的貪吃蛇游戲似乎沒什么難度,那如果是命令行版的呢?是不是你會(huì)對(duì)它的實(shí)現(xiàn)原理感興趣呢?讓我們開始吧!
Vue.js寫一個(gè)命令行貪吃蛇游戲
安裝
npm install temir-snake-game -g
開始游戲
在終端窗口運(yùn)行temir-sg
.
對(duì)于Windows系統(tǒng),推薦使用hyper終端進(jìn)行體驗(yàn).
將Vue渲染到命令行界面
使用Vue.js實(shí)現(xiàn)命令行貪吃蛇游戲,首先意味著我們要將Vue.js渲染到命令行界面,才能開始具體的游戲?qū)崿F(xiàn).我們經(jīng)常用Vue.js來編寫Web應(yīng)用,但是Vue的能力卻不僅僅局限于此,它的舞臺(tái)也不只有瀏覽器.Vue3擁有出色的跨平臺(tái)能力,我們可以通過createRenderer API創(chuàng)建一個(gè)自定義渲染器,通過創(chuàng)建宿主環(huán)境中對(duì)應(yīng)的Node和Element,并對(duì)元素進(jìn)行增刪改查操作.【推薦:vue.js視頻教程】
得益于Vue3出色的跨平臺(tái)能力,我實(shí)現(xiàn)了Temir,一個(gè)用Vue組件來編寫命令行界面應(yīng)用的工具.開發(fā)者只需要使用Vue就可以編寫命令行應(yīng)用,不需要任何額外的學(xué)習(xí)成本.順便值得一提的是,它還支持HMR~
前端(vue)入門到精通課程,老師在線輔導(dǎo):聯(lián)系老師
Apipost = Postman + Swagger + Mock + Jmeter 超好用的API調(diào)試工具:點(diǎn)擊使用
關(guān)于Temir就不在這里進(jìn)行詳細(xì)的介紹了,有興趣的童鞋可以上Github查看介紹或者看使用Vue.js編寫命令行界面這篇文章.
貪吃蛇游戲?qū)崿F(xiàn)
有了Temir,我們就具備了使用Vue.js編寫命令行游戲的條件,接下來我們來看看游戲的具體實(shí)現(xiàn):
實(shí)現(xiàn)拆解
首先我們對(duì)游戲?qū)崿F(xiàn)進(jìn)行一下簡(jiǎn)單的拆解,從元素+邏輯的維度來看,可以簡(jiǎn)單分為幾部分:
元素初始化
競(jìng)技臺(tái)
蛇的爬行與食物的生成都需要依賴坐標(biāo),最簡(jiǎn)單的坐標(biāo)其實(shí)只需要一個(gè)索引值.因此競(jìng)技臺(tái)的組成也很簡(jiǎn)單,就是由很多個(gè)小盒子(這里以⬛表示)組成,每一個(gè)盒子對(duì)應(yīng)一個(gè)坐標(biāo)(索引),我們要做的是一個(gè)28*28的競(jìng)技臺(tái),因此它的索引集合就是(0~783).
const basic = 28 const backgroundIcon = '⬛' const arena = ref<string[]>([]) function initArena() { arena.value = Array.from({ length: basic * basic }, () => backgroundIcon) }
蛇
前面我們提到了坐標(biāo)的概念,蛇身的組成就是一串有規(guī)律的坐標(biāo).
const snakeIcon = '?' // 坐標(biāo)(索引)30,29 長(zhǎng)度為2的蛇身 const snakeBody = ref([30, 29])
食物
食物的生成其實(shí)也就是隨機(jī)一個(gè)坐標(biāo)(索引),只不過要注意的是,我們需要避開蛇身本身的坐標(biāo).
const foodIcon = '?' // 食物坐標(biāo) const foodCoord = ref(77) // 生成食物 function generateFood() { const food = Math.floor(Math.random() * basic * basic) // 與蛇身沖突,重新生成 if (snakeBody.value.includes(food)) { generateFood() return } foodCoord.value = food }
初始化后的元素長(zhǎng)這樣 :
蛇的爬行
蛇的爬行邏輯有兩個(gè)基礎(chǔ)元素,方向 + 步數(shù).前面我們提到了競(jìng)技臺(tái)的組成是一個(gè)28*28的行列式結(jié)構(gòu),那么關(guān)于方向和步數(shù)的映射,就比較清晰了:
const map = { left: -1, right: 1, top: -28, bottom: 28 }
有了兩個(gè)基本元素,我們就可以得出我們每一次爬行的下一個(gè)坐標(biāo).我們只需要在每次爬行的時(shí)候往蛇頭添加對(duì)應(yīng)的坐標(biāo),并移除蛇尾所在的坐標(biāo)就可以達(dá)到蛇爬行的效果.
function move() { const h = snakeBody.value[0] // 計(jì)算下一次爬行坐標(biāo),并添加至蛇頭 head.value = h + direction.value snakeBody.value.unshift(head.value) // 吃到食物,重新生成 if (head.value === foodCoord.value) { generateFood() } // 只有在未吃到食物的時(shí)候,才需要移除蛇尾 else { snakeBody.value.pop() } }
越界邏輯
貪吃蛇的游戲結(jié)束規(guī)則判斷就是爬行時(shí)蛇頭越界(這里的界限指的是超出競(jìng)技臺(tái)的范圍)或者碰到蛇身.
function isOutOfRange(h: number) { // 1. 蛇頭碰到蛇身 return snakeBody.value.indexOf(h, 1) > 0 // 2. 蛇頭超出競(jìng)技臺(tái)上方 || h < 0 // 3. 蛇頭超出競(jìng)技臺(tái)下方 || h > basic * basic - 1 // 4. 蛇頭超出競(jìng)技臺(tái)右方 || (direction.value === 1 && h % basic === 0) // 5. 蛇頭超出競(jìng)技臺(tái)左方 || (direction.value === -1 && h % basic === basic - 1) }
方向控制
貪吃蛇游戲核心的操作邏輯在于操縱蛇的方向進(jìn)行食物的捕捉.所以我們需要做的就是捕捉用戶方向鍵的輸入進(jìn)行方向的切換.Temir提供了useInput函數(shù)監(jiān)聽用戶的輸入.
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繪制
關(guān)于UI的繪制與呈現(xiàn)Temir提供了一些Vue組件,我們只需要像構(gòu)建Flexbox布局那樣構(gòu)建終端UI:
<script 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>
到這里,貪吃蛇的實(shí)現(xiàn)就結(jié)束了,對(duì)具體實(shí)現(xiàn)感興趣的可以戳源碼查看.
演示