用JS實現(xiàn)的貪吃蛇游戲

需要用到html、css、javascript 和 DOM 這些知識點就可以了。主要是js,其他只是一些基本的知識。js貌似也不是很難。但是問題就在這里,即使知識點都會了,但是還是無法綜合運用把東西做出來

創(chuàng)新互聯(lián)專業(yè)成都做網(wǎng)站、成都網(wǎng)站制作,集網(wǎng)站策劃、網(wǎng)站設(shè)計、網(wǎng)站制作于一體,網(wǎng)站seo、網(wǎng)站優(yōu)化、網(wǎng)站營銷、軟文發(fā)稿等專業(yè)人才根據(jù)搜索規(guī)律編程設(shè)計,讓網(wǎng)站在運行后,在搜索中有好的表現(xiàn),專業(yè)設(shè)計制作為您帶來效益的網(wǎng)站!讓網(wǎng)站建設(shè)為您創(chuàng)造效益。

游戲界面

先把整個游戲界面做出來:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>貪吃蛇</title>
    <style>
        *{padding: 0; margin: 0;}
        .title{text-align: center; margin: 10px 0;}
        #main{width: 800px; height: 600px; border:1px solid red; margin: 0 auto;}
        #main .left{width: 600px; height: 600px; float: left;
            position: relative;}
        /*隨機的食物通過position了定位的,所以父標簽要加上position: relative*/
        #main .right{width: 200px; height: 100%; float: left; border-left: 1px solid red;
            box-sizing: border-box; text-align: center}
        /*整體寬度包括left和right的以及內(nèi)部元素的邊框,這樣寬度不夠,會跑到下一行。這里用了box-sizing解決問題*/
        #main .right h3{margin: 10px auto; text-align: center;}
        #main .right h3 #score{color: red;}
        #main .right button{width: 100px; height: 30px; font-size: 20px; margin-top: 30px;
            border: 0; border-radius: 5px;
            background-color: pink; color: green;}
        .food{background-color: black;}
        .snake{background-color: darkgreen}
    </style>
</head>
<body>
<h3 class="title">貪吃蛇</h3>
<div id="main">
    <div class="left"></div>
    <div class="right">
        <h3 class="status">請點擊開始</h3>
        <h3>分數(shù):<span id="score">0</span></h3>
        <button id="btn">開始</button>
    </div>
</div>
<script>
    // 先空著
</script>
</body>
</html>

一個標題,然后下面是游戲界面。界面分2部分,左邊是600*600的正方形,右邊寬度200,可以顯示一些游戲信息。
這里有一個問題,就是界面寬800,左邊寬600,右邊寬200。不過中間還有個邊框至少還要占1的寬度。所以右邊的部分會被擠到下一行。把800的總寬度加一點可以解決,也可以像這里這樣,使用 box-sizing 屬性。
box-sizing:border-box; :為元素設(shè)定的寬度和高度決定了元素的邊框盒。就是說,為元素指定的任何內(nèi)邊距和邊框都將在已設(shè)定的寬度和高度內(nèi)進行繪制。通過從已設(shè)定的寬度和高度分別減去邊框和內(nèi)邊距才能得到內(nèi)容的寬度和高度。

初始化游戲

上面的界面還缺少蛇和食物。這里就是一個一個的小方塊。食物是一個方塊,蛇初始是連續(xù)的3個方塊。小方塊就是帶背景色的div,背景色已經(jīng)在上面的css里寫上了。
另外蛇和食物的位置都要用 position: absolute; 來做定位。已經(jīng)提前在他們的父元素就是left界面里設(shè)置好了 position: relative;。

初始化食物

直接調(diào)用一個初始化的方法 init() ,然后是定義這個初始化方法。下面只是定義了食物的一些屬性,最后調(diào)用了一個生成食物的方法 food() :

    // 初始化
    init();
    // 初始化方法
    function init() {
        // 獲取地圖的跨度和高度
        this.map_width = parseInt(getComputedStyle(map).width);
        this.map_height = parseInt(getComputedStyle(map).height);

        // 食物的高度和寬度
        this.food_width = 20;
        this.food_height = 20;
        // 食物的位置,先定義在左上角看看樣子,之后再搞隨機位置
        this.food_X = 0;
        this.food_Y = 0;
        food();  // 生成食物
    }

