1. 程式人生 > >學寫前端原生貪吃蛇

學寫前端原生貪吃蛇

看了人家視訊上寫的貪吃蛇,瞬間覺得自己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程式碼。。。。
看下成果圖:在這裡插入圖片描述
在這裡插入圖片描述
注:上述程式碼如有解釋錯誤的,請大佬幫忙改善,謝謝,有不清楚的也可以留言,本人會盡量及時回覆的