之前讀書學習C語言的時候,老是想着要完成貪食蛇這款遊戲,可是那時候個人編程基礎和編程思惟還不夠紮實和成熟,因此致使怎麼折騰都寫不出來,可是如今工做了,就想着要完成讀書時候的當心願,就開始動手寫了,寫着寫着,就還真是寫出來了,哈哈,看來我也是成長了很多。。。html
貪食蛇這款遊戲,相信你們必定都不陌生了,童年之做,用編程語言編寫出來的貪食蛇,無非就是要實現幾個功能:蛇的移動、是否吃到本身、吃到食物、撞牆,只要把須要實現的功能給列出來了,接下來只要一個個去實現就行了,思路清晰仍是很重要的,由於都是用原生JS來編寫的,因此對JS功底仍是有點要求的,我以爲有時間練手的能夠嘗試一下,好了,廢話很少說,進去正題。。。node
首先咱們須要建立一個大畫布,開始和暫停按鈕,畫布是蛇在裏面跑的,開始和暫停分別控制開始和中止,這裏須要用到HTML和CSS,但我選擇了用JS實現,畢竟練手嘛,固然是要往不一樣的方法去出發。git
而後動態建立蛇的節點(一個蛇頭兩個蛇點)和食物,好了,最基本的HTML和CSS已經用JS來實現了,接下來開始實現須要實現的功能了編程
好了,思路分析好了就開始動手了!!bash
Let't it go ..app
如上所述,首先要建立場景,畫布和開始、暫停按鈕dom
直接上代碼:編程語言
let d = document,
//定時器
timer = null,
//禁止連續按鍵
disabled = 0,
//main是畫布,open是開始,stop是暫停
node = {
main: d.createElement('div'),
open: d.createElement('button'),
stop: d.createElement('button'),
}
//賦予樣式
node.main.style.background = `rgb(104, 163, 210)`;
node.main.style.width = `${15 * (30+1)}px`;
node.main.style.height = `${15 * (20+1)}px`;
node.main.id = 'main';
node.main.position = `relative`;
node.open.textContent = `開始`;
node.stop.textContent = `暫停`;
//加入document.body
for( key in node) document.body.appendChild(node[key]);
複製代碼
這樣就建立好畫布和按鈕了,完成了基本的場景準備函數
這裏我原本打算用函數function Snake()
,而後經過構造函數去調用的,但想了想,可是用ES6的class
去定義吧!!學習
直接上代碼:
class Snake {
constructor() {
this.init();
}
//初始化
init() {
//添加蛇身彙總
this.snakeAll = document.createElement('div');
this.snakeAll.id = 'all';
this.snakeAll.style.position = `relative`;
this.snakeAll.style.top = `${0}px`;
document.getElementById('main').appendChild(this.snakeAll);
//蛇節點座標
this.Snode = [
{ x: 3 , y: 1, flag: null},
{ x: 2 , y: 1, flag: null},
{ x: 1 , y: 1, flag: null},
];
//默認方向
this.direction = 'right';
//蛇節點寬度
this.width = 15;
//蛇節點長度
this.height = 15;
this.createSnake();
}
}
複製代碼
new Snake()的時候調用構造函數,調用this.init()
,對蛇須要用到的參數進行初始化和建立蛇必要的節點
接下來看this.createSnake()
代碼
//建立蛇
createSnake() {
//首先清除蛇的節點
this.removeSnake();
//根據座標添加節點
this.Snode.forEach( item => {
if(item.x != null) {
let div = document.createElement('div');
div.style.background = `#734fb3`;
div.style.width = `${this.width}px`
div.style.height = `${this.width}px`
div.style.position = `absolute`;
div.style.left = `${item.x * this.width}px`
div.style.top = `${item.y * this.height}px`
div.style.borderRadius = `${50}%`
div.classList.add('snake');
item.flag = div;
this.snakeAll.appendChild(div);
}
})
}
複製代碼
this.createSnake()
也是class Snake {}
裏面的方法,只是我怕代碼太長大家看的不耐煩...
this.createSnake()
的做用就是根據蛇的默認參數來建立蛇的節點,建立以前須要清除以前存在的蛇的節點
看this.removeSnake()
代碼:
//刪除蛇
removeSnake() {
for( let key in this.Snode) {
this.Snode[key].flag && this.snakeAll.removeChild(this.Snode[key].flag)
}
}
複製代碼
好了,建立蛇的節點的邏輯大概就是這樣,這裏來總結一下:
經過定義Snake
的類來編寫方法,new Snake()
的時候會調用構造函數裏面的this.init()
來設置默認參數
而後調用this.createSnake()
來根據this.Snode[]
默認參數來建立蛇的節點
建立以前必定要記住調用this.removeSnake()
來清除以前建立的節點,這部分的邏輯大概就是如此。。。
建立食物的邏輯比較簡單吧,跟建立蛇的邏輯是差很少的
也是定義一個Food
的類,而後經過new Food()
去調用建立食物
好了,直接上代碼
class Food {
constructor() {
this.width = 15;
this.height = 15;
this.createFood();
}
//建立食物
createFood() {
this.food && this.food.remove();
this.food = document.createElement('div');
this.food.id = 'food';
this.x = Math.floor(Math.random() * 15);
this.y = Math.floor(Math.random() * 10);
this.food.style.position = `relative`;
this.food.style.top = `${this.width * this.y}px`;
this.food.style.left = `${this.height * this.x}px`;
this.food.style.width = `${this.width}px`
this.food.style.height = `${this.height}px`
this.food.style.background = `#3F51B5`
this.food.style.borderRadius = `${50}%`
document.getElementById('all').appendChild(this.food);
}
}
複製代碼
看了上面建立蛇的邏輯,再看這段建立食物的邏輯應該問題不大吧?哈哈,我相信你看懂了
接下來開始說蛇的移動了,這段是重點呢,由於蛇的移動包含了撞牆的判斷
、吃到食物的判斷
、吃到本身的判斷
等功能,是有點麻煩,可是我會分段來講,這樣會清晰不少,好了,直接開始...
蛇的移動的原理就是除了蛇頭
,蛇尾
的後一個點的座標等於前一個點的座標,蛇頭
的座標由this.direction
這個屬性來控制的,而後經過計時器不斷執行this.createSnake()
就能夠實現蛇的移動了
好了,直接上代碼
//運動
run(food,timer) {
this.food = food;
//移動蛇身體
for(let i=this.Snode.length-1; i>0; i--) {
this.Snode[i].x = this.Snode[i-1].x;
this.Snode[i].y = this.Snode[i-1].y;
}
//移動蛇頭
switch(this.direction){
case 'left': this.Snode[0].x -=1;break;
case 'right': this.Snode[0].x += 1; break;
case 'top': this.Snode[0].y -= 1; break;
case 'bottom': this.Snode[0].y += 1;break;
}
this.createSnake();
}
複製代碼
這樣子就能夠實現蛇的移動了,是否是很簡單呢
下面開始細分功能,如下的代碼都是在run()
方法裏面的,我把他給分出來這樣容易看清楚
判斷是否撞牆就是判斷蛇的X,Y座標是否超出畫布的最大或最小X、Y座標
上面的代碼說了
畫布X的座標範圍是0 - 30
畫布Y的座標範圍是0 - 20
//判斷是否撞牆,根據蛇頭的X和Y座標去判斷
if(this.Snode[0].x > 30 || this.Snode[0].x < 0 || this.Snode[0].y > 20 || this.Snode[0].y < 0) {
alert("哎喲,老兄,看路啊,把我給撞暈了");
this.snakeAll.remove();
clearInterval(timer);
this.init();
this.food.createFood();
}
複製代碼
撞牆後移動蛇的所有節點,清除定時,從新調用this.init()
方法建立,和調用this.food.createFood()
方法建立食物
是否是條例很清晰呢?
吃到食物的邏輯也是很簡單的
當蛇頭的座標X
和Y
都等於食物的X
和Y
就等因而吃到了食物...
//吃到食物
if(this.Snode[0].x == this.food.x && this.Snode[0].y == this.food.y) {
this.Snode.push({x: null,y: null,flag: null});
this.food.createFood();
}
複製代碼
this.Snode
控制蛇的節點,吃到食物後須要多增長一個節點,直接push()
進去就好
而後從新調用方法建立食物就好
本身吃到本身,這種狀況只會在蛇擁有四個節點
以上的狀況下才會發生
當蛇頭的座標等於本身身體的任意一個座標的時候,就能夠判斷爲吃到了本身
//判斷是否吃到本身,只有四個點以上纔有可能吃到本身
for(let i=4;i<this.Snode.length;i++) {
if(this.Snode[0].x == this.Snode[i].x && this.Snode[0].y == this.Snode[i].y) {
alert("哎喲,老兄,肚子餓也不能吃本身啊");
this.snakeAll.remove();
clearInterval(timer);
this.init();
this.food.createFood();
}
}
複製代碼
吃到本身後,也是須要初始化和從新建立食物
好了,看到這裏,基本上已經完成了,接下來只須要添加點DOM事件便可
添加鍵盤事件和開始、暫停定時事件
//添加鍵盤事件
document.body.onkeydown = e => {
let ev = e || window.event;
if(!disabled) {
disabled = 1;
switch(ev.keyCode){
case 39:{
if(snake.direction != 'left'){
snake.direction = 'right';
break;
}
}
case 38:{
if(snake.direction != 'bottom'){
snake.direction = 'top';
break;
}
}
case 37:{
if(snake.direction != 'right'){
snake.direction = 'left';
break;
}
}
case 40:{
if(snake.direction != 'top'){
snake.direction = 'bottom';
break;
}
}
};
}
setTimeout( () => {
disabled = 0;
},100);
}
//開始
node.open.addEventListener('click', ()=> {
clearInterval(timer);
timer = setInterval( () => {
snake.run(food,timer);
},100);
})
//暫停
node.stop.addEventListener('click', ()=> {
clearInterval(timer);
})
複製代碼
好了,代碼寫到這裏基本上就完成了,不知道大家有沒有看到最後(傷心臉)!!
電腦上重裝了,沒了git,懶得裝,直接貼完整代碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<script>
class Snake {
constructor() {
this.init();
}
//初始化
init() {
//添加蛇身彙總
this.snakeAll = document.createElement('div');
this.snakeAll.id = 'all';
this.snakeAll.style.position = `relative`;
this.snakeAll.style.top = `${0}px`;
document.getElementById('main').appendChild(this.snakeAll);
//蛇節點座標
this.Snode = [
{ x: 3 , y: 1, flag: null},
{ x: 2 , y: 1, flag: null},
{ x: 1 , y: 1, flag: null},
];
//默認方向
this.direction = 'right';
//蛇節點寬度
this.width = 15;
//蛇節點長度
this.height = 15;
this.createSnake();
}
//建立蛇
createSnake() {
//首先清除蛇的節點
this.removeSnake();
//根據座標添加節點
this.Snode.forEach( item => {
if(item.x != null) {
let div = document.createElement('div');
div.style.background = `#734fb3`;
div.style.width = `${this.width}px`
div.style.height = `${this.width}px`
div.style.position = `absolute`;
div.style.left = `${item.x * this.width}px`
div.style.top = `${item.y * this.height}px`
div.style.borderRadius = `${50}%`
div.classList.add('snake');
item.flag = div;
this.snakeAll.appendChild(div);
}
})
}
//刪除蛇
removeSnake() {
for( let key in this.Snode) {
this.Snode[key].flag && this.snakeAll.removeChild(this.Snode[key].flag)
}
}
//運動
run(food,timer) {
this.food = food;
//移動蛇身體
for(let i=this.Snode.length-1; i>0; i--) {
this.Snode[i].x = this.Snode[i-1].x;
this.Snode[i].y = this.Snode[i-1].y;
}
//移動蛇頭
switch(this.direction){
case 'left': this.Snode[0].x -=1;break;
case 'right': this.Snode[0].x += 1; break;
case 'top': this.Snode[0].y -= 1; break;
case 'bottom': this.Snode[0].y += 1;break;
}
//判斷是否撞牆,根據蛇頭的X和Y座標去判斷
if(this.Snode[0].x > 30 || this.Snode[0].x < 0 || this.Snode[0].y > 20 || this.Snode[0].y < 0) {
alert("哎喲,老兄,看路啊,把我給撞暈了");
this.snakeAll.remove();
clearInterval(timer);
this.init();
this.food.createFood();
}
//吃到食物
if(this.Snode[0].x == this.food.x && this.Snode[0].y == this.food.y) {
this.Snode.push({x: null,y: null,flag: null});
this.food.createFood();
}
//判斷是否吃到本身,只有四個點以上纔有可能吃到本身
for(let i=4;i<this.Snode.length;i++) {
if(this.Snode[0].x == this.Snode[i].x && this.Snode[0].y == this.Snode[i].y) {
alert("哎喲,老兄,肚子餓也不能吃本身啊");
this.snakeAll.remove();
clearInterval(timer);
this.init();
this.food.createFood();
}
}
this.createSnake();
}
}
class Food {
constructor() {
this.width = 15;
this.height = 15;
this.createFood();
}
//建立食物
createFood() {
this.food && this.food.remove();
this.food = document.createElement('div');
this.food.id = 'food';
this.x = Math.floor(Math.random() * 15);
this.y = Math.floor(Math.random() * 10);
this.food.style.position = `relative`;
this.food.style.top = `${this.width * this.y}px`;
this.food.style.left = `${this.height * this.x}px`;
this.food.style.width = `${this.width}px`
this.food.style.height = `${this.height}px`
this.food.style.background = `#3F51B5`
this.food.style.borderRadius = `${50}%`
document.getElementById('all').appendChild(this.food);
}
}
(()=>{
let d = document,
//定時器
timer = null,
//禁止連續按鍵
disabled = 0,
//main是畫布,open是開始,stop是暫停
node = {
main: d.createElement('div'),
open: d.createElement('button'),
stop: d.createElement('button'),
}
//賦予樣式
node.main.style.background = `rgb(104, 163, 210)`;
node.main.style.width = `${15 * (30+1)}px`;
node.main.style.height = `${15 * (20+1)}px`;
node.main.id = 'main';
node.main.position = `relative`;
node.open.textContent = `開始`;
node.stop.textContent = `暫停`;
//加入document.body
for( key in node) document.body.appendChild(node[key]);
let snake = new Snake();
let food = new Food();
//添加鍵盤事件
document.body.onkeydown = e => {
let ev = e || window.event;
if(!disabled) {
disabled = 1;
switch(ev.keyCode){
case 39:{
if(snake.direction != 'left'){
snake.direction = 'right';
break;
}
}
case 38:{
if(snake.direction != 'bottom'){
snake.direction = 'top';
break;
}
}
case 37:{
if(snake.direction != 'right'){
snake.direction = 'left';
break;
}
}
case 40:{
if(snake.direction != 'top'){
snake.direction = 'bottom';
break;
}
}
};
}
setTimeout( () => {
disabled = 0;
},100);
}
//開始
node.open.addEventListener('click', ()=> {
clearInterval(timer);
timer = setInterval( () => {
snake.run(food,timer);
},100);
})
//暫停
node.stop.addEventListener('click', ()=> {
clearInterval(timer);
})
})();
</script>
</body>
</html>
複製代碼
本人是菜鳥,若是以爲我那裏寫得很差,能夠及時指出來,我也很樂意接受高手的指點,哈哈!!
對了 對了 忘了貼效果圖
難看是難看了點,但重要的是邏輯思惟嘛,哈哈!!
我以爲寫下了這個貪食蛇,是對本身的編程實力和思惟是一種檢測,雖然沒有什麼實際性的意義(尷尬臉)
不知不覺我也已經工做一年多了,從畢業的跨行成功到如今生活的無奈,有時候我也不知道堅持是爲了什麼,我也彷佛是忘記了本身的初心了,沒錯,我就是那種很喪很喪的那種人,哈哈!!
最後送你們一句話:人生,無非就是起起落落落落落落,因此要好好活着,哈哈
,生活,加油!!