JavaScript 精美貪吃蛇實現流程

這篇文章不簡單!!

今天博主嘔心瀝血寫瞭一個貪吃蛇的小遊戲,整個過程從無到有簡直扣人心弦。

接下來本博主就來好好說到說到這個過程!!

話不多說,我們還是先來看看最後的呈現效果吧。

看完瞭才知道什麼叫做操作,簡直傳奇!!

接下來不吹牛來講講實際操作 !

首先我們要知道所謂的貪吃蛇無非就是在表格上面行走的顏色!最初的效果也就是如下圖所示

當然在這個地方是用數組來操作的,也是用構造函數來封裝的。

在這個地方封裝瞭三個 js 文件,分別用來寫 食物、蛇、還有我們的中介者 Game.js 文件!

我剛剛也說瞭,寫貪吃蛇是一個從無到有的過程,所以最開始在我們的 index.html 裡面什麼都沒有,就隻有一個 div 盒子,其他的表格、蛇、食物都是後期用函數創建的。

一、創建html結構

以下是我們 body 裡面最開始的代碼:

<body>
    <div id="app">
    </div>
    <script src="js/Snake.js"></script>
    <script src="js/Food.js"></script>
    <script src="js/Game.js"></script>
    <script>
        var game=new Game();
    </script>
</body>

二、創建表格

從無到有第一步,當然是先把我們的表格追加到頁面上,得有一個大體的框架!!這樣就能看到剛剛上面圖中所示的表格瞭,看到這仿佛就覺得接下來的每一步都是值得的!

function Game() {
    this.row = 25;  // 行數
    this.col = 25;  // 列數
    this.init();    //初始化節點
}
Game.prototype.init = function () {
    this.dom = document.createElement('table'); // 創建表格
    var tr, td;
    // 遍歷行和列
    for (var i = 0; i < this.row; i++) {
        tr = document.createElement('tr');  // 創建行
        for (var j = 0; j < this.col; j++) {
            td = document.createElement('td');  // 創建列
            tr.appendChild(td);     // 把列追加到行
        }
        this.dom.appendChild(tr);   // 把行追加到表格
    }
    document.querySelector('#app').appendChild(this.dom);   //把表格追加到div裡
}

三、創建蛇頭、蛇身

從無到有第二步,蛇頭蛇頭蛇頭、蛇身蛇身蛇身!看到這覺得學到知道是多麼重要的環節。

function Snake() {
    // 蛇的初始化身體
    this.body = [
        { 'row': 3, 'col': 5 },
        { 'row': 3, 'col': 4 },
        { 'row': 3, 'col': 3 },
        { 'row': 3, 'col': 2 }
    ];
}
Snake.prototype.render = function () {
    // 蛇頭的渲染
    game.setColorHead(this.body[0].row, this.body[0].col);
    // 蛇身的渲染
    for (var i = 1; i < this.body.length; i++) {
        game.setColor(this.body[i].row, this.body[i].col, '#649c49')
    }
}

Snake 裡面調用 中介者 Game 原型對象裡面的屬性!

Game.prototype.setColor = function (row, col, color) {
    this.dom.getElementsByTagName('tr')[row].getElementsByTagName('td')[col].style.background = color;
}

四、創建食物

從無到有第三步,食物食物食物! 看到這,我們的基本形態就算是全都有瞭!

function Food(gameSnake) {
    var self = this;
    // 下面的 do-while 循環語句作用是先創建一個 row 和 col 然後判斷這個 row 和 col 是否在蛇的身上
    do {
        // 食物的位置
        this.row = parseInt(Math.random() * gameSnake.row)
        this.col = parseInt(Math.random() * gameSnake.col)
    } while ((function () {
        // 遍歷蛇的 row col 然後和 food 新隨機出來的 row col 進行判斷,是否重合
        for (var i = 0; i < gameSnake.snake.body.length; i++) {
            if (self.row == gameSnake.snake.body[i].row && self.col == gameSnake.snake.body[i].col) {
                return true;
            }
        }
        return false;
    })());
}
Food.prototype.render = function () {
    game.setHTML(this.row, this.col);
}

五、讓蛇動起來

從無到有第四步,動起來動起來動起來!這裡用數組的頭增尾刪簡直就是天作之合!

