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è)大?。?/p>
食物:
需求 : 需要在棋盤剩余空白位置隨機(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-12
JavaScript自動(dòng)點(diǎn)擊鏈接 防止繞過(guò)瀏覽器訪問(wèn)的方法
下面小編就為大家?guī)?lái)一篇JavaScript自動(dòng)點(diǎn)擊鏈接 防止繞過(guò)瀏覽器訪問(wèn)的方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-01-01
JavaScript 動(dòng)態(tài)生成方法的例子
動(dòng)態(tài)生成方法的例子,這些方法在新對(duì)象實(shí)例化的時(shí)候創(chuàng)建2009-07-07
JavaScript 設(shè)計(jì)模式學(xué)習(xí) Factory
通過(guò)接口實(shí)現(xiàn)工廠,這是通過(guò)List方式顯示RSS 等實(shí)現(xiàn)代碼。2009-07-07
CascadeView級(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-04
FireFox JavaScript全局Event對(duì)象
在IE下 JavaScript 中可以在任何地方使用全局的window.event來(lái)取得本次JavaScript被觸發(fā)的Event,從而取得 KeyCode,EventSourceElement 等對(duì)象。2009-06-06
js控制的回到頁(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