生成食物的方法。這里主要是生成一個div,然后把這個div添加到map元素里。位置的話使用相對定位,關(guān)鍵是計算出橫坐標和縱坐標:

    // 獲取變量
    var map = document.getElementsByClassName('left')[0];
    // 生成食物
    function food() {
        // 創(chuàng)建食物
        var foodBox = document.createElement('div');
        foodBox.style.width = this.food_width+"px";
        foodBox.style.height = this.food_height+"px";
        // 食物的位置
        this.food_X = Math.floor(Math.random()*this.map_width/this.food_width);  // 0~29之間
        this.food_Y = Math.floor(Math.random()*this.map_height/this.food_height);
        foodBox.style.position = 'absolute';
        foodBox.style.top = this.food_Y*this.food_height+"px";  // 隨機數(shù)乘以寬度
        foodBox.style.left = this.food_X*this.food_width+"px";
        // 設(shè)置一個類名,然后css給這個類定義樣式
        foodBox.className = "food";
        // 將食物追加到map中
        map.appendChild(foodBox);
    }

初始化蛇

首先在初始化方法里追加蛇的屬性。這里蛇是由連續(xù)的小方塊組成。那么蛇就是一個數(shù)組,數(shù)組里的每個元素就是組成蛇的一個一個的小方塊。有3個參數(shù):水平位置、垂直位置、是否是蛇頭:

    // 初始化方法
    function init() {
        // 省略之前的代碼
        // 食物的位置,先定義在左上角看看樣子,之后再搞隨機位置
        // this.food_X = 0;
        // this.food_Y = 0;
        food();  // 生成食物

        // 初始化蛇身
        this.snake_width = 20;
        this.snake_height = 20;
        // 下面是一個3段的蛇,第一個參數(shù)是水平位置,第二個參數(shù)是垂直位置,但三個參數(shù)是是否是頭部
        this.snake_body = [[2, 0, true], [1, 0, false], [0, 0, false]];
        snake(); // 生成蛇身
    }

上面調(diào)用了snake()方法來生成蛇。初始化的時候,蛇始終是生成在左上角。蛇的每一段其實都是獨立的,這里用一個for循環(huán),把snake_body的每一段都添加到了map中。這里是根據(jù)snake_boy來添加蛇,也就是說之后,任何時候只要修改了snake_body這個數(shù)組,然后調(diào)用snake()方法,就是生成一個蛇了:

    // 生成蛇身
    function snake() {
        // 用for循環(huán)遍歷數(shù)組,將每一段作為一個div然后添加
        for(var i=0; i<this.snake_body.length; i++){
            // 創(chuàng)建蛇身
            var snakeBox = document.createElement('div');
            // 高度和寬度
            snakeBox.style.width = this.snake_width+"px";
            snakeBox.style.height = this.snake_height+"px";
            // 定位
            snakeBox.style.position = 'absolute';
            // 位置
            snakeBox.style.top = this.snake_body[i][1]*this.snake_width+"px";
            snakeBox.style.left = this.snake_body[i][0]*this.snake_height+"px";
            // 設(shè)置一個類名,然后css給這個類定義樣式
            snakeBox.className = "snake";
            // 追加到map中
            map.appendChild(snakeBox);
        }
    }

小結(jié)

上面已經(jīng)生成了完整的游戲初始化的界面,包括隨機生成的一個食物,以及左上角的長度為3段的蛇。完整的js代碼如下:

