學寫前端原生貪吃蛇
阿新 • • 發佈:2018-11-19
看了人家視訊上寫的貪吃蛇,瞬間覺得自己low爆了。。。。就學他寫了三遍,以我的記性,覺得還是有必要記錄下過程,以便記憶。
先看下截圖:
首先html分析:
注:下列的id是用在js裡的,class是用在css裡的
<div class="startPage" id="startPage"> <!-- 這是開始頁的大頁面 --> <div class="startBtn" id="startBtn"></div> <!-- 載在大頁面上的開始按鈕鍵 --> </div> <div class="wrapper"> <!-- 開始後的大頁面 --> <div class="left-side"> <!-- 將大頁面分出一個左邊,用來承載暫停和計分的按鍵 --> <div class="header"> <!-- 在這個左邊的開頭寫個用來載計分的框 --> <div class="score"> <!-- 顯示“分數” --> 分數: <span id="score"></span> <!-- 用在之後js裡的動態計分操作 --> </div> </div> <img src="img/zanting.png" id="startP" alt=""> <!-- 顯示暫停按鈕,這個按鈕用圖片代替 --> </div> <div class="main"> <!-- 大頁面中的主頁面,和左邊的區分下 --> <div class="content" id="content"></div> <!-- 遊戲區域,在js裡控制 --> </div> </div> <div class="lose" id="lose"> <!-- 遊戲結束的大頁面 --> <div class="con"> <!-- 遊戲結束後的計分清單 --> <span class="loseScore" id="loseScore"> <!-- js動態計分 --> </span> <div class="close" id="close"></div> <!-- 遊戲結束計分上的關閉按鈕 --> </div> </div>
上述是貪吃蛇的全部html程式碼,將貪吃蛇遊戲的大致介面劃分出來。
接下來是css程式碼:
*{ /* css的書寫習慣,先將所有的margin和padding的預設值都去掉 */ margin: 0; padding: 0; } .startPage{ /* 開始遊戲的大頁面設定 */ width: 100%; height: 640px; /* 遊戲介面高度,這是我按自己電腦瀏覽器大小設定的 */ position: absolute; /* 給它個絕對定位 */ z-index: 999; /* 顯示在介面最前頭 */ } .startBtn{ /* 遊戲開始按鍵的設定 */ width: 200px; /* 設定按鍵圖片的寬和高 */ height: 50px; position: absolute; /* 從這到margin:auto,這幾行放在一起,表示居中在大頁面的正中間 */ top: 0; left: 0; bottom: 0; right: 0; margin:auto; background-image: url(img/begin.png); /* 把開始按鍵的圖片放入 */ background-size: 100% 100%; /* 將圖片平攤開 */ cursor: pointer; /* 滑鼠劃入時,箭頭變成小手 */ } .wrapper{ /* 遊戲開始後的大頁面 */ width: 100%; height: 640px; /* 同上含義 */ background-image: url('img/bj1.png'); /* 放入遊戲背景圖片 */ background-size: 100% 100%; /* 平攤 */ position: relative; /* 相對定位 */ top: 0px; left: 0px; } .left-side{ /* 在大頁面上劃分出的左邊部分 */ width: 14%; /* 佔大頁面的14%寬度 */ height: 640px; /* 高度 */ position: absolute; /* 絕對定位 */ top: 0; left: 0; } .left-side img{ /* 顯示暫停按鍵圖片的設定 */ width: 120px; /* 設定圖片的寬高 */ height: 40px; position: absolute; /* 絕對定位 */ left: 15%; /* 該圖片在左邊劃分框中,距離左邊距15% */ top: 100px; /* 圖片距離上邊距100px */ } .header{ /* 用來承載計分的框 */ width: 100%; height: 40px; margin-top: 30px; /* 外邊距30px */ position: absolute; /* 絕對定位 */ } .score{ /* 計分數字顯示 */ width: 100px; height: 40px; font-size: 18px; color: #fff; /* 字型白色 */ position: absolute; /* 基於上個absolute的中心定位 */ top: 0; left: 0; bottom: 0; right: 0; margin:auto; } .main{ /* 在大頁面中劃分出的另一個部分 */ position: absolute; /* 絕對定位 */ left: 15%; /* 距離大頁面左邊據15% */ width: 52%; /* 佔大頁面的寬52% */ height: 640px; /* 值含義同上 */ border: 1px solid black; /* 邊框,作為標識,可去除 */ } .content{ /* 遊戲邊框,用來表示蛇不能出去的部分 */ position: absolute; top: 0; left: 0; bottom: 0; right: 0; margin:auto; /* 從position到這表示基於父級定位的中心定位 */ width: 525px; /* 邊框的寬高 */ height: 525px; border: 13px solid rgb(2, 32, 7); /* 邊框框 */ } .lose{ /* 表示遊戲結束後的出現的一個大頁面 */ width: 100%; height: 640px; position: absolute; top: 0; left: 0; display: none; /* 先設定不顯示,之後在js中修改 */ } .con{ /* 表示遊戲結束後出現的結束圖片 */ width: 340px; /* 為圖片設定寬高 */ height: 160px; background-image:url('img/over.png'); /* 加入圖片 */ background-size: 100% 100%; /* 平攤圖片 */ position: absolute; /* 同上 */ top: 0; left: 0; bottom: 0; right: 0; margin:auto; border-radius: 15% 15%; /* 圖片的四個角設定圓度 */ } .loseScore{ /* 在js中呼叫,用來顯示最後的計分結果 */ font-size: 24px; /* 字型大小24px */ color: #fff; position: absolute; left: 130px; /* 計分數字在遊戲結束圖片上找到合適的位置存放 ,這是按自己的圖片設定的*/ top: 80px; } .close{ /* 設定遊戲結束右上角的關閉按鈕 */ background-image: url('img/shanchu.png'); /* 插入關閉按鈕圖片 */ background-size: 100% 100%; /* 平攤圖片 */ width: 35px; /* 設定關閉按鈕的寬高 */ height: 35px; position: absolute; /* 絕對定位 */ top: 0px; right: 0px; /* 圖片放到右上角 */ }
還有部分css程式碼,在js程式碼編輯的時候一同寫出,現在上述的css算是將貪吃蛇遊戲的頁面背景設定完畢。
接下來js程式碼:
var content = document.getElementById('content'); //將html中有id = content的元素提取出來 function init(){ //設定初始時的所有預設值 this.foodW = 20; //表示食物的寬 this.foodH = 20; //高 this.foodX = 0; //表示食物的x座標位置 this.foodY = 0; //y座標位置 this.snakeW = 0; //蛇的寬高 this.snakeH = 0; this.snakeBody = [[4,1,'head'],[3,1,'body'],[2,1,'body']]; //表示三節蛇身(包括蛇頭),用陣列表示 this.mapH = parseInt(getComputedStyle(content).height); //表示遊戲介面的寬高,用getComputedStyle(content).height來提取出content裡的height屬性值 this.mapW = parseInt(getComputedStyle(content).width); //同上含義 this.mapDiv = content; //表示介面整體 this.direct = 'right'; //初始時蛇的方向 this.up = true; // 表示向上蛇可動 this.down = true; //向下可動 this.right = false; //向右邊不可動 this.left = false; //向左邊不可動 //上述表示的具體意思是,當蛇的運動方向為右時,不能操作蛇的左右運動,只能操作上下 this.score = 0; //初始計分為0 bindEvent(); //觸發bindEvent()函式 }
設定完初始值後,開始設定蛇和食物的初始狀態
function food(){ //食物的設定
var food = document.createElement('div'); //生成一個div的食物food
food.style.width = this.foodW + 'px'; //設定食物的寬高,等於上述函式中已經設定好的預設食物寬高
food.style.height = this.foodH + 'px'; //同上
food.style.position = 'absolute'; //設定食物的位置為絕對定位
this.foodX = Math.floor(Math.random()*this.mapW/20); //食物的x座標的隨機位置,表示0到(遊戲介面的寬除以食物自己的寬)的隨機數
this.foodY = Math.floor(Math.random()*this.mapH/20); //食物的y座標的隨機位置,同上
food.style.top = this.foodY * 20 + 'px'; //表示食物的上邊距距離為食物的y座標乘以自己的高
food.style.left = this.foodX * 20 + 'px'; //同上
this.mapDiv.appendChild(food).setAttribute('class','food'); //給每個食物div都設定上名為food的class,再到css中設定食物的大小和載入圖片
}
function snake(){ //蛇的設定
for(var i = 0;i < this.snakeBody.length;i ++){ //用for迴圈,i小於蛇身的長度,(初始時的length為3)
var snake = document.createElement('div'); //生成一個div的蛇snake
snake.style.width = this.snakeW + 20 + 'px'; //設定生成的蛇的寬高
snake.style.height = this.snakeH + 20 + "px";
snake.style.position = 'absolute'; //設定蛇的絕對位置
snake.style.top = this.snakeBody[i][1] * 20 + 'px'; //設定每節蛇的距離上邊距的位置
snake.style.left = this.snakeBody[i][0] * 20 + 'px'; //設定每節蛇的距離左邊距的位置
snake.classList.add(this.snakeBody[i][2]); //給每節蛇新增class,這裡的class為上述的snakeBody中設定的第3個位置上的‘head’和’body‘,因此又需要對head和body設定css程式碼,用來區分蛇頭和蛇身
this.mapDiv.appendChild(snake).classList.add('snake'); //在html中新增上snake的div,並加上class = ‘snake’
switch(this.direct){ //蛇頭的轉向(注:由於我找的蛇頭的方向預設為右邊)
case 'right': //由於預設圖片蛇頭向右所以不需要改變操作
break;
case 'left' : //將蛇頭的方向轉向左邊
snake.style.transform = 'rotate(180deg)';
break;
case 'up' : //將蛇頭方向旋轉到上邊
snake.style.transform = 'rotate(270deg)';
break;
case 'down' : //將蛇頭方向旋轉到下方
snake.style.transform = 'rotate(90deg)';
break;
default :
break;
}
}
}
食物和蛇的css程式碼:
.food{
background-image: url('img/pg.png'); //加入食物的圖片
background-size: 100% 100%;
}
.head{
background-image: url('img/s.png'); //加入蛇頭的圖片
background-size: 100% 100%;
}
.body{
background-image: url('img/she.png'); //加入蛇身的圖片
background-size: 100% 100%;
}
設定完蛇和食物的狀態後,設定遊戲開始時的介面:
var startPage = document.getElementById('startPage'); //從html中獲取到id = startPage的元素節點,startPage表示開始遊戲的按鈕
var startP = document.getElementById('startP'); //同上,startP表示暫停的按鈕
function startGame(){ //遊戲開始
startPage.style.display = 'none'; // 將該節點設定為不顯示
startP.style.display = 'block'; //使該節點顯示
food(); //呼叫food函式
snake(); //呼叫snake函式
}
接下來設定蛇的運動狀態js:
var scoreBox = document.getElementById('score'); //獲取用於計分的節點
function move(){
for(var i = this.snakeBody.length-1;i > 0;i --){ //for迴圈,表示蛇運動時,後一節蛇的位置是前一節蛇之前所在的位置
this.snakeBody[i][0] = this.snakeBody[i-1][0];
this.snakeBody[i][1] = this.snakeBody[i-1][1];
}
switch(this.direct){ //表示蛇頭的轉動位置情況
case 'right' :
this.snakeBody[0][0] +=1; //右邊時,蛇頭的x位置加1
break;
case 'left' :
this.snakeBody[0][0] -=1; //左邊時,蛇頭的x位置減1,
break;
case 'up' :
this.snakeBody[0][1] -=1; //上邊時,蛇頭的y位置減1
break;
case 'down' :
this.snakeBody[0][1] +=1; //下邊時,蛇頭的y位置加1
default:
break;
}
removeClass('snake'); //刪除class = ‘snake’的節點
snake(); //重新呼叫snake函式
if(this.snakeBody[0][0] == this.foodX && this.snakeBody[0][1] == this.foodY){ //當蛇吃到食物時
var snakeEndX = this.snakeBody[this.snakeBody.length-1][0]; //表示吃到食物後,蛇尾的x座標為蛇的長-1
var snakeEndY = this.snakeBody[this.snakeBody.length-1][1]; //同上
switch(this.direct){
case 'right':
this.snakeBody.push([snakeEndX ,snakeEndY,'body']); //蛇頭向右時,蛇尾的x位置需要+1
break;
case 'left':
this.snakeBody.push([snakeEndX ,snakeEndY,'body']); //蛇頭向左時,蛇尾的x位置需要-1
break;
case 'up':
this.snakeBody.push([snakeEndX,snakeEndY ,'body']); //蛇頭向上時,蛇尾的y位置需要-1
break;
case 'down':
this.snakeBody.push([snakeEndX,snakeEndY ,'body']); //蛇頭向下時,蛇尾的y位置需要+1
break;
default:
break;
} //注:當蛇吃到食物時,新增的尾巴會在原來的尾巴的位置上加上,直到蛇頭離開食物的位置,此時的尾巴就會變成真正的尾巴
this.score += 1; //吃到食物,分數+1
scoreBox.innerHTML = this.score; //使html中的id = ‘scoreBox’的節點上加上此時的分數
removeClass('food'); //蛇吃到食物後,所吃的食物消失
food(); //又重新出現新的食物的位置
}
if(this.snakeBody[0][0] < 0 || this.snakeBody[0][0] > this.mapW/20){ //當蛇頭x碰到邊界時,遊戲結束
reloadGame(); //結束遊戲,重新載入遊戲
}
if(this.snakeBody[0][1] < 0 || this.snakeBody[0][1] > this.mapH/20){ //當蛇頭y碰到邊界時,遊戲結束
reloadGame(); //結束遊戲,重新載入遊戲
}
var snakeHX = this.snakeBody[0][0]; //得到蛇頭x
var snakeHY = this.snakeBody[0][1]; //得到蛇頭y
for(var i = 1;i < this.snakeBody.length;i++){ //當蛇頭碰到自己的身體時,遊戲結束
if(snakeHX == snakeBody[i][0] && snakeHY == snakeBody[i][1]){
reloadGame();
}
}
}
接下來是刪除節點的函式:
function removeClass(className){ //給一個class類名
var ele = document.getElementsByClassName(className); //用ele得到html中有該class類名的節點
while(ele.length > 0){ //當節點長度大於0時,即存在該節點
ele[0].parentNode.removeChild(ele[0]); //刪除該節點
}
}
然後是重新載入遊戲的js程式碼:
var loseScore = document.getElementById('loseScore');
function reloadGame(){
removeClass('snake'); //刪除class = ‘snake’的節點,即刪除蛇
removeClass('food'); //刪除食物,同上
clearInterval(snakeMove); //清除snakeMove每隔時間間隔長度運動
this.snakeBody = [[4,1,'head'],[3,1,'body'],[2,1,'body']]; //再新增上預設初始的蛇身座標
this.direct = 'right'; //初始方向
this.right = false;
this.left = false;
this.up = true;
this.down = true;
lose.style.display = 'block'; //使id = ‘lose’的節點出現,即出現遊戲結束後的圖片
loseScore.innerHTML = this.score; //記錄遊戲結束後的最終得分
this.score = 0; //記錄後,清0
scoreBox.innerHTML = this.score; //同時將遊戲開始時的動態積分也清0
startGameBool = true;
startPauseBool = true;
startP.setAttribute('src','./img/begin.png'); //改變id = ‘startP’節點的圖片內容
}
關於操作蛇時的上下左右按鍵設定:
function setDirect(code){ //給該函式一個表示對應按鍵數字
switch(code){
case 37: //表示左,方向為左時,上下的按鍵可觸發但是左右的按鍵不可以觸發
if(this.left){
this.direct = 'left';
this.left = false;
this.right = false;
this.up = true;
this.down = true;
}
break;
case 38: //上 ,同上
if(this.up){
this.direct = 'up';
this.left = true;
this.right = true;
this.up = false;
this.down = false;
}
break;
case 39: //右 ,同上
if(this.right){
this.direct = 'right';
this.left = false;
this.right = false;
this.up= true;
this.down = true;
}
break;
case 40: //下 ,同上
if(this.down){
this.direct = 'down';
this.left = true;
this.right = true;
this.up = false;
this.down = false;
}
break;
default:
break;
}
}
接下來是關於遊戲的開始和暫停的設定:
var startGameBool = true; //表示該鑰匙是開著時
var startPauseBool = true;
var snakeMove;
var speed = 200;
function startAndPush(){
if(startPauseBool){ //關於暫停的鑰匙
if(startGameBool){ //關於遊戲剛開啟時的鑰匙
startGame(); //遊戲開始,啟動遊戲開始的介面
startGameBool = false; //遊戲開始後,不需要再出現這個過程,將該鑰匙鎖住
}
startP.setAttribute('src','./img/kk.png'); //為html中的id = ‘startP’設定插入圖片
document.onkeydown = function(e){ //觸發按鍵發生的時間
var code = e.keyCode; //得到所觸發按鍵所表示的數字
setDirect(code); //呼叫上述的按鍵方向函式
}
snakeMove = setInterval(function(){ 表示每隔speed時間啟用一次
move(); 呼叫move函式,表示每隔speed時間,執行一次move函式事件
},speed);
startPauseBool = false; //執行過後,需要上鎖,防止該事件在遊戲過程中被啟用出錯
}else{ //當鑰匙關閉時,執行的事件
startP.setAttribute('src','./img/zanting.png'); //改變id = startP的節點中的圖片內容
clearInterval(snakeMove); //清除snakeMove每隔speed時間的執行
document.onkeydown = function(e){ //表示禁止按鍵啟用
e.returnValue = false;
return false;
}
startPauseBool = true; //執行上述事件後,將鎖開啟
}
}
之後是關於主體啟用鍵盤按鍵和滑鼠按鍵事件:
var lose = document.getElementById('lose');
var close = document.getElementById('close');
var startBtn = document.getElementById('startBtn');
function bindEvent(){
document.onkeydown = function(e){ //同上述描述,是按鍵的數字獲取和啟用方向事件
var code = e.keyCode;
setDirect(code)
}
close.onclick = function(){ //表示遊戲結束後右上角的關閉按鍵的觸發事件
lose.style.display = 'none'; //使遊戲結束介面不顯示
}
startBtn.onclick = function(){ //表示剛啟動遊戲時的開始按鈕
startAndPush();
}
startP.onclick = function(){ //表示遊戲中的暫停和解除暫停的滑鼠按鍵事件
startAndPush();
}
}
上述就是所有的js程式碼。。。。
看下成果圖:
注:上述程式碼如有解釋錯誤的,請大佬幫忙改善,謝謝,有不清楚的也可以留言,本人會盡量及時回覆的