// 蛇的運動
Snake.prototype.update = function () {
    this.direction = this.willDirection;
    switch (this.direction) {
        case 'R':   //右
            this.body.unshift({ 'row': this.body[0].row, 'col': this.body[0].col + 1 });
            break;
        case 'D':   //下
            this.body.unshift({ 'row': this.body[0].row + 1, 'col': this.body[0].col });
            break;
        case 'L':   //左
            this.body.unshift({ 'row': this.body[0].row, 'col': this.body[0].col - 1 });
            break;
        case 'U':   //上
            this.body.unshift({ 'row': this.body[0].row - 1, 'col': this.body[0].col });
            break;
    }
    // 死亡的判斷,超出瞭表格邊緣的部分
    if (this.body[0].col > game.col - 1 || this.body[0].col < 0 || this.body[0].row > game.row - 1 || this.body[0].row < 0) {
        alert('撞到墻瞭哦,一共吃掉瞭' + game.score + '顆草莓');
        this.body.shift();
        clearInterval(game.timer);
        location.reload();
    }
    // 自己撞到自己的時候會判定死亡
    for (var i = 1; i < this.body.length; i++) {
        // 如果當前蛇的頭部和身體的某一個部位的 row 和 col 完全重合的時候
        if (this.body[0].row == this.body[i].row && this.body[0].col == this.body[i].col) {
            alert('撞到自己瞭,吃掉瞭' + game.score + '顆草莓');
            this.body.shift();
            clearInterval(game.timer);
            location.reload();
        }
    }
    // 蛇吃食物
    // 判斷如果當前的蛇的頭部沒有和食物進行重合,就代表此時沒有吃到食物,此時就進行尾部刪除,如果重合瞭就代表遲到瞭,此時我們不進行刪除尾部
    if (this.body[0].row == game.food.row && this.body[0].col == game.food.col) {
        // 此時情況是隻有頭部增加瞭,尾部沒有刪除
        game.food = new Food(game);    //創建新的食物
        game.score++;
        game.f = 0;
    } else {
        this.body.pop();    //刪除數組最後一個元素
    }
}

六、控制蛇的方向

從無到有第五步,蛇的方向! 做到這裡,回望過去的每一步,都是浮雲!!

// 蛇的方向改變,防止的是在一次渲染之前會出現調頭的情況
Snake.prototype.changeDirection = function (d) {
    this.willDirection = d;
}
// 設置鍵盤的事件監聽
Game.prototype.bindEvent = function () {
    var self = this;
    document.addEventListener('keydown', function (e) {
        // 用ASCII碼值判斷鍵盤方向
        switch (e.keyCode) {
            case 37:    //左
                if (self.snake.direction == 'R') return;    // 先進行判斷,如果當前的方向是向右移動,此時我們不能按左鍵
                self.snake.changeDirection('L');
                self.d = 'L';
                break;
            case 38:    //上
                if (self.snake.direction == 'D') return;    // 先進行判斷,如果當前的方向是向下移動,此時我們不能按上鍵
                self.snake.changeDirection('U');
                self.d = 'U';
                break;
            case 39:    //右
                if (self.snake.direction == 'L') return;    // 先進行判斷,如果當前的方向是向左移動,此時我們不能按右鍵
                self.snake.changeDirection('R');
                self.d = 'R';
                break;
            case 40:    //下
                if (self.snake.direction == 'U') return;    // 先進行判斷,如果當前的方向是向上移動,此時我們不能按下鍵
                self.snake.changeDirection('D');
                self.d = 'D';
                break;
        }
    })
}

最後按照我們喜歡的樣子設置樣式就好啦,這裡我按我自己喜歡粉色設置相應的顏色以及插入我喜換的食物的樣子!!

七、完整代碼

index.html

<!DOCTYPE html>
<html lang="en">
 
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>貪吃蛇</title>
    <style>
        * {
            padding: 0;
            margin: 0;
        }
 
        #app {
            position: relative;
            border: 20px solid #f8bbd0;
            background-color: #fce4ec;
            width: 500px;
            height: 500px;
            margin: 15px auto;
        }
 
        table {
            border-collapse: collapse;
            background-color: #fce4ec;
        }
 
        td {
            position: relative;
            background-size: 100% 100%;
            border-radius: 50%;
            width: 20px;
            height: 20px;
            text-align: center;
            /* background-color: #fce4ec; */
            /* border: 1px solid #aaa; */
        }
 
        td .snake {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
        }
 
        .start,
        .suspend {
            cursor: pointer;
            position: absolute;
            width: 150px;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
        }
 
        .suspend {
            display: none;
            z-index: 2;
        }
    </style>
</head>
 