<script>
    // 獲取變量
    var map = document.getElementsByClassName('left')[0];
    // 初始化
    init();
    // 初始化方法
    function init() {
        // 獲取地圖的跨度和高度
        this.map_width = parseInt(getComputedStyle(map).width);
        this.map_height = parseInt(getComputedStyle(map).height);

        // 食物的高度和寬度
        this.food_width = 20;
        this.food_height = 20;
        // 食物的位置,先定義在左上角看看樣子,之后再搞隨機位置
        // this.food_X = 0;
        // this.food_Y = 0;
        food();  // 生成食物

        // 初始化蛇身
        this.snake_width = 20;
        this.snake_height = 20;
        // 下面是一個3段的蛇,第一個參數(shù)是水平位置,第二個參數(shù)是垂直位置,但三個參數(shù)是是否是頭部
        this.snake_body = [[2, 0, true], [1, 0, false], [0, 0, false]];
        snake(); // 生成蛇身
    }

    // 生成蛇身
    function snake() {
        // 用for循環(huán)遍歷數(shù)組,將每一段作為一個div然后添加
        for(var i=0; i<this.snake_body.length; i++){
            // 創(chuàng)建蛇身
            var snakeBox = document.createElement('div');
            // 高度和寬度
            snakeBox.style.width = this.snake_width+"px";
            snakeBox.style.height = this.snake_height+"px";
            // 定位
            snakeBox.style.position = 'absolute';
            // 位置
            snakeBox.style.top = this.snake_body[i][1]*this.snake_width+"px";
            snakeBox.style.left = this.snake_body[i][0]*this.snake_height+"px";
            // 設(shè)置一個類名,然后css給這個類定義樣式
            snakeBox.className = "snake";
            // 追加到map中
            map.appendChild(snakeBox);
        }
    }

    // 生成食物
    function food() {
        // 創(chuàng)建食物
        var foodBox = document.createElement('div');
        foodBox.style.width = this.food_width+"px";
        foodBox.style.height = this.food_height+"px";
        // 食物的位置
        this.food_X = Math.floor(Math.random()*this.map_width/this.food_width);  // 0~29之間
        this.food_Y = Math.floor(Math.random()*this.map_height/this.food_height);
        foodBox.style.position = 'absolute';
        foodBox.style.top = this.food_X*this.food_width+"px";  // 隨機數(shù)乘以寬度
        foodBox.style.left = this.food_Y*this.food_height+"px";
        // 設(shè)置一個類名,然后css給這個類定義樣式
        foodBox.className = "food";
        // 將食物追加到map中
        map.appendChild(foodBox);
    }
</script>

移動蛇

首先來實現(xiàn)點擊開始按鈕,開始游戲

開始按鈕

點擊開始按鈕,調(diào)用start_game()方法,觸發(fā)游戲運行,并且變成暫停按鈕。點擊暫停按鈕,調(diào)用pause_game()方法,暫停游戲,并且再變?yōu)殚_始按鈕。這里還獲取了一個status變量,可以隨時改變它的innerHtml的內(nèi)容,在頁面上顯示一些信息,調(diào)試的時候一些中間過程也可以實現(xiàn)在這里:

    var status = document.getElementsByClassName('status')[0];
    // 點擊按鈕
    var btn = document.getElementById('btn');
    btn.onclick = function btn() {
        if (this.innerHTML==="開始"){
            start_game();
            this.innerHTML = "暫停";
        } else {
            pause_game();
            this.innerHTML = "開始";
        }
    };

開始游戲

開始游戲就是啟動一個定時器。這里首先修改了status里顯示的內(nèi)容,然后創(chuàng)建了一個定時器:

    // 開始游戲
    function start_game() {
        status.innerHTML = "游戲運行中";
        this.snakeMove = setInterval(move, 100);
    }

上面的定時器調(diào)用了move,下面就來寫這個move方法。move方法就是讓蛇移動,這里先讓蛇一直往左移動。具體的做法就是設(shè)置snake_body數(shù)組,然后調(diào)用snake()方法,生成一個新的蛇。把之前的蛇清除了,然后把新的蛇添加進去,看上去取出蛇在移動的效果了:

    // 蛇移動的方法
    function move() {
        // 從尾端開始,數(shù)組里的后一個值用數(shù)組的前一個值替代
        for(var i=this.snake_body.length-1; i>0; i--){
            this.snake_body[i][0] = this.snake_body[i-1][0];
            this.snake_body[i][1] = this.snake_body[i-1][1];
        }
        // 數(shù)組最前的那個值,就是蛇頭
        this.snake_body[0][0] += 1;

        // 重新生成蛇,才能在頁面上有變化
        // 先移除原有的蛇身體
        clearBox('snake');  // 這個方法可以復(fù)用,只有要清除食物的時候也能用到
        // 然后繪制新的蛇身
        snake();
    }

    // 清除Box
    function clearBox(class_name) {
        var box = document.getElementsByClassName(class_name);
        while(box.length){
            map.removeChild(box[0]);
        }
    }

這里移除蛇身體的方法,之后要移除食物的時候也能用。只要傳入不同的類名,就能把這個類的元素都清除了。

