js实现贪吃蛇

实现方法_1

1实现效果

在这里插入图片描述

2 实现步骤

html部分忽略,布局写的太辣眼了

2.1 移动场地

用的表格td,利用双层for循环加上字符串的拼接,最终利用innnerHTML将布局显示

for (let i = 0; i < height; i++) {
   
            str += '<tr>';
            for (let j = 0; j < width; j++) {
   
                if (i == 0) {
   
                    str += '<td>' + j + '</td>';
                } else if (j == 0) {
   
                    str += '<td>' + i + '</td>';
                } else {
   
                    str += '<td></td>';
                }
            }
            str += '</tr>';
        }
        document.querySelector('table').innerHTML = str;

2.2 游戏难度

更改定时器的时间(简单)

let difficulty = document.querySelector("#difficulty");
        difficulty.onchange = function () {
   
            if (!isPlay) {
   
                time = difficulty.value;
                clearInterval(timeId);
                difficulty.preventDefault();
            }

        }

2.3 造蛇和食物

利用一维数组存放蛇,但是因为需要确定蛇头和蛇身的位置需要横纵坐标,所以存放的是键值对形式的数据 {key_x: x_value , key_y: y_value}; ,如何显示蛇呢,道理和js实现动漫拼图1.0版中initEvent函数哪里说的的原理一样。snake一维数组记录的蛇身位置,移动场地是有n个tds组成的,通过snake记录的横纵坐标计算出对应的tds[index]中的index值(index=行×width+列),然后将这个td更换背景,来显示蛇。同理显示食物也是如此。

// 初始化蛇
        function initSnake() {
   
            let snake = [];
            snake[0] = getRandom();
            let x = snake[0].x;
            let y = snake[0].y;
            snake[1] = {
   
                x: x - 1,
                y: y
            };
            snake[2] = {
   
                x: x - 2,
                y: y
            };
            return snake;
        }
 //显示画面
        function show(snake, food) {
   
            // 1 清除所有蛇和食物
            tds.forEach(function (item) {
   
                item.style.backgroundColor = '';
            });
            // 2 显示蛇
            snake.forEach(function (item, id) {
   
                let i = parseInt(item.x);
                let j = parseInt(item.y);
                console.log(i, j);
                let tdId = j * width + i;
                console.log(tdId);
                if (id == 0) {
   
                    tds[tdId].style.backgroundColor = 'blue';
                } else {
   
                    tds[tdId].style.backgroundColor = 'green';
                }
            });
            // 3 显示食物 
            let tdFoodId = food.y * width + food.x;
            tds[tdFoodId].style.backgroundColor = 'red';
        }

2.4 蛇的移动

监听键盘的按键按下事件

通过按对应的wasd上左下右,来更改d的值,表示蛇头的移动方向,调用自己写的move函数,实现对应的行向或列向的加加减减实现上下移动,在配合上定时器,便实现了蛇的移动,但这是仅限于单个块的移动(蛇头),那其他块(蛇身)怎么正确随蛇身移动呢?(需要注意越界问题)

这里方法就很巧妙了,我们存放蛇用的是一维数组,而且一维数组有两个方法
pop:移除数组的尾部元素,并返回该值。
unshift:在数组头部添加新的元素。
思路:这里我们先不去考虑越界的情况,假设蛇头的下一个位置都是合法的。
首先,通过我们的按键事件,获得我们预使蛇头向那个方向移动,然后计算出新的蛇头,利用unshift将新蛇头加入snake数组,在利用pop移除最后面的一个(前面加一个,右面移除一个,这样就实现了蛇的移动,我们可以很容易想到整体一条直线的时候确实可以,那要是拐弯呢?也是可以的)

