日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
H5小游戲開發(fā)連載教程之掃雷游戲?qū)崿F(xiàn)

接上篇教程:??H5小游戲開發(fā)教程之頁面基礎(chǔ)布局的開發(fā)??

按需網(wǎng)站策劃可以根據(jù)自己的需求進行定制,成都網(wǎng)站制作、網(wǎng)站設(shè)計構(gòu)思過程中功能建設(shè)理應排到主要部位公司成都網(wǎng)站制作、網(wǎng)站設(shè)計的運用實際效果公司網(wǎng)站制作網(wǎng)站建立與制做的實際意義

很抱歉,讓大家久等了,從上周開始,工作很忙,一直沒時間寫,在這期間,我也在思考是否有更好或更簡單的實現(xiàn)方案,在不同的設(shè)備上都能有不錯的體驗;通過這篇教程,我為大家?guī)硪粋€非常簡單的掃雷游戲?qū)崿F(xiàn)方案;原本打算用兩篇文章的,由于過于簡單,就用一篇文章搞定了。

我們先欣賞下本篇文章實現(xiàn)的游戲界面:

掃雷游戲界面

掃雷游戲界面

我想,Windows的掃雷游戲大家應該都玩過吧?其實,這個游戲是有成功訣竅的,它考察了你思考問題的能力;如果1個格子的數(shù)值是1,那么它的周圍8個方向有且只有一個雷;同理,格子數(shù)值是2,它的周圍8個方向有且只有2個雷;由于1個格子最多有8個相鄰格子,所以1個格子周圍最多包含8個雷。

現(xiàn)在,我們正式開始。首先,我們在src根目錄創(chuàng)建一個文件:shared.js文件,這個文件用于定義所有游戲公用的變量及函數(shù);我們在該文件中定義一個genArr函數(shù);該函數(shù)非常簡單,用于創(chuàng)建一個指定長度的數(shù)組并用指定的值填充;在我們的游戲教程中,會大量使用該函數(shù)生成用于遍歷的數(shù)組。

export const genArr = (len, v) => Array(len).fill(v)

然后,在src/components/mine文件夾下創(chuàng)建一個文件game.js。我們首先用JS文檔注釋聲明2個類型,并引入一些我們將要用到的函數(shù);通過語義,大家應該能明白這2個類型中字段的含義吧?GameOptions類型中的rows是行數(shù),cols是列數(shù),mineCount是雷的數(shù)量;Block類型的num是格子的值,open是打開標識,flag是插旗標識,插旗是用于標記已確定了的雷,以防誤點擊。

/**
* @typedef {{rows: number, cols: number, mineCount: number}} GameOptions 游戲選項
*
* @typedef {{num: number, open: boolean, flag: boolean}} Block 方格子
*/

import { computed, reactive } from 'vue'
import { genArr } from '../../shared'

然后,我們導出一個匿名函數(shù)。大家切記:我們以下所有的JS代碼全部寫在該函數(shù)內(nèi)部。

export default () => {}

然后,我們創(chuàng)建一個用于保存游戲狀態(tài)的響應式對象。

const state = reactive({
rows: 9, // 行數(shù)
cols: 9, // 列數(shù)
mineCount: 10, // 雷數(shù)量
/** @type {Block[][]} 存放格子的二維數(shù)組 */
blocks: [],
isOver: false, // 游戲結(jié)束
isFirstClick: true // 是否首次點擊
})

然后,我們定義一個根據(jù)網(wǎng)格行列數(shù)生成二維數(shù)組陣列的函數(shù),初始格子的值num全部設(shè)為0,open和flag屬性都為false。

 /** @returns {Block[][]} */
const genBlocks = (rows, cols) => {
return genArr(rows).map(() => genArr(cols).map(() => ({ num: 0, open: false, flag: false })))
}

然后,我們定義一個獲取格子對象的函數(shù),由于我們很多地方需要獲取格子對象,所以定義一個函數(shù)比較好。

const getBlock = (row, col) => (state.blocks[row] || [])[col]

掃雷游戲有一個原則就是,首次單擊的格子不能是地雷,所以,我們必須在玩家首次點開一個格子后,再生成地雷分布圖;我們生成地雷分布圖的函數(shù)需要一個行列坐標,來確保該坐標一定不是地雷。如下是生成地雷分布圖函數(shù):

const genMap = (row, col) => {
const { blocks } = state
genArr(state.rows)
.reduce((t, _, i) => [...t, ...genArr(state.cols).map((_, j) => [i, j])], []) // 行列坐標構(gòu)成的一維數(shù)組
.filter(_ => !(_[0] === row && _[1] === col)) // 過濾掉玩家首次單擊的坐標
.sort(() => Math.random() - .5) // 對坐標隨機排序
.slice(0, state.mineCount) // 根據(jù)雷的數(shù)量對數(shù)組切片
.forEach(_ => {
blocks[_[0]][_[1]].num = 9 // 遍歷坐標數(shù)組,將對應坐標的格子對象的值設(shè)置為9,9代表雷
})
// 如下遍歷用于更新每個非雷的格子周圍雷的數(shù)量,num的值就是雷的數(shù)量
blocks.forEach((a, i) => {
a.forEach((b, j) => {
if (b.num < 9) {
b.num = [
getBlock(i - 1, j - 1),
getBlock(i - 1, j),
getBlock(i - 1, j + 1),
getBlock(i, j + 1),
getBlock(i + 1, j + 1),
getBlock(i + 1, j),
getBlock(i + 1, j -1),
getBlock(i, j - 1)
].filter(_ => _ && _.num > 8).length
}
})
})
}