暫停游戲

暫停游戲就很簡單了,就是清除掉計時器就好了。再點開始又會重新生成新的計時器:

    // 暫停游戲
    function pause_game() {
        // 暫停游戲就是清除定時器
        status.innerHTML = "游戲暫停...";
        clearInterval(this.snakeMove)
    }

鍵盤事件

在開始游戲的時候,為鍵盤綁定事件:

    // 開始游戲
    function start_game() {
        status.innerHTML = "游戲運行中";
        this.snakeMove = setInterval(move, 100);
        // 綁定鍵盤按下的事件
        bindKeyDown();
    }

    // 鍵盤按下的事件
    function bindKeyDown() {
        window.onkeydown = function (ev) {
            status.innerHTML = ev.keyCode;
        }
    }

這里先看看keyCode這個屬性的值。按下不同的按鈕,值是不同的,根據(jù)這個值來判斷按的是哪個按鈕,然后觸發(fā)響應(yīng)的方法。
左37、上38、右39、下40。還有空格是32。

根據(jù)方向來移動蛇

下面就來為這些事件寫上不同的處理方法:

    // 鍵盤按下的事件
    function bindKeyDown() {
        window.onkeydown = function (ev) {
            // status.innerHTML = ev.keyCode;
            var code = ev.keyCode;  // 獲取按鍵
            switch (code){
                case 37:
                    this.direction = 'left';
                    break;
                case 38:
                    this.direction = 'up';
                    break;
                case 39:
                    this.direction = 'right';
                    break;
                case 40:
                    this.direction = 'down';
                    break;
                case 32:
                    // 我這里還希望開始游戲后,可以用空格控制暫停和開始
                    btn();  // 前面的按鈕事件沒寫
                    break;
            }
        }
    }

這里只是修改了this.direction這個屬性的值。接下來要修改之前寫的move()方法,根據(jù)這個屬性的值,給出朝不同方法移動的效果。這里有了方向這個概念,在初始化的時候也要把方向?qū)傩赃M行初始化。把下面這句添加到初始化函數(shù)init()中去:

this.direction = 'right';

最后來修改move()方法,之前是默認向右移動的,現(xiàn)在要根據(jù)this.direction的值來確定向哪里移動:

    // 蛇移動的方法
    function move() {
        // 從尾端開始,數(shù)組里的后一個值用數(shù)組的前一個值替代
        for(var i=this.snake_body.length-1; i>0; i--){
            this.snake_body[i][0] = this.snake_body[i-1][0];
            this.snake_body[i][1] = this.snake_body[i-1][1];
        }

        // 根據(jù)方法來操作
        switch (this.direction){
            case 'left':
                this.snake_body[0][0] -= 1;
                break;
            case 'right':
                this.snake_body[0][0] += 1;
                break;
            case 'up':
                this.snake_body[0][1] -= 1;
                break;
            case 'down':
                this.snake_body[0][1] += 1;
                break;
        }
        // 數(shù)組最前的那個值,就是蛇頭
        // this.snake_body[0][0] += 1;

        // 重新生成蛇,才能在頁面上有變化
        // 先移除原有的蛇身體
        clearBox('snake');  // 這個方法可以復(fù)用,只有要清除食物的時候也能用到
        // 然后繪制新的蛇身
        snake();
    }

優(yōu)化移動方向

現(xiàn)在的蛇是自由移動的,按照游戲規(guī)則,蛇不不能直接掉頭的。所以要修改一個按鍵事件的case出的內(nèi)容,只有在特定的條件下才響應(yīng)鍵盤方向的事件:

    // 鍵盤按下的事件
    function bindKeyDown() {
        window.onkeydown = function (ev) {
            // status.innerHTML = ev.keyCode;
            var code = ev.keyCode;  // 獲取按鍵
            switch (code){
                case 37:
                    if (this.direction === 'up' ||  this.direction === 'down') {
                        this.direction = 'left';
                    }
                    break;
                case 38:
                    if (this.direction === 'left' ||  this.direction === 'right'){
                        this.direction = 'up';
                    }
                    break;
                case 39:
                    if (this.direction === 'up' ||  this.direction === 'down'){
                        this.direction = 'right';
                    }
                    break;
                case 40:
                    if (this.direction === 'left' ||  this.direction === 'right'){
                        this.direction = 'down';
                    }
                    break;
                case 32:
                    // 我這里還希望開始游戲后,可以用空格控制暫停和開始
                    btn();  // 前面的按鈕事件沒寫
                    break;
            }
        }
    }

