javascript貪吃蛇游戲設(shè)計(jì)與實(shí)現(xiàn)
本文為大家分享了javascript實(shí)現(xiàn)貪吃蛇游戲的具體代碼,供大家參考,具體內(nèi)容如下
效果圖
設(shè)計(jì)
貪吃蛇游戲是一款休閑益智類游戲。既簡(jiǎn)單又耐玩。該游戲通過(guò)控制蛇頭方向吃蛋,從而使得蛇變得越來(lái)越長(zhǎng)。
玩法:
點(diǎn)擊屏幕控制蛇的移動(dòng)方向,尋找吃的東西,每吃一口就能得到一定的積分,而且蛇的身子會(huì)越吃越長(zhǎng),身子越長(zhǎng)玩的難度就越大,不能咬到自己的身體,更不能咬自己的尾巴,等到了一定的分?jǐn)?shù),游戲勝利。
設(shè)計(jì):
首先需要?jiǎng)?chuàng)建一個(gè)棋盤,然后需要生成一條貪吃蛇,接著隨機(jī)生成食物。每當(dāng)蛇吃到食物的時(shí)候,隨機(jī)生成新的食物,蛇頭吃到自己的身體的時(shí)候游戲結(jié)束。
棋盤設(shè)計(jì):
元素 :行數(shù),列數(shù),基礎(chǔ)細(xì)胞(可表現(xiàn)為空,食物,蛇身體);
屬性 :創(chuàng)建棋盤,清空棋盤;
基礎(chǔ)細(xì)胞設(shè)計(jì):
屬性 :重設(shè)顏色,重設(shè)大小;
食物:
需求 : 需要在棋盤剩余空白位置隨機(jī)位置生成食物;
貪吃蛇:
元素 : 位置集合(數(shù)組),移動(dòng)速率,移動(dòng)方向
需求: 初始隨機(jī)生成只有一節(jié)的貪吃蛇,定時(shí)器函數(shù)(根據(jù)移動(dòng)方向求得下一個(gè)要移動(dòng)到的位置,需要注意的是到達(dá)邊界后進(jìn)行特殊處理。判斷下個(gè)位置是否為蛇本身,如果是蛇就吃到自己,游戲結(jié)束。接著將下個(gè)位置添加到蛇位置集合內(nèi),最后判斷下個(gè)位置 是否與食物相同,如果相同,則重現(xiàn)生成新的食物,否則移除蛇尾)。
方向控制:
本游戲使用點(diǎn)擊屏幕,控制蛇移動(dòng)方向。
實(shí)現(xiàn)
cell.js
/* * @Author: ls * @Date: 2020-09-01 18:23:09 * @LastEditTime: 2020-09-16 14:23:37 * @LastEditors: Please set LastEditors * @Description: 基礎(chǔ)細(xì)胞類 * @FilePath: \snake\assets\cell.js */ cc.Class({ extends: cc.Component, properties: {}, onLoad() {}, /** * @param {*} cellColor */ setCellColor(cellColor = new cc.color(255, 255, 255, 255)) { this.node.getChildByName('color').color = cellColor; }, /** * @param {*} cellSize */ setCellPos(cellSize = new cc.v2(20, 20)) { this.node.width = cellSize.x; this.node.height = cellSize.y; }, });
guideCtrl.js
/* * @Author: ls * @Date: 2020-09-03 18:09:18 * @LastEditTime: 2020-09-14 08:55:47 * @LastEditors: Please set LastEditors * @Description: 引導(dǎo)類 * @FilePath: \snake\assets\guideCtrl.js */ cc.Class({ extends: cc.Component, properties: { step: [cc.Node], startToggle: cc.Toggle, }, onLoad() { this.startGuide(); this.startToggle.isChecked = false; }, /** * 開始引導(dǎo) */ startGuide() { if (!this.step.length) { this.node.destroy(); return; } for (let index = 0, length = this.step.length; index < length; index++) { this.step[index].active = false; } this._step = 0; this.step[0].active = true; }, /** * 下一個(gè)引導(dǎo)頁(yè)面 */ nextGuide() { this._step++; if (this._step < this.step.length - 1) { this.step[this._step].active = true; this.step[this._step - 1].active = false; if (this._step === this.step.length - 2) { this.step[this._step + 1].active = true; } } else { this.node.active = false; } }, callback: function (toggle) { cc.sys.localStorage.setItem('isStart', toggle.isChecked); }, });
gameCtrl.js
/* * @Author: ls * @Date: 2020-09-01 15:44:33 * @LastEditTime: 2020-09-16 14:23:18 * @LastEditors: Please set LastEditors * @Description: 游戲?qū)а蓊? * @FilePath: \snake\assets\gameController.js */ var noneColor = new cc.color(120, 120, 120, 255); var foodColor = new cc.color(254, 168, 23, 255); var snakeColor = new cc.color(243, 60, 66, 255); cc.Class({ extends: cc.Component, properties: { // 棋盤 node_grid: cc.Node, // 分?jǐn)?shù) lab_score: cc.Label, // 最好分?jǐn)?shù) lab_best: cc.Label, // 開始 node_start: cc.Node, // 新人引導(dǎo) node_guide: cc.Node, // 結(jié)束 node_over: cc.Node, // 基礎(chǔ)類 cellPrefab: cc.Prefab, // 移動(dòng)速度 mSpeed: 5, // 列數(shù) colCount: 30, // 行數(shù) rowCount: 30, }, onLoad() { // 初始化方向 // 靜止、上、下、左、右 // (0,0)、(0,1)、(0,-1)、(-1,0)、(1,0) this._direction = { x: 0, y: 0 }; // 初始化細(xì)胞大小 this._cellSize = { x: 10, y: 10 }; this._map = []; this.initCellPool(); this.onCreateMap(); // 顯示開始游戲界面 this.showStartGame(); }, /** * 初始化細(xì)胞對(duì)象池 */ initCellPool() { this.cellPool = new cc.NodePool(); let initCount = this.rowCount * this.colCount; for (let i = 0; i < initCount; i++) { let cell = cc.instantiate(this.cellPrefab); // 創(chuàng)建節(jié)點(diǎn) this.cellPool.put(cell); // 通過(guò) put 接口放入對(duì)象池 } }, /** * 創(chuàng)建地圖 */ onCreateMap() { this._map = []; let node_bg = this.node_grid.getChildByName('background'); this._cellSize = { x: node_bg.width / this.rowCount, y: node_bg.height / this.colCount }; for (var y = 0; y < this.colCount; y++) { for (let x = 0; x < this.rowCount; x++) { var obj = {}; obj.x = x; obj.y = y; obj.node = this.createCell(node_bg, x, y); this._map.push(obj); } } }, /** * 從對(duì)象池請(qǐng)求對(duì)象 * @param {*} parentNode */ createCell: function (parentNode, x, y) { let cell = null; if (this.cellPool.size() > 0) { // 通過(guò) size 接口判斷對(duì)象池中是否有空閑的對(duì)象 cell = this.cellPool.get(); } else { // 如果沒有空閑對(duì)象,也就是對(duì)象池中備用對(duì)象不夠時(shí),我們就用 cc.instantiate 重新創(chuàng)建 cell = cc.instantiate(this.cellPrefab); } cell.getComponent('cell').setCellPos(this._cellSize); cell.x = this._cellSize.x * x; cell.y = this._cellSize.y * y; cell.parent = parentNode; return cell; }, /** * 還原地圖 */ clearMap() { for (let index = 0, length = this._map.length; index < length; index++) { this._map[index].node.getComponent('cell').setCellColor(noneColor); } }, /** * 顯示開始界面 */ showStartGame() { this.node_over.active = false; this.node_start.active = true; }, /** * 顯示結(jié)束界面 */ showOverGame() { this.node_start.active = false; this.node_over.active = true; }, /** * 游戲開始 */ startGame() { this.node_guide.active = false; this.node_over.active = false; this.node_start.active = false; this.lab_score.node.active = true; this.lab_best.node.active = true; this.node_grid.active = true; // 是否首次進(jìn)入界面 if (!cc.sys.localStorage.getItem('isStart')) { this.node_guide.active = true; } this._score = 0; // 更新最高分?jǐn)?shù) this.updateBest(); this._canControl = true; this._direction = { x: 1, y: 0 }; this._snakeGrid = []; this._foodGrid = {}; // 初始化觸摸事件 this.openTouchEvent(); this.clearMap(); this.onCreateSnake(); this.onCreateFood(); // 開啟移動(dòng) this.schedule(this.move, 1 / this.mSpeed); }, /** * 更新分?jǐn)?shù) */ updateBest() { this._best = cc.sys.localStorage.getItem('best'); if (this._best) { if (this._best < this._score) { this._best = this._score; cc.sys.localStorage.setItem('best', this._best); } } else { this._best = this._score; cc.sys.localStorage.setItem('best', this._best); } this.lab_best.string = this._best; }, /** * 游戲結(jié)束 */ gameOver() { // 是否能控制 蛇改變移動(dòng)方向 this._canControl = false; this.unschedule(this.move); this.closeTouchEvent(); this.clearMap(); this.showOverGame(); }, /** * 創(chuàng)建蛇 */ onCreateSnake() { let x = ~~(Math.random() * this.rowCount); let y = ~~(Math.random() * this.colCount); for (let index = 0, length = this._map.length; index < length; index++) { if (this._map[index].x === x && this._map[index].y === y) { this._map[index].node.getComponent('cell').setCellColor(snakeColor); this._snakeGrid.push(this._map[index]); } } }, /** * 創(chuàng)建食物 */ onCreateFood() { if (this._map.length !== this._snakeGrid.length) { let r = ~~(Math.random() * (this._map.length - this._snakeGrid.length)); let subGrid = []; for (let i = 0; i < this._map.length; i++) { subGrid.push(this._map[i]); } for (let m = 0; m < subGrid.length; m++) { for (let n = 0; n < this._snakeGrid.length; n++) { if (subGrid[m].x === this._snakeGrid[n].x && subGrid[m].y === this._snakeGrid[n].y) { subGrid.splice(m, 1); if (m > 0) { m--; } } } } for (let index = 0; index < subGrid.length; index++) { if (index === r) { this._foodGrid = subGrid[index]; this._foodGrid.node.getComponent('cell').setCellColor(foodColor); // 增加分?jǐn)?shù) this._score++; this.lab_score.string = this._score; } } } }, /** * 打開觸摸 */ openTouchEvent() { var self = this; this.node.on( cc.Node.EventType.TOUCH_START, function (touch) { if (self._canControl) { self._canControl = false; let touchPos = self.node.convertToNodeSpaceAR(touch.getLocation()); self._direction = self.getTouchDirection(touchPos); this.scheduleOnce(function () { self._canControl = true; }, 1 / this.mSpeed); } }, this ); }, /** * 關(guān)閉觸摸 */ closeTouchEvent() { this.node.off(cc.Node.EventType.TOUCH_START, this); }, /** * 獲取選擇的方向 * @param {* 觸摸位置} touchPos */ getTouchDirection(touchPos) { // 獲取向量長(zhǎng)度 function getABS(pos) { return Math.sqrt(pos.x * pos.x + pos.y * pos.y); } // 獲取橫向 方向 function getLandscape(touchPos) { if (touchPos.x > 0) { cc.log('更改為 向 右 移動(dòng)'); return { x: 1, y: 0 }; } else { cc.log('更改為 向 左 移動(dòng)'); return { x: -1, y: 0 }; } } // 獲取豎向 方向 function getPortrait(touchPos) { if (touchPos.y > 0) { cc.log('更改為 向 上 移動(dòng)'); return { x: 0, y: 1 }; } else { cc.log('更改為 向 下 移動(dòng)'); return { x: 0, y: -1 }; } } if (getABS(this._direction) === 1) { cc.log('蛇 正在移動(dòng)'); if (this._direction.y === 1) { cc.log('蛇 正在向 上 移動(dòng)'); return getLandscape(touchPos); } else if (this._direction.y === -1) { cc.log('蛇 正在向 下 移動(dòng)'); return getLandscape(touchPos); } else if (this._direction.x === -1) { cc.log('蛇 正在向 左 移動(dòng)'); return getPortrait(touchPos); } else if (this._direction.x === 1) { cc.log('蛇 正在向 右 移動(dòng)'); return getPortrait(touchPos); } } else { cc.log('蛇 未開始 或 停止了移動(dòng)。此時(shí)修改方向無(wú)效!'); } }, /** * 移動(dòng) */ move() { let nextGrid = {}; nextGrid.x = this._snakeGrid[this._snakeGrid.length - 1].x + this._direction.x; nextGrid.y = this._snakeGrid[this._snakeGrid.length - 1].y + this._direction.y; if (this._direction.x === 1) { // 向右 if (nextGrid.x > this.colCount - 1) { nextGrid.x = 0; } } else if (this._direction.x === -1) { // 向左 if (nextGrid.x < 0) { nextGrid.x = this.colCount - 1; } } else if (this._direction.y === 1) { // 向上 if (nextGrid.y > this.rowCount - 1) { nextGrid.y = 0; } } else if (this._direction.y === -1) { // 向下 if (nextGrid.y < 0) { nextGrid.y = this.rowCount - 1; } } for (let m = 0, l = this._map.length; m < l; m++) { if (this._map[m].x === nextGrid.x && this._map[m].y === nextGrid.y) { nextGrid = this._map[m]; } } for (let n = 0, length = this._snakeGrid.length; n < length; n++) { if (nextGrid.x === this._snakeGrid[n].x && nextGrid.y === this._snakeGrid[n].y) { this.gameOver(); // return false; } } nextGrid.node.getComponent('cell').setCellColor(snakeColor); this._snakeGrid.push(nextGrid); if (nextGrid.x === this._foodGrid.x && nextGrid.y === this._foodGrid.y) { this.onCreateFood(); } else { let startGrid = this._snakeGrid.shift(); startGrid.node.getComponent('cell').setCellColor(noneColor); } }, });
完整代碼:js貪吃蛇游戲
更多有趣的經(jīng)典小游戲?qū)崿F(xiàn)專題,分享給大家:
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
ES6的Fetch異步請(qǐng)求的實(shí)現(xiàn)方法
這篇文章主要介紹了ES6的Fetch異步請(qǐng)求的實(shí)現(xiàn)方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-12-12JavaScript自動(dòng)點(diǎn)擊鏈接 防止繞過(guò)瀏覽器訪問的方法
下面小編就為大家?guī)?lái)一篇JavaScript自動(dòng)點(diǎn)擊鏈接 防止繞過(guò)瀏覽器訪問的方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-01-01JavaScript 動(dòng)態(tài)生成方法的例子
動(dòng)態(tài)生成方法的例子,這些方法在新對(duì)象實(shí)例化的時(shí)候創(chuàng)建2009-07-07JavaScript 設(shè)計(jì)模式學(xué)習(xí) Factory
通過(guò)接口實(shí)現(xiàn)工廠,這是通過(guò)List方式顯示RSS 等實(shí)現(xiàn)代碼。2009-07-07CascadeView級(jí)聯(lián)組件實(shí)現(xiàn)思路詳解(分離思想和單鏈表)
本文介紹自己最近做省市級(jí)聯(lián)的類似的級(jí)聯(lián)功能的實(shí)現(xiàn)思路,為了盡可能地做到職責(zé)分離跟表現(xiàn)與行為分離,這個(gè)功能拆分成了2個(gè)組件并用到了單鏈表來(lái)實(shí)現(xiàn)關(guān)鍵的級(jí)聯(lián)邏輯,下一段有演示效果的gif圖2016-04-04FireFox JavaScript全局Event對(duì)象
在IE下 JavaScript 中可以在任何地方使用全局的window.event來(lái)取得本次JavaScript被觸發(fā)的Event,從而取得 KeyCode,EventSourceElement 等對(duì)象。2009-06-06js控制的回到頁(yè)面頂端goTop的代碼實(shí)現(xiàn)
在瀏覽網(wǎng)頁(yè)的時(shí)候應(yīng)該會(huì)經(jīng)常見到右下角有個(gè)【回到頂端】的懸浮東東,本文也要使用js實(shí)現(xiàn)一下,感興趣的朋友可以參考下哈,希望可以幫助到你2013-03-03修復(fù)ie8&chrome下window的resize事件多次執(zhí)行
window.onresize 事件 專用事件綁定器 v0.1,用于解決 lte ie8 & chrome 及其他可能會(huì)出現(xiàn)的 原生 window.resize 事件多次執(zhí)行的 BUG.2011-10-10