JavaScript制作贪吃蛇小游戏

目录

效果展示

原理分析

 Game.js文档

Snake.js文档

Food.js文档

附上源代码


写了这么久的代码

是否你和我一样感到枯燥乏味了呢?

是否没有前进的动力了呢?

别忘了当时的你踌躇满志将前端Web一举拿下的斗志啊!

今天博主要给大家展现一个好玩的游戏

贪吃蛇小游戏!

嘿,你可别小瞧这东西!

制作过程是从无到有

等做完它

你就能从中体会到比玩游戏还快乐的居然是打代码!


效果展示

原理分析

我们制作前为了代码更清晰就分成几个js文档来编写。

其中Game.js可以看作是一个媒介的作用;Snake.js是当蛇初始化时在最左边向右走的状态;

 贪吃蛇在游戏中的运动可以想象成这条蛇是在一张25*25的表格中运动:

 如图所示

 Game.js文档

📝所以我们在Game.js中添加表格节点

function Game() {
    this.row = 25;
    this.col = 25;

}

Game.prototype.init = function() {
    this.dom = document.createElement('table');
    // 创建表格--父元素为document(页面中创建表格)
    // var tr, td;
    for (var i = 0; i < this.row; i++) { //追加行
        var tr = document.createElement('tr');
        for (var j = 0; j < this.td; j++) { //追加列
            var td = document.createElement('col');
            td.appendChild(tr);
        }
    }
};

Snake.js文档

📝Snake.js中当蛇初始化时最左边向右走的状态

如图所示

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.'pink');
    // 蛇身的渲染
    for (var i = 1; i < this.body.length; i++) {
        game.setColor(this.body[i].row, this.body[i].col, 'cyan')
    }
}

蛇在运动的时候它的原理是“头增尾删”。

因为蛇的长度先是不变的,而我们改变的也是改变这个四个格子的颜色,走一格头部那一格颜色变为粉色,尾巴那一格的颜色变为白色。

✨接下来让蛇通过我们按键来进行运动:放在一个监听事件内

// 设置键盘的事件监听
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;
        }
    })
}

✨接下来我们判定蛇是否撞到墙而结束

 // 死亡的判断,超出了表格边缘的部分
        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();
            }
        }

Food.js文档

📝食物food类,用来产生食物

function Food(gameSnake) {
        // 食物的位置
        this.row = parseInt(Math.random() * gameSnake.row)
        this.col = parseInt(Math.random() * gameSnake.col)
}
Food.prototype.render = function() {
    game.setHTML(this.row, this.col);
}

✨食物随机生成在单元格中,利用do...while来实现

function Food(gameSnake) {
    var self = this;
    // 下面的 do-while 循环语句作用是先创建一个 row 和 col
       然后判断这个 row 和 col 是否在蛇的身上
    //do...while来创建食物
    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;
        })());
}

附上源代码

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="img/start.gif" alt="" class="start">
        <img src="img/stop.png" alt="" class="suspend">
    </div>
    <!-- <script src="js/last.js"></script> -->
    <script src="Snake.js"></script>
    <script src="Food.js"></script>
    <script src="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(); //实例化蛇类--绑定到Game()中 原型链来找
    this.food = new Food(this); //初始化食物
    // this.last = new Last();
    this.start(); //执行定时器任务
    this.bindEvent(); //键盘的事件监听
    this.d = 'R';
}
//创建对象Game 里面写上它的属性(特征)↑

Game.prototype.init = function() {
        this.dom = document.createElement('table');
        // 创建表格--父元素为document(页面中创建表格)
        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 = 'img/head.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(./img/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...while来创建食物
    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);
}

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>