移動方法

之前的move()方法只是讓蛇動起來。每次移動后,當(dāng)要判斷一下當(dāng)前的位置。比如:吃到食物了、撞墻了或者撞到蛇身了。

吃食物

吃食物的邏輯放在蛇移動的move()方法里。在計算好新的蛇身數(shù)組后,增加判斷蛇頭位置的邏輯:

    // 蛇移動的方法
    function move() {
        // 前面是生成新的蛇身數(shù)組的代碼

        // 判斷蛇頭吃食物
        if (this.snake_body[0][0]===this.food_X && this.snake_body[0][1]===this.food_Y){
            // status.innerHTML = "吃到食物了"
            clearBox("food");  // 移除食物
            food();  // 生成新的食物
            // 增加分數(shù),先把下面這句加到最上面的獲取變量里
            // var scoreBox = document.getElementById('score');
            // 在去初始化函數(shù)init()里,加一條初始化分數(shù)變量
            // this.score = 0;
            this.score += 1;
            scoreBox.innerHTML = this.score;
            // 增加蛇身長度
            // var snake_end = this.snake_body[this.snake_body.length-1];  // 這個是錯誤的
            var snake_end = Array.from(this.snake_body[this.snake_body.length-1]);  // 這里需要深copy
            this.snake_body.push(snake_end);
        }

        // 后面是移除原有蛇身,然后繪制新蛇身的方法
    }

這里增加蛇蛇身長度的需要注意一下。
可以這么做,把新生成的身體的一段放到一個臨時的位置,等移動一次以后,就會變成正常的位置:

var snake_end = [0, 0, false]
this.snake_body.push(snake_end);

上面那么做肯定不好看,最好是追加到生當(dāng)前身體最后一段的位置上,但是不能簡單的向下面這么做:

 var snake_end = this.snake_body[this.snake_body.length-1];  // 這個是錯誤的
 this.snake_body.push(snake_end);

由于 this.snake_body[this.snake_body.length-1] 本身也是一個數(shù)組,如果直接賦值給snake_end的話,就是一個地址引用(淺copy)。這里要么獲取到具體的值,生成snake_end這個數(shù)組,比如下面這樣:

            var snake_last = this.snake_body[this.snake_body.length-1];
            var snake_end = [snake_last[0], snake_last[1], false];
            this.snake_body.push(snake_end);

或者就是像例子中那樣要做一個數(shù)組的深copy,把數(shù)組里的值賦值給snake_end。而不是直接的賦值操作。

判斷撞墻

我先在開是的位置增加一個變量wall,用來開啟撞墻判斷的功能,這樣如果只有不想撞墻的話,還能方便的關(guān)掉:

    // 設(shè)置自定義的游戲參數(shù)
    var wall = true;  // 開啟撞墻

繼續(xù)在move()方法里,在判斷吃食物的后面添加判斷撞墻的邏輯。撞墻之后,就是

    // 蛇移動的方法
    function move() {

        // 判斷蛇頭吃食物

        // 判斷撞墻
        wall && (
            this.snake_body[0][0]<0 || this.snake_body[0][0]>=this.map_width/this.snake_width ||
            this.snake_body[0][1]<0 || this.snake_body[0][1]>=this.map_height/this.snake_height
        ) && game_over();

        // 后面是移除原有蛇身,然后繪制新蛇身的方法

    }

    // 游戲結(jié)束的方法
    function game_over() {
        status.innerHTML = "Game Over";
        clearInterval(this.snakeMove);
        alert("游戲結(jié)束\n分數(shù):"+this.score);
        clearBox('snake');
        clearBox('food');
        init();
    }

上面的move()函數(shù)里只復(fù)雜判斷是否撞墻,如果撞墻就調(diào)用game_over()方法。

撞到蛇身