<body>
    <!-- <h3 id="f">幀編號:0</h3>
    <h3 id="score">分數:0</h3> -->
    <div id="app">
        <img src="images/start.gif" alt="" class="start">
        <img src="images/suspended.png" alt="" class="suspend">
    </div>
    <!-- <script src="js/last.js"></script> -->
    <script src="js/Snake.js"></script>
    <script src="js/Food.js"></script>
    <script src="js/Game.js"></script>
    <script>
        var game = null;
        var flag = true;
        var suspend = document.querySelector('.suspend');
        document.querySelector('.start').addEventListener('click', function () {
            // document.querySelector('#app').style.backgroundColor='white';
            this.style.display = 'none';
            game = new Game();
            document.querySelector('table').addEventListener('click', function () {
                clearInterval(game.timer);
                suspend.style.display = 'block';
 
            })
            suspend.addEventListener('click', function () {
                suspend.style.display = 'none';
                game.timer = setInterval(function () {
                    game.f++;
                    // document.getElementById('f').innerHTML = '幀編號:' + game.f;
                    // document.getElementById('score').innerHTML = '分數:' + game.score;
                    // 清屏
                    game.clear();
                    // 蛇的運動(更新)
                    // 蛇的更新速度,當蛇變長的時候,速度要加快
                    var during = game.snake.body.length < 30 ? 30 - game.snake.body.length : 1;
                    game.f % during == 0 && game.snake.update();
 
                    // game.snake.update();
                    // 渲染蛇
                    game.snake.render();
                    // 渲染食物
                    game.food.render();
                }, 10)
            })
        })
    </script>
</body>
 
</html>

Game.js

function Game() {
    this.row = 25;  // 行數
    this.col = 25;  // 列數
    this.score = 0;   //分數
    this.init();    //初始化節點
    this.snake = new Snake();  //實例化蛇類
    this.food = new Food(this);   //初始化食物
    // this.last = new Last();
    this.start();   //執行定時器任務
    this.bindEvent();   //鍵盤的事件監聽
    this.d = 'R';
}
Game.prototype.init = function () {
    this.dom = document.createElement('table'); // 創建表格
    var tr, td;
    // 遍歷行和列
    for (var i = 0; i < this.row; i++) {
        tr = document.createElement('tr');  // 創建行
        for (var j = 0; j < this.col; j++) {
            td = document.createElement('td');  // 創建列
            tr.appendChild(td);     // 把列追加到行
        }
        this.dom.appendChild(tr);   // 把行追加到表格
    }
    document.querySelector('#app').appendChild(this.dom);   //把表格追加到div裡
}
// 遍歷表格,清除表格上的顏色
Game.prototype.clear = function () {
    for (var i = 0; i < this.row; i++) {
        for (var j = 0; j < this.col; j++) {
            this.dom.getElementsByTagName('tr')[i].getElementsByTagName('td')[j].style.background = '';
            this.dom.getElementsByTagName('tr')[i].getElementsByTagName('td')[j].innerHTML = '';
        }
    }
}
// 設置顏色的方法 讓表格的第幾行,第幾列設置什麼顏色
Game.prototype.setColor = function (row, col, color) {
    this.dom.getElementsByTagName('tr')[row].getElementsByTagName('td')[col].style.background = color;
}
// 設置蛇頭
Game.prototype.setColorHead = function (row, col) {
    var img = document.createElement('img');
    img.src = 'images/snake.png';
    img.className = 'snake';
    this.dom.getElementsByTagName('tr')[row].getElementsByTagName('td')[col].appendChild(img);
    // this.dom.getElementsByTagName('tr')[row].getElementsByTagName('td')[col].style.backgroundColor='transparent'
    switch (this.d) {
        case 'R':   //右
                break;
        case 'D':   //下
                img.style.transform = 'rotate(90deg)';
                break;
        case 'L':   //左
                img.style.transform = 'rotate(180deg)';
                break;
        case 'U':   //上
                img.style.transform = 'rotate(-90deg)';
                break;
    }
}
// 渲染食物
Game.prototype.setHTML = function (row, col) {
    this.dom.getElementsByTagName('tr')[row].getElementsByTagName('td')[col].style.backgroundImage = 'url(./images/food.png)';
}
// 設置鍵盤的事件監聽
Game.prototype.bindEvent = function () {
    var self = this;
    document.addEventListener('keydown', function (e) {
        // 用ASCII碼值判斷鍵盤方向
        switch (e.keyCode) {
            case 37:    //左
                if (self.snake.direction == 'R') return;    // 先進行判斷,如果當前的方向是向右移動,此時我們不能按左鍵
                self.snake.changeDirection('L');
                self.d = 'L';
                break;
            case 38:    //上
                if (self.snake.direction == 'D') return;    // 先進行判斷,如果當前的方向是向下移動,此時我們不能按上鍵
                self.snake.changeDirection('U');
                self.d = 'U';
                break;
            case 39:    //右
                if (self.snake.direction == 'L') return;    // 先進行判斷,如果當前的方向是向左移動,此時我們不能按右鍵
                self.snake.changeDirection('R');
                self.d = 'R';
                break;
            case 40:    //下
                if (self.snake.direction == 'U') return;    // 先進行判斷,如果當前的方向是向上移動,此時我們不能按下鍵
                self.snake.changeDirection('D');
                self.d = 'D';
                break;
        }
    })
}
Game.prototype.start = function () {
    // 幀編號
    this.f = 0;
    // 定時器裡面的核心就是遊戲的渲染本質:清屏-更新-渲染
    this.timer = setInterval(function () {
        game.f++;
        // document.getElementById('f').innerHTML = '幀編號:' + game.f;
        // document.getElementById('score').innerHTML = '分數:' + game.score;
        // 清屏
        game.clear();
        // 蛇的運動(更新)
        // 蛇的更新速度,當蛇變長的時候,速度要加快
        var during = game.snake.body.length < 30 ? 30 - game.snake.body.length : 1;
        game.f % during == 0 && game.snake.update();
        // game.snake.update();
        // 渲染蛇
        game.snake.render();
        // 渲染食物
        game.food.render();
    }, 10)
}

