關於JS實作貪吃蛇的文章很多,本文我們主要用jQury來實現貪吃蛇小遊戲,以下實現這篇文章主要介紹了利用jQuery實現貪吃蛇小遊戲的方法,文中給出的是吸納思路和範例程式碼,需要的朋友可以參考下。
前言
相信貪吃蛇的遊戲大家都玩過。在那個水果機還沒有流行,人手一部諾基亞的時代,貪吃蛇是手機裡必備的遊戲。筆者閒的無聊的時候就拿出手機來玩上幾局,挑戰一下自己的紀錄。
#後來上大學了,用c語言做過貪吃蛇的遊戲,但主要是透過函數來控制(PS:現在讓我看程式碼都看不懂(⊙﹏⊙)) 。現在學習前端框架之後,透過jQuery來實現一個貪吃蛇的遊戲效果,雖然遊戲介面比(bu)較(ren)簡(zhi)陋(shi),但是主要學習一下游戲中面向對象和由局部到整體的思想。
設計想法
在開始寫程式碼前先讓我們來構思整體遊戲的實作過程:
需要的對象
首先既然是貪吃蛇,那麼遊戲中一定要牽涉到兩個對象,一個是蛇的對象,另一個是食物的對象。食物對像一定要有一個屬性就是食物的座標點,蛇物件有一個屬性是數組,用來存放蛇身體所有的座標點。
如何移動
另外全域需要有一個計時器來週期性的移動蛇的身體。由於蛇的身體彎曲有各種不同的形狀,因此我們只處理蛇的頭部和尾部,每次移動都根據移動的方向的不同來添加新的頭部,再把尾部擦去,看起來就像蛇在向前爬行一樣。
方向控制
由於蛇有移動的方向,因此我們也需要在全域定義一個方向對象,對像中有上下左右所代表的值。同時,在蛇物件的屬性中我們也需要定義一個方向屬性,用來表示目前蛇所移動的方向。
碰撞偵測
在蛇向前爬行的過程中,會遇到三種不同的情況,需要進行不同的判斷偵測。第一種情況是吃到了食物,這時候就需要在蛇的陣列中加入食物的座標點;第二種情況是碰到了自己的身體,第三種是碰到了邊界,這兩種情況都導致遊戲結束;如果不是上面的三種情況,蛇就可以正常的移動。
開始程式設計
整體構思有了,下面就開始寫程式碼了。
建立布幕
首先整個遊戲需要一個建立活動的場景,我們透過一個表格佈局來作為整個遊戲的背景。
<style type="text/css"> #pannel table{ border-collapse:collapse; } #pannel td{ width: 10px; height: 10px; border: 1px solid #000; } #pannel td.food{ background: green; } #pannel td.body{ background: #f60; } </style> <p id="pannel"> </p> <select name="" id="palSize"> <option value="10">10*10</option> <option value="20">20*20</option> <option value="40">30*30</option> </select> <select name="" id="palSpeed"> <option value="500">速度-慢</option> <option value="250">速度-正常</option> <option value="100">速度-快</option> </select> <button id="startBtn">开始</button>
pannel就是我們的布幕,我們在這個裡面用td標籤來畫上一個個的「像素點」。我們用兩種樣式來表現不同的對象,.body表示蛇的身體的樣式,.food表示食物的樣式。
var settings = { // pannel面板的长度 pannelSize: 10, // 贪吃蛇移动的速度 speed: 500, // 贪吃蛇工作线程 workThread: null, }; function setPannel(size){ var content = []; content.push('<table>'); for(let i=0;i<size;i++){ content.push('<tr>'); for(let j=0;j<size;j++){ content.push('<td class="td_'+i+'_'+j+'"></td>'); } content.push('</tr>'); } content.push('</table>'); $('#pannel').html(content.join('')); } setPannel(settings.pannelSize);
我們定義了一個全域的settings用來存放全域性的變量,例如幕布的大小、蛇移動的速度和工作的執行緒。然後透過一個函數把幕布畫了出來,最後的效果就是這樣:
#方向和定位
既然我們的「舞台」已經搭建完了,怎麼來定義我們「演員」的位置和移動的方向。首先定義一個全域的方向變量,對應的數值就是我們的上下左右方向鍵所代表的keyCode。
var Direction = { UP: 38, DOWN: 40, LEFT: 37, RIGHT: 39, };
我們在上面畫幕布的時候透過兩次遍歷畫出了一個類似中學裡學的座標系,有X軸和Y軸。如果每次都用{x:x,y:y}來表示會很(mei)麻(bi)煩(ge),我們可以定義一個座標點物件。
function Position(x,y){ // 距离X轴长度,取值范围0~pannelSize-1 this.X = x || 0; // 距离Y轴长度,取值范围0~pannelSize-1 this.Y = y || 0; }
副咖–食物
既然定義好了座標點對象,那麼可以先來看一下簡單的對象,就是我們的食物(Food)對象,上面說了,它有一個重要的屬性就是它的座標點。
function Food(){ this.pos = null; // 随机产生Food坐标点,避开蛇身 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; } } }; // 画点 this.handleDot = function(flag, dot, className){ if(flag){ $('.td_'+dot.X+'_'+dot.Y).addClass(className); } else { $('.td_'+dot.X+'_'+dot.Y).removeClass(className); } }; }
既然食物有了座標點這個屬性,那我們什麼時候要給他賦值呢?我們知道Food是隨機產生的,因此我們定義了一個Create函數用來產生Food的座標點。但是產生的座標點又不能在蛇的身體上,所以透過一個while循環來產生座標點,如果座標點正確了,就終止循環。另外為了方便我們統一處理座標點的樣式,因此定義了一個handleDot函數。
主咖–蛇
終於到了我們的主咖,蛇。先定義一下蛇基本的屬性,最重要的肯定是蛇的body屬性,每次移動時,都需要對這個陣列做一些操作。其次是蛇的方向,我們給它一個預設向下的方向。然後是食物,在蛇的構造函數中我們傳入食物對象,在後續移動時需要判斷是否吃到食物。
function Snake(myFood){ // 蛇的身体 this.body = []; // 蛇的方向 this.dir = Direction.DOWN; // 蛇的食物 this.food = myFood; // 创造蛇身 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); } }; }
移動函數處理
下面对蛇移动的过程进行处理,由于我们每次都采用添头去尾的方式移动,因此我们每次只需要关注蛇的头和尾。我们约定数组的第一个元素是头,最后一个元素是尾。
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; } // 数组添头 this.body.unshift(newHead); // 数组去尾 this.body.pop(); };
检测函数处理
这样我们对蛇身数组就处理完了。但是我们还需要对新的头(newHead)进行一些碰撞检测,判断新头部的位置上是否有其他东西(碰撞检测)。
// 食物检测 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; } }; // 边界检测 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; } }; // 蛇身检测 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; };
重新绘制
因此我们需要对Move函数进行一些扩充:
this.Move = function(){ // ...数组操作 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'); } };
因为在Move函数处理数组的后我们的蛇身还没有重新绘制,因此我们很巧妙地判断如果是吃到食物的情况,在数组中就把原来的尾部添加上,这样就达到了吃食物的效果。同时我们定义一个rePaint函数进行页面的重绘。
游戏控制器
我们的“幕布”、“演员”和“动作指导”都已经到位,那么,我们现在就需要一个“摄影机”进行拍摄,让它们都开始“干活”。
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); }; // 开始游戏 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绑定一个keydown事件,当触发按键时改变蛇的移动方向,但是如果和当前蛇移动方向相反时就直接return。最后的效果如下:
可以戳这里查看实现效果
点击这里下载源码
总结
实现了贪吃蛇的一些基本功能,比如移动、吃点、控制速度等,页面也比较的简单,就一个table、select和button。后期可以添加一些其他的功能,比如有计分、关卡等,也可以添加多个点,有的点吃完直接GameOver等等。
相关推荐;
以上是jQuery實作貪吃蛇小遊戲方法實例的詳細內容。更多資訊請關注PHP中文網其他相關文章!