先手動設(shè)置一個初始的長蛇身,避免測試撞擊蛇身之前要先玩一段時間的尷尬。還是把這些自定義參數(shù)放到開頭部分:

    // 設(shè)置自定義的游戲參數(shù)
    var snake_length = 15;  // 正整數(shù),增加額外的蛇身體。0就是最小的3段,1就是額外增加1段。
    var wall_snake = true;  // 開啟撞擊蛇身
    var wall = true;  // 開啟撞墻

修改一下初始化方法,在初始化的時候就生成一個很長的蛇身體,完整的初始化init()方法:

    // 初始化方法
    function init() {
        // 獲取地圖的跨度和高度
        this.map_width = parseInt(getComputedStyle(map).width);
        this.map_height = parseInt(getComputedStyle(map).height);

        // 初始化成績和界面
        this.score = 0;
        scoreBox.innerHTML = this.score;
        btn.innerHTML = "開始";

        // 食物的高度和寬度
        this.food_width = 20;
        this.food_height = 20;
        // 食物的位置,先定義在左上角看看樣子,之后再搞隨機位置
        // this.food_X = 0;
        // this.food_Y = 0;
        food();  // 生成食物

        // 初始化蛇身
        this.snake_width = 20;
        this.snake_height = 20;
        // 下面是一個3段的蛇,第一個參數(shù)是水平位置,第二個參數(shù)是垂直位置,但三個參數(shù)是是否是頭部
        // 最后發(fā)現(xiàn)第三個參數(shù)并沒有用
        this.snake_body = [[2, 0, true], [1, 0, false], [0, 0, false]];
        // 追加額外的蛇身
        if (typeof snake_length === 'number' && snake_length%1 === 0) {
            // 綜合上面的2個條件,可以判斷snake_length是一個整數(shù)
            for (var i=0; i<snake_length; i++) {
                this.snake_body.push([0, 0, false])
            }
        }
        // 蛇移動的方向
        this.direction = 'right';
        snake(); // 生成蛇身
    }

好了,現(xiàn)在不用玩了,直接可以鉆出一條大長龍。
然后是撞擊蛇身的判斷,還是move()函數(shù)里,放到之前撞墻的后面

    // 蛇移動的方法
    function move() {

        // 判斷蛇頭吃食物

        // 判斷撞墻
        wall && (
            this.snake_body[0][0]<0 || this.snake_body[0][0]>=this.map_width/this.snake_width ||
            this.snake_body[0][1]<0 || this.snake_body[0][1]>=this.map_height/this.snake_height
        ) && game_over();

        // 判斷撞到蛇身
        if (wall_snake) {
            for (var i=1; i<this.snake_body.length; i++) {
                this.snake_body[i][0] === this.snake_body[0][0] &&
                this.snake_body[i][1] === this.snake_body[0][1] &&
                game_over();
            }
        }

        // 后面是移除原有蛇身,然后繪制新蛇身的方法

    }

這里沒有什么難點??梢匀ピ囍惨幌伦约涸囋?。

總結(jié)

上面也只是有個大概的樣子。基本的東西都搞出來了。還有優(yōu)化空間,另外也還有些小BUG其實。下面跳上最終我自己的完整的代碼,有興趣的話自己修改:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>貪吃蛇</title>
    <style>
        *{padding: 0; margin: 0;}
        .title{text-align: center; margin: 10px 0;}
        #main{width: 800px; height: 600px; border:1px solid red; margin: 0 auto;}
        #main .left{width: 600px; height: 600px; float: left;
            position: relative;}
        /*隨機的食物通過position了定位的,所以父標簽要加上position: relative*/
        #main .right{width: 200px; height: 100%; float: left; border-left: 1px solid red;
            box-sizing: border-box; text-align: center}
        /*整體寬度包括left和right的以及內(nèi)部元素的邊框,這樣寬度不夠,會跑到下一行。這里用了box-sizing解決問題*/
        #main .right h3{margin: 10px auto; text-align: center;}
        #main .right h3 #score{color: red;}
        #main .right button{width: 100px; height: 30px; font-size: 20px; margin-top: 30px;
            border: 0; border-radius: 5px;
            background-color: pink; color: green;}
        .food{background-color: black;}
        .snake{background-color: darkgreen}
    </style>
</head>
<body>
<h3 class="title">貪吃蛇</h3>
<div id="main">
    <div class="left"></div>
    <div class="right">
        <h3 class="status">請點擊開始</h3>
        <h3>分數(shù):<span id="score">0</span></h3>
        <button id="btn">開始</button>
    </div>