在这里插入图片描述
蛇在吃食物时,食物的位置肯定和新蛇头是重合,吃过食物之后蛇的长度应该加一,此时呢就只需要调用unshift函数,加入新蛇头就行了,不用移除后一个,在随机一个食物位置即可。
在这里插入图片描述

 // 4.1 蛇的移动方向
        document.addEventListener("keydown", direction);

        function direction(event) {
   
            if (event.keyCode == 65 && d != "RIGHT") {
   
                d = "LEFT";
            } else if (event.keyCode == 87 && d != "DOWN") {
   
                d = "UP";
            } else if (event.keyCode == 68 && d != "LEFT") {
   
                d = "RIGHT";
            } else if (event.keyCode == 83 && d != "UP") {
   
                d = "DOWN";
            } else if (event.keyCode == 32) {
   
                // 暂停
                suspendGame();
            } else if (event.keyCode == 82) {
   
                // 重新开始
                restartGame();
            } else if (event.keyCode == 66 && !isPlay) {
   
                // 开始
                startGame();
            }
        }
        // 4.2 蛇的移动
        function move(snake) {
   
            let snakeX = snake[0].x;
            let snakeY = snake[0].y;
            let isEat = false;
            if (d == "LEFT") snakeX -= speed;
            if (d == "UP") snakeY -= speed;
            if (d == "RIGHT") snakeX += speed;
            if (d == "DOWN") snakeY += speed;
            if (snakeX == food.x && snakeY == food.y) {
   
                isEat = true;
                let tdId = food.y * width + food.x;
                tds[tdId].style.backgroundColor = 'lightgray';
                score += 5;
                scoreSpan.innerHTML = score;
                food = getFood(snake);
            }
            let newHead = {
   
                x: snakeX,
                y: snakeY
            };
            // 检测是否越界,或者自己碰到自己
            if (snakeX < 0 || snakeX >= width || snakeY < 0 || snakeY >= height || collision(newHead, snake)) {
   
                endGame();
            } else {
   
                // 没有吃到,移除最后一个,在头部加一个,蛇长不变
                // 吃到,if不成立,蛇长不减,蛇头加一,整体变长一
                if (!isEat) {
   
                    snake.pop();
                }
                snake.unshift(newHead);
                console.log("newHead=" + newHead.x + "," + newHead.y);
            } 

越界问题
就是构造蛇头的时候,当前的蛇头如果已经处在上下左右某个边界了,肯定是不能在朝这个蛇头方向在移动了(这时候构造出来的新的蛇头的横纵坐标,肯定是不合法的,这里用的一维数组感觉不明显,下面的实现方法2,利用二维数组存储蛇时,这种情况,直接会索引越界报错的),而且根据游戏规则,出现越界问题时,也就是游戏结束之时(所谓的撞墙了)。所以呢,我们在产生新的蛇头坐标时,先去检查是否合法,确保不越界了,再去unshift加入数组首部,不然直接结束游戏;还有一种结束游戏,就是新的蛇头的位置,是在蛇的身体某个位置,就是自己碰自己,也直接结束游戏。

2.5 产生食物的随机位置

食物的随机位置不能在蛇身上

 // 产生随机数字(游戏开始时蛇的随机位置和食物的随机位置)
        function getRandom() {
   
            // 蛇头的产生范围限定在(2-44)
            let ran_x = parseInt(Math.random() * (width - 4)) + 2;
            let ran_y = parseInt(Math.random() * height);
            return {
   
                x: ran_x,
                y: ran_y
            };
        }
        // 检查食物随机产生的位置是否在蛇的身体上
        function getFood(snake) {
   
            while (true) {
   
                let food = getRandom();
                let flag = true;
                for (let i = 0; i < snake.length; i++) {
   
                    let item = snake[i];
                    if (item.x == food.x && item.y == food.y) {
   
                        flag = false;
                        break;
                    }
                }
                if (flag) {
   
                    return food;
                }
            }
        }

其他就是一些繁琐的获取元素,添加事件,测试逻辑,该bug了

3 全部代码

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>贪吃蛇</title>
    <style>
        table {
     
            border-collapse: collapse;
        }

        .body {
     
            display: flex;
            height: 600px;
            background-color: lightblue;
        }

        td {
     
            border: 1px solid black;
            width: 15px;
            height: 15px;
            margin-left: 4px;
            font-size: 10px;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值