當玩家點開一個格子后,如果該格子的值是0,那么我們需要深度遞歸遍歷,將相鄰的值為0和1的格子全部自動打開;如下是自動打開格子的函數(shù)定義:

const openBlocks = (row, col) => {
;[
[row - 1, col],
[row, col + 1],
[row + 1, col],
[row, col - 1]
].forEach(coords => {
const block = getBlock(...coords) // es6參數(shù)展開
if (block && !block.open && !block.flag) { // 如果格子存在并且沒被打開且沒被插旗
if (block.num < 2) { // 如果格子值為0和1,將格子打開
block.open = true
}
if (block.num < 1) { // 如果值為0,進行深度遞歸遍歷
openBlocks(...coords)
}
}
})
}

當玩家點擊的格子值為9時,我們需要打開所有的地雷,并結(jié)束游戲;如下是自動打開所有地雷的函數(shù):

const openMineBlocks = () => {
state.blocks.forEach(a => {
a.forEach(b => {
if (b.num > 8) {
b.open = true
}
})
})
}

當玩家打開了所有不是雷的格子后,我們需要結(jié)束游戲,如下是用于判斷是否已完成挑戰(zhàn)的函數(shù):

const isFinish = () => {
return state.blocks.every(a => a.filter(b => b.num < 9).every(b => b.open))
}

我們需要一個函數(shù),用于開始新游戲,該函數(shù)用于對游戲狀態(tài)進行重置或更新,并啟動游戲;如下是開始游戲函數(shù)定義:

/** @param {GameOptions} options */
const start = (options = {}) => {
Object.keys(options).forEach(key => {
if (options[key]) {
state[key] = options[key]
}
})
state.isOver = false
state.isFirstClick = true
state.blocks = genBlocks(state.rows, state.cols)
}

我們需要一個用于處理格子單擊事件的函數(shù)。

const onBlockClick = (row, col) => {
const block = getBlock(row, col)
if (state.isOver || block.flag || block.open) return // 如果游戲結(jié)束或格子插了旗或格子已打開,直接返回
block.open = true
if (state.isFirstClick) { // 如果是首次單擊格子,那么生成地雷分布圖
state.isFirstClick = false
genMap(row, col)
} else if (block.num > 8) { // 如果該格子是地雷,結(jié)束游戲并自動炸開所有的地雷
state.isOver = true
openMineBlocks()
return setTimeout(() => confirm('挑戰(zhàn)失??!是否重新開始?') && start(), 100)
} else if (block.num < 1) {
openBlocks(row, col) // 使用深度遞歸遍歷,打開值為0和1的相鄰格子
}
// 如果挑戰(zhàn)成功,那么給玩家2個選擇:挑戰(zhàn)更高難度或重新挑戰(zhàn)該難度
isFinish() && setTimeout(() => confirm('挑戰(zhàn)成功!是否挑戰(zhàn)更高難度?')
? start({ rows: state.rows + 1, mineCount: state.mineCount + state.cols })
: start(), 100)
}

我們需要一個用于處理格子上下文菜單的函數(shù),該函數(shù)用于插旗和移除旗之間切換。

/** @param {PointerEvent} evt */
const onBlockContextmenu = (row, col, evt) => {
evt.preventDefault()
if (state.isOver) return
const block = getBlock(row, col)
if (!block.open) {
block.flag = !block.flag
}
}

如下是3個計算屬性定義,分別用于統(tǒng)計插旗數(shù)量,打開的格子數(shù)量,未打開的格子數(shù)量。

const flagCount = computed(() => {
return state.blocks.reduce((t, a) => t + a.filter(_ => _.flag).length, 0)
})
const openCount = computed(() => {
return state.blocks.reduce((t, a) => t + a.filter(_ => _.open).length, 0)
})
const unopenCount = computed(() => state.rows * state.cols - openCount.value)

最后,我們在導出的匿名函數(shù)的底部返回組件中使用到的變量和函數(shù)。

return { state, flagCount, openCount, unopenCount, start, onBlockClick, onBlockContextmenu }

如下是src/components/mine/Index.vue文件源碼,我們使用table承載游戲界面;我認為table很適合二維數(shù)組數(shù)據(jù)的可視化。



如下是本篇教程中用到的幾張圖片,由于是在網(wǎng)上找的,擔心有版權(quán)問題,僅提供截圖,就不放到文章里面了,大家可以用自己的圖片替代,將圖片放到src/components/mine/img文件夾中;其實這個不是重點,重點是實現(xiàn)原理。

感謝閱讀!以上就是本篇教程的全部內(nèi)容,童鞋們都理解了嗎?我感覺掃雷游戲的實現(xiàn)非常簡單,幾乎沒什么難度,童鞋們應該都能理解吧?


新聞標題:H5小游戲開發(fā)連載教程之掃雷游戲?qū)崿F(xiàn)
新聞來源:http://www.5511xx.com/article/dhcgchd.html