</div>
<script>
    // 設(shè)置自定義的游戲參數(shù)
    var snake_length = 15;  // 正整數(shù),增加額外的蛇身體。0就是最小的3段,1就是額外增加1段。
    var wall_snake = true;  // 開啟撞擊蛇身
    var wall = true;  // 開啟撞墻
    // 獲取變量
    var map = document.getElementsByClassName('left')[0];
    var status = document.getElementsByClassName('status')[0];
    var scoreBox = document.getElementById('score');
    var btn = document.getElementById('btn');
    // 初始化
    init();
    // 初始化方法
    function init() {
        // 獲取地圖的跨度和高度
        this.map_width = parseInt(getComputedStyle(map).width);
        this.map_height = parseInt(getComputedStyle(map).height);

        // 初始化成績和界面
        this.score = 0;
        scoreBox.innerHTML = this.score;
        btn.innerHTML = "開始";

        // 食物的高度和寬度
        this.food_width = 20;
        this.food_height = 20;
        // 食物的位置,先定義在左上角看看樣子,之后再搞隨機位置
        // this.food_X = 0;
        // this.food_Y = 0;
        food();  // 生成食物

        // 初始化蛇身
        this.snake_width = 20;
        this.snake_height = 20;
        // 下面是一個3段的蛇,第一個參數(shù)是水平位置,第二個參數(shù)是垂直位置,但三個參數(shù)是是否是頭部
        // 最后發(fā)現(xiàn)第三個參數(shù)并沒有用
        this.snake_body = [[2, 0, true], [1, 0, false], [0, 0, false]];
        // 追加額外的蛇身
        if (typeof snake_length === 'number' && snake_length%1 === 0) {
            // 綜合上面的2個條件,可以判斷snake_length是一個整數(shù)
            for (var i=0; i<snake_length; i++) {
                this.snake_body.push([0, 0, false])
            }
        }
        // 蛇移動的方向
        this.direction = 'right';
        snake(); // 生成蛇身
    }

    // 點擊按鈕
    btn.onclick = function () {
        if (this.innerHTML==="開始"){
            start_game();
            this.innerHTML = "暫停";
        } else {
            pause_game();
            this.innerHTML = "開始";
        }
    };

    // 開始游戲
    function start_game() {
        status.innerHTML = "游戲運行中";
        this.snakeMove = setInterval(move, 100);
        // 綁定鍵盤按下的事件
        bindKeyDown();
    }

    // 鍵盤按下的事件
    function bindKeyDown() {
        window.onkeydown = function (ev) {
            // status.innerHTML = ev.keyCode;
            var code = ev.keyCode;  // 獲取按鍵
            switch (code){
                case 37:
                    if (this.direction === 'up' ||  this.direction === 'down') {
                        this.direction = 'left';
                    }
                    break;
                case 38:
                    if (this.direction === 'left' ||  this.direction === 'right'){
                        this.direction = 'up';
                    }
                    break;
                case 39:
                    if (this.direction === 'up' ||  this.direction === 'down'){
                        this.direction = 'right';
                    }
                    break;
                case 40:
                    if (this.direction === 'left' ||  this.direction === 'right'){
                        this.direction = 'down';
                    }
                    break;
                case 32:
                    // 我這里還希望開始游戲后,可以用空格控制暫停和開始
                    btn();  // 前面的按鈕事件沒寫
                    break;
            }
        }
    }

    // 蛇移動的方法
    function move() {
        // 從尾端開始,數(shù)組里的后一個值用數(shù)組的前一個值替代
        for(var i=this.snake_body.length-1; i>0; i--){
            this.snake_body[i][0] = this.snake_body[i-1][0];
            this.snake_body[i][1] = this.snake_body[i-1][1];
        }

        // 根據(jù)方法來操作
        switch (this.direction){
            case 'left':
                this.snake_body[0][0] -= 1;
                break;
            case 'right':
                this.snake_body[0][0] += 1;
                break;
            case 'up':
                this.snake_body[0][1] -= 1;
                break;
            case 'down':
                this.snake_body[0][1] += 1;
                break;
        }
        // 數(shù)組最前的那個值,就是蛇頭
        // this.snake_body[0][0] += 1;

        // 判斷蛇頭吃食物
        if (this.snake_body[0][0]===this.food_X && this.snake_body[0][1]===this.food_Y){
            // status.innerHTML = "吃到食物了"
            clearBox("food");  // 移除食物
            food();  // 生成新的食物
            // 增加分數(shù),先把下面這句加到最上面的獲取變量里
            // var scoreBox = document.getElementById('score');
            // 在去初始化函數(shù)init()里,加一條初始化分數(shù)變量
            // this.score = 0;
            this.score += 1;
            scoreBox.innerHTML = this.score;
            // 增加蛇身長度
            // var snake_end = this.snake_body[this.snake_body.length-1];  // 這個是錯誤的
            var snake_end = Array.from(this.snake_body[this.snake_body.length-1]);  // 這里需要深copy
            this.snake_body.push(snake_end);
        }

        // 判斷撞墻
        wall && (
            this.snake_body[0][0]<0 || this.snake_body[0][0]>=this.map_width/this.snake_width ||
            this.snake_body[0][1]<0 || this.snake_body[0][1]>=this.map_height/this.snake_height
        ) && game_over();

        // 判斷撞到蛇身
        if (wall_snake) {
            for (var i=1; i<this.snake_body.length; i++) {
                this.snake_body[i][0] === this.snake_body[0][0] &&
                this.snake_body[i][1] === this.snake_body[0][1] &&
                game_over();
            }
        }

        // 重新生成蛇,才能在頁面上有變化
        // 先移除原有的蛇身體
        clearBox('snake');  // 這個方法可以復(fù)用,只有要清除食物的時候也能用到
        // 然后繪制新的蛇身
        snake();
    }

    // 游戲結(jié)束的方法
    function game_over() {
        status.innerHTML = "Game Over";
        clearInterval(this.snakeMove);
        alert("游戲結(jié)束\n分數(shù):"+this.score);
        clearBox('snake');
        clearBox('food');
        init();
    }

    // 清除Box
    function clearBox(class_name) {
        var box = document.getElementsByClassName(class_name);
        while(box.length){
            map.removeChild(box[0]);
        }
    }

    // 暫停游戲
    function pause_game() {
        // 暫停游戲就是清除定時器
        status.innerHTML = "游戲暫停...";
        clearInterval(this.snakeMove)
    }

    // 生成蛇身
    function snake() {
        // 用for循環(huán)遍歷數(shù)組,將每一段作為一個div然后添加
        for(var i=0; i<this.snake_body.length; i++){
            // 創(chuàng)建蛇身
            var snakeBox = document.createElement('div');
            // 高度和寬度
            snakeBox.style.width = this.snake_width+"px";
            snakeBox.style.height = this.snake_height+"px";
            // 定位
            snakeBox.style.position = 'absolute';
            // 位置
            snakeBox.style.top = this.snake_body[i][1]*this.snake_width+"px";
            snakeBox.style.left = this.snake_body[i][0]*this.snake_height+"px";
            // 設(shè)置一個類名,然后css給這個類定義樣式
            snakeBox.className = "snake";
            // 追加到map中
            map.appendChild(snakeBox);
        }
    }

    // 生成食物
    function food() {
        // 創(chuàng)建食物
        var foodBox = document.createElement('div');
        foodBox.style.width = this.food_width+"px";
        foodBox.style.height = this.food_height+"px";
        // 食物的位置
        this.food_X = Math.floor(Math.random()*this.map_width/this.food_width);  // 0~29之間
        this.food_Y = Math.floor(Math.random()*this.map_height/this.food_height);
        foodBox.style.position = 'absolute';
        foodBox.style.top = this.food_Y*this.food_height+"px";  // 隨機數(shù)乘以寬度
        foodBox.style.left = this.food_X*this.food_width+"px";
        // 設(shè)置一個類名,然后css給這個類定義樣式
        foodBox.className = "food";
        // 將食物追加到map中
        map.appendChild(foodBox);
    }
</script>
</body>
</html>

標題名稱:用JS實現(xiàn)的貪吃蛇游戲
網(wǎng)頁鏈接:http://muchs.cn/article42/ghgdec.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站設(shè)計公司商城網(wǎng)站、用戶體驗、做網(wǎng)站軟件開發(fā)、域名注冊

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

網(wǎng)站托管運營