Snake.js

function Snake() {
    // 蛇的初始化身體
    this.body = [
        { 'row': 3, 'col': 5 },
        { 'row': 3, 'col': 4 },
        { 'row': 3, 'col': 3 },
        { 'row': 3, 'col': 2 }
    ];
    this.direction = 'R';   //信號量,設置運動方向
    this.willDirection = 'R';   //即將改變的方向,目的就是為瞭方向出現原地調頭的情況
}
Snake.prototype.render = function () {
    // 蛇頭的渲染
    game.setColorHead(this.body[0].row, this.body[0].col);
    // 蛇身的渲染
    for (var i = 1; i < this.body.length; i++) {
        game.setColor(this.body[i].row, this.body[i].col, '#649c49')
    }
}
// 蛇的運動
Snake.prototype.update = function () {
    this.direction = this.willDirection;
    switch (this.direction) {
        case 'R':   //右
            this.body.unshift({ 'row': this.body[0].row, 'col': this.body[0].col + 1 });
            break;
        case 'D':   //下
            this.body.unshift({ 'row': this.body[0].row + 1, 'col': this.body[0].col });
            break;
        case 'L':   //左
            this.body.unshift({ 'row': this.body[0].row, 'col': this.body[0].col - 1 });
            break;
        case 'U':   //上
            this.body.unshift({ 'row': this.body[0].row - 1, 'col': this.body[0].col });
            break;
    }
    // 死亡的判斷,超出瞭表格邊緣的部分
    if (this.body[0].col > game.col - 1 || this.body[0].col < 0 || this.body[0].row > game.row - 1 || this.body[0].row < 0) {
        alert('撞到墻瞭哦,一共吃掉瞭' + game.score + '顆草莓');
        this.body.shift();
        clearInterval(game.timer);
        location.reload();
    }
    // 自己撞到自己的時候會判定死亡
    for (var i = 1; i < this.body.length; i++) {
        // 如果當前蛇的頭部和身體的某一個部位的 row 和 col 完全重合的時候
        if (this.body[0].row == this.body[i].row && this.body[0].col == this.body[i].col) {
            alert('撞到自己瞭,吃掉瞭' + game.score + '顆草莓');
            this.body.shift();
            clearInterval(game.timer);
            location.reload();
        }
    }
    // 蛇吃食物
    // 判斷如果當前的蛇的頭部沒有和食物進行重合,就代表此時沒有吃到食物,此時就進行尾部刪除,如果重合瞭就代表遲到瞭,此時我們不進行刪除尾部
    if (this.body[0].row == game.food.row && this.body[0].col == game.food.col) {
        // 此時情況是隻有頭部增加瞭,尾部沒有刪除
        game.food = new Food(game);    //創建新的食物
        game.score++;
        game.f = 0;
    } else {
        this.body.pop();    //刪除數組最後一個元素
    }
}
// 蛇的方向改變,防止的是在一次渲染之前會出現調頭的情況
Snake.prototype.changeDirection = function (d) {
    this.willDirection = d;
}

Food.js

function Food(gameSnake) {
    var self = this;
    // 下面的 do-while 循環語句作用是先創建一個 row 和 col 然後判斷這個 row 和 col 是否在蛇的身上
    do {
        // 食物的位置
        this.row = parseInt(Math.random() * gameSnake.row)
        this.col = parseInt(Math.random() * gameSnake.col)
    } while ((function () {
        // 遍歷蛇的 row col 然後和 food 新隨機出來的 row col 進行判斷,是否重合
        for (var i = 0; i < gameSnake.snake.body.length; i++) {
            if (self.row == gameSnake.snake.body[i].row && self.col == gameSnake.snake.body[i].col) {
                return true;
            }
        }
        return false;
    })());
}
Food.prototype.render = function () {
    game.setHTML(this.row, this.col);
}

八、圖片

接下裡我把我用的圖片放到這個地方,喜歡的寶寶們也可以直接拿去用!

九、總結

看到最後的朋友們有沒有覺得其實還是很簡單的!感興趣的趕緊去試試叭!!

到此這篇關於JavaScript 精美貪吃蛇實現流程的文章就介紹到這瞭,更多相關JavaScript 貪吃蛇內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: