新聞中心
這篇文章主要介紹了jQuery怎么實(shí)現(xiàn)實(shí)現(xiàn)貪吃蛇小游戲,具有一定借鑒價(jià)值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
10年積累的成都做網(wǎng)站、網(wǎng)站設(shè)計(jì)經(jīng)驗(yàn),可以快速應(yīng)對(duì)客戶對(duì)網(wǎng)站的新想法和需求。提供各種問(wèn)題對(duì)應(yīng)的解決方案。讓選擇我們的客戶得到更好、更有力的網(wǎng)絡(luò)服務(wù)。我雖然不認(rèn)識(shí)你,你也不認(rèn)識(shí)我。但先建設(shè)網(wǎng)站后付款的網(wǎng)站建設(shè)流程,更有成武免費(fèi)網(wǎng)站建設(shè)讓你可以放心的選擇與我們合作。
設(shè)計(jì)思想
在開(kāi)始寫(xiě)代碼前首先讓我們來(lái)構(gòu)思一下整體游戲的實(shí)現(xiàn)過(guò)程:

需要的對(duì)象
首先既然是貪吃蛇,那么游戲中肯定要涉及到兩個(gè)對(duì)象,一個(gè)是蛇的對(duì)象,另一個(gè)是食物的對(duì)象。食物對(duì)象肯定要有一個(gè)屬性就是食物的坐標(biāo)點(diǎn),蛇對(duì)象有一個(gè)屬性是一個(gè)數(shù)組,用來(lái)存放蛇身體所有的坐標(biāo)點(diǎn)。
如何移動(dòng)
另外全局需要有一個(gè)定時(shí)器來(lái)周期性的移動(dòng)蛇的身體。由于蛇的身體彎彎曲曲有各種不同的形狀,因此我們只處理蛇的頭部和尾部,每次移動(dòng)都根據(jù)移動(dòng)的方向的不同來(lái)添加新的頭部,再把尾部擦去,看起來(lái)就像蛇在向前爬行一樣。
方向控制
由于蛇有移動(dòng)的方向,因此我們也需要在全局定義一個(gè)方向?qū)ο?,?duì)象中有上下左右所代表的值。同時(shí),在蛇對(duì)象的屬性中我們也需要定義一個(gè)方向?qū)傩?,用?lái)表示當(dāng)前蛇所移動(dòng)的方向。
碰撞檢測(cè)
在蛇向前爬行的過(guò)程中,會(huì)遇到三種不同的情況,需要進(jìn)行不同的判斷檢測(cè)。第一種情況是吃到了食物,這時(shí)候就需要向蛇的數(shù)組中添加食物的坐標(biāo)點(diǎn);第二種情況是碰到了自己的身體,第三種是碰到了邊界,這兩種情況都導(dǎo)致游戲結(jié)束;如果不是上面的三種情況,蛇就可以正常的移動(dòng)。
開(kāi)始編程
整體構(gòu)思有了,下面就開(kāi)始寫(xiě)代碼了。
搭建幕布
首先整個(gè)游戲需要一個(gè)搭建活動(dòng)的場(chǎng)景,我們通過(guò)一個(gè)表格布局來(lái)作為整個(gè)游戲的背景。
pannel就是我們的幕布,我們?cè)谶@個(gè)里面用td標(biāo)簽來(lái)畫(huà)上一個(gè)個(gè)的“像素點(diǎn)”。我們用兩種樣式來(lái)表現(xiàn)不同的對(duì)象,.body表示蛇的身體的樣式,.food表示食物的樣式。
var settings = {
// pannel面板的長(zhǎng)度
pannelSize: 10,
// 貪吃蛇移動(dòng)的速度
speed: 500,
// 貪吃蛇工作線程
workThread: null,
};
function setPannel(size){
var content = [];
content.push('我們定義了一個(gè)全局的settings用來(lái)存放全局性的變量,比如幕布的大小、蛇移動(dòng)的速度和工作的線程。然后通過(guò)一個(gè)函數(shù)把幕布畫(huà)了出來(lái),最后的效果就是這樣:

方向和定位
既然我們的“舞臺(tái)”已經(jīng)搭建完了,怎么來(lái)定義我們“演員”的位置和移動(dòng)的方向呢。首先定義一個(gè)全局的方向變量,對(duì)應(yīng)的數(shù)值就是我們的上下左右方向鍵所代表的keyCode。
var Direction = {
UP: 38,
DOWN: 40,
LEFT: 37,
RIGHT: 39,
};我們?cè)谏厦娈?huà)幕布的時(shí)候通過(guò)兩次遍歷畫(huà)出了一個(gè)類似于中學(xué)里學(xué)的坐標(biāo)系,有X軸和Y軸。如果每次都用{x:x,y:y}來(lái)表示會(huì)很(mei)麻(bi)煩(ge),我們可以定義一個(gè)坐標(biāo)點(diǎn)對(duì)象。
function Position(x,y){
// 距離X軸長(zhǎng)度,取值范圍0~pannelSize-1
this.X = x || 0;
// 距離Y軸長(zhǎng)度,取值范圍0~pannelSize-1
this.Y = y || 0;
}副咖–食物
既然定義好了坐標(biāo)點(diǎn)對(duì)象,那么可以先來(lái)看一下簡(jiǎn)單的對(duì)象,就是我們的食物(Food)對(duì)象,上面說(shuō)了,它有一個(gè)重要的屬性就是它的坐標(biāo)點(diǎn)。
function Food(){
this.pos = null;
// 隨機(jī)產(chǎn)生Food坐標(biāo)點(diǎn),避開(kāi)蛇身
this.Create = function(){
if(this.pos){
this.handleDot(false, this.pos, 'food');
}
let isOk = true;
while(isOk){
let x = parseInt(Math.random()*settings.pannelSize),
y = parseInt(Math.random()*settings.pannelSize);
if(!$('.td_'+x+'_'+y).hasClass('body')){
isOk = false;
let pos = new Position(x, y);
this.handleDot(true, pos, 'food');
this.pos = pos;
}
}
};
// 畫(huà)點(diǎn)
this.handleDot = function(flag, dot, className){
if(flag){
$('.td_'+dot.X+'_'+dot.Y).addClass(className);
} else {
$('.td_'+dot.X+'_'+dot.Y).removeClass(className);
}
};
}既然食物有了坐標(biāo)點(diǎn)這個(gè)屬性,那么我們什么時(shí)候給他賦值呢?我們知道Food是隨機(jī)產(chǎn)生的,因此我們定義了一個(gè)Create函數(shù)用來(lái)產(chǎn)生Food的坐標(biāo)點(diǎn)。但是產(chǎn)生的坐標(biāo)點(diǎn)又不能在蛇的身體上,所以通過(guò)一個(gè)while循環(huán)來(lái)產(chǎn)生坐標(biāo)點(diǎn),如果坐標(biāo)點(diǎn)正確了,就終止循環(huán)。此外為了方便我們統(tǒng)一處理坐標(biāo)點(diǎn)的樣式,因此定義了一個(gè)handleDot函數(shù)。
主咖–蛇
終于到了我們的主咖,蛇。首先定義一下蛇基本的屬性,最重要的肯定是蛇的body屬性,每次移動(dòng)時(shí),都需要對(duì)這個(gè)數(shù)組進(jìn)行一些操作。其次是蛇的方向,我們給它一個(gè)默認(rèn)向下的方向。然后是食物,在蛇的構(gòu)造函數(shù)中我們傳入食物對(duì)象,在后續(xù)移動(dòng)時(shí)需要判斷是否吃到食物。
function Snake(myFood){
// 蛇的身體
this.body = [];
// 蛇的方向
this.dir = Direction.DOWN;
// 蛇的食物
this.food = myFood;
// 創(chuàng)造蛇身
this.Create = function(){
let isOk = true;
while(isOk){
let x = parseInt(Math.random()*(settings.pannelSize-2))+1,
y = parseInt(Math.random()*(settings.pannelSize-2))+1;
console.log(x,y)
if(!$('.td_'+x+'_'+y).hasClass('food')){
isOk = false;
let pos = new Position(x, y);
this.handleDot(true, pos, 'body')
this.body.push(pos);
}
}
};
this.handleDot = function(flag, dot, className){
if(flag){
$('.td_'+dot.X+'_'+dot.Y).addClass(className);
} else {
$('.td_'+dot.X+'_'+dot.Y).removeClass(className);
}
};
}移動(dòng)函數(shù)處理
下面對(duì)蛇移動(dòng)的過(guò)程進(jìn)行處理,由于我們每次都采用添頭去尾的方式移動(dòng),因此我們每次只需要關(guān)注蛇的頭和尾。我們約定數(shù)組的第一個(gè)元素是頭,最后一個(gè)元素是尾。
this.Move = function(){
let oldHead = Object.assign(new Position(), this.body[0]),
oldTail = Object.assign(new Position(), this.body[this.body.length - 1]),
newHead = Object.assign(new Position(), oldHead);
switch(this.dir){
case Direction.UP:
newHead.X = newHead.X - 1;
break;
case Direction.DOWN:
newHead.X = newHead.X + 1;
break;
case Direction.LEFT:
newHead.Y = newHead.Y - 1;
break;
case Direction.RIGHT:
newHead.Y = newHead.Y + 1;
break;
default:
break;
}
// 數(shù)組添頭
this.body.unshift(newHead);
// 數(shù)組去尾
this.body.pop();
};檢測(cè)函數(shù)處理
這樣我們對(duì)蛇身數(shù)組就處理完了。但是我們還需要對(duì)新的頭(newHead)進(jìn)行一些碰撞檢測(cè),判斷新頭部的位置上是否有其他東西(碰撞檢測(cè))。
// 食物檢測(cè)
this.eatFood = function(){
let newHead = this.body[0];
if(newHead.X == this.food.pos.X&&newHead.Y == this.food.pos.Y){
return true;
} else {
return false;
}
};
// 邊界檢測(cè)
this.konckWall = function(){
let newHead = this.body[0];
if(newHead.X == -1 ||
newHead.Y == -1 ||
newHead.X == settings.pannelSize ||
newHead.Y == settings.pannelSize ){
return true;
} else {
return false;
}
};
// 蛇身檢測(cè)
this.konckBody = function(){
let newHead = this.body[0],
flag = false;
this.body.map(function(elem, index){
if(index == 0)
return;
if(elem.X == newHead.X && elem.Y == newHead.Y){
flag = true;
}
});
return flag;
};重新繪制
因此我們需要對(duì)Move函數(shù)進(jìn)行一些擴(kuò)充:
this.Move = function(){
// ...數(shù)組操作
if(this.eatFood()){
this.body.push(oldTail);
this.food.Create();
this.rePaint(true, newHead, oldTail);
} else if(this.konckWall() || this.konckBody()) {
this.Over();
} else {
this.rePaint(false, newHead, oldTail);
}
};
this.Over = function(){
clearInterval(settings.workThread);
console.log('Game Over');
};
this.rePaint = function(isEatFood, newHead, oldTail){
if(isEatFood){
// 加頭
this.handleDot(true, newHead, 'body');
} else {
// 加頭
this.handleDot(true, newHead, 'body');
// 去尾
this.handleDot(false, oldTail, 'body');
}
};因?yàn)樵贛ove函數(shù)處理數(shù)組的后我們的蛇身還沒(méi)有重新繪制,因此我們很巧妙地判斷如果是吃到食物的情況,在數(shù)組中就把原來(lái)的尾部添加上,這樣就達(dá)到了吃食物的效果。同時(shí)我們定義一個(gè)rePaint函數(shù)進(jìn)行頁(yè)面的重繪。

游戲控制器
我們的“幕布”、“演員”和“動(dòng)作指導(dǎo)”都已經(jīng)到位,那么,我們現(xiàn)在就需要一個(gè)“攝影機(jī)”進(jìn)行拍攝,讓它們都開(kāi)始“干活”。
function Control(){
this.snake = null;
// 按鈕的事件綁定
this.bindClick = function(){
var that = this;
$(document).on('keydown', function(e){
if(!that.snake)
return;
var canChangrDir = true;
switch(e.keyCode){
case Direction.DOWN:
if(that.snake.dir == Direction.UP){
canChangrDir = false;
}
break;
case Direction.UP:
if(that.snake.dir == Direction.DOWN){
canChangrDir = false;
}
break;
case Direction.LEFT:
if(that.snake.dir == Direction.RIGHT){
canChangrDir = false;
}
break;
case Direction.RIGHT:
if(that.snake.dir == Direction.LEFT){
canChangrDir = false;
}
break;
default:
canChangrDir = false;
break;
}
if(canChangrDir){
that.snake.dir = e.keyCode;
}
});
$('#palSize').on('change',function(){
settings.pannelSize = $(this).val();
setPannel(settings.pannelSize);
});
$('#palSpeed').on('change',function(){
settings.speed = $(this).val();
});
$('#startBtn').on('click',function(){
$('.food').removeClass('food');
$('.body').removeClass('body');
that.startGame();
});
};
// 初始化
this.init = function(){
this.bindClick();
setPannel(settings.pannelSize);
};
// 開(kāi)始游戲
this.startGame = function(){
var food = new Food();
food.Create();
var snake = new Snake(food);
snake.Create();
this.snake =snake;
settings.workThread = setInterval(function(){
snake.Move();
},settings.speed);
}
this.init();
}我們給document綁定一個(gè)keydown事件,當(dāng)觸發(fā)按鍵時(shí)改變蛇的移動(dòng)方向,但是如果和當(dāng)前蛇移動(dòng)方向相反時(shí)就直接return。最后的效果如下:

感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“jQuery怎么實(shí)現(xiàn)實(shí)現(xiàn)貪吃蛇小游戲”這篇文章對(duì)大家有幫助,同時(shí)也希望大家多多支持創(chuàng)新互聯(lián),關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,更多相關(guān)知識(shí)等著你來(lái)學(xué)習(xí)!
網(wǎng)頁(yè)標(biāo)題:jQuery怎么實(shí)現(xiàn)實(shí)現(xiàn)貪吃蛇小游戲
分享URL:http://www.dlmjj.cn/article/gdsdej.html


咨詢
建站咨詢
