JS练习项目1——Javascript实现简易贪吃蛇(附源码)

文章目录

  • 贪吃蛇项目
    • 1.项目预览
    • 2.起步(涉及知识点)
    • 3.源码

贪吃蛇项目

1.项目预览

JS练习项目1——Javascript实现简易贪吃蛇(附源码)_第1张图片
JS练习项目1——Javascript实现简易贪吃蛇(附源码)_第2张图片

  • 功能实现:
    • 可以通过键盘的 上下左右 控制 蛇 的移动
    • 游戏刚开始随机生成食物,蛇 默认往右走
    • 在没吃到食物前,食物静止不动;吃到食物后,食物随机生成
    • 蛇 每吃到一个食物身体(蛇节) 增加一节
    • 当蛇头碰到地图(方格)的边界时,游戏结束(蛇无法再控制,静止不动)

2.起步(涉及知识点)

canvas : 相当于画布;行内元素,让方格在网页水平居中对齐先转块级( display: block); 宽高设置用行内样式。

  • canvas 参数:
    • getContext(‘2d’) 获取绘制工具箱;创建 Context 对象
    • fillStyle = ‘blue` or fillStyle = ‘#ccc’ 填充颜色
    • fillRect 绘制矩形 fillRect(x,y,width,height)
    • strokeStyle 绘制画笔的颜色
    • stroke() 开始绘制
    • clearRect() 擦除画布上的内容
    • moveTo 定义线条开始时的坐标
    • lineTo 定义线条结束时的坐标

setInterval: 定时器,蛇 移动的原理就是,利用每一张不同位置的蛇的位置图片,一直覆盖掉(并擦除掉上一次的图)上一张的图,通过定时器 1000/3 (1s执行3次这样的操作),让视觉效果看起来蛇平滑的移动。

addEventListener: 事件监听(keydown)键盘按下监听,利用 e.keyCode 获取 上下左右 对应的键值(上: 38 下: 40 左:37 右:39)

pop(): pop() 删除并返回数组的最后一个元素

unshift(): 向数组的开头添加一个或多个元素,并返回新的长度


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>
        canvas {
     
            /* 在这里要居中的时候,要先转成块级元素 */
            display: block;
            margin: 0 auto;
            background-color: #33cc99;
        }
    </style>
</head>

<body>
    <!-- 准备画布 -->
    <canvas width="500" height="500" id="huabu">
    
</canvas>
</body>

<script>
    // 通过 js 代码来绘制网格
    // 获取画布对象
    let huabu = document.getElementById('huabu');
    // 获取绘制工具箱
    let tools = huabu.getContext('2d');
    // 从画图工具箱取出要使用的工具;
    // 随机生成x 和 y轴的坐标
    let x = Math.floor(Math.random() * 20) * 25;
    let y = Math.floor(Math.random() * 20) * 25;
    let isEated = false;

    // 每次移动的距离为一格(默认就是在这个位置出发)
    let snake = [{
     
        x: 3,
        y: 0
    }, {
     
        x: 2,
        y: 0
    }, {
     
        x: 1,
        y: 0
    }]

    let directionX = 1;
    let directionY = 0;

    // 判断游戏是否结束
    let isGameOver = false;

    document.addEventListener('keydown', (e) => {
     
        let k = event.keyCode;
        // 上: 38 下: 40 左:37 右:39
        if (k === 38) {
     
            directionX = 0
            directionY = -1
        } else if (k === 40) {
     
            directionX = 0
            directionY = 1
        } else if (k === 37) {
     
            directionX = -1
            directionY = 0
        } else if (k === 39) {
     
            directionX = 1
            directionY = 0
        }
    })

    // 利用定时器,让贪吃蛇动起来
    setInterval(() => {
     
        if (isGameOver) {
     
            return
        }
        // 只需要将擦除 -> 重绘 的代码放里面即可(记得每次都先擦除画布再重绘一次)
        tools.clearRect(0, 0, 500, 500);


        // 注意:食物有默认的行为,刚开始是不动 的,被蛇吃后才会动

        // -------------绘制食物开始------------
        // 这段代码的意思是: 只有食物被吃掉了,才会生成新的食物
        if (isEated) {
     
            // 在这里是对 x 和 y 进行重新复制,不用加 var 声明
            x = Math.floor(Math.random() * 20) * 25;
            y = Math.floor(Math.random() * 20) * 25;
        }

        // 设置矩形的颜色
        tools.fillStyle = '#fff000';
        // 画一个矩形
        // fillRect(x,y,width,height)
        // 不能随机生成 x,y 
        // 随机生成一个位置 Math.random() 生成一个随机数 * 25就得到一个范围
        // 问题: 如果根据 Math.random() 得到一个 [0,19] 并且 0 和 19 都能取到的值?
        // 解决方案: Math.random() * 20 => [0,1) * 20 => [0,20)

        // Math.floor()  向下取整
        tools.fillRect(x, y, 25, 25);
        // -------------绘制食物结束------------

        // ----------绘制贪吃蛇开始---------
        // 说明: 贪吃蛇随着 擦除 重绘,会动起来

        // 蛇已经动起来,默认向右: x + 1,y 不变
        // 水平向左: x -1y 不变
        // 垂直向下:y +1 x 不变
        // 垂直向上: y -1  x 不变

        // 新添加的节,是上一次的蛇头位置再加1,就得到了
        let oldHead = snake[0];
        let newHead = {
     
            x: oldHead.x + directionX,
            y: oldHead.y + directionY
        }

        // 在这里做一个边界的判定
        console.log(newHead.x * 30);

        // 1.如果蛇头的 y 坐标比0小,说明游戏结束
        if (newHead.y < 0 || newHead.x < 0 || newHead.x * 30 >= 600 || newHead.y * 30 >= 600) {
     
            // 游戏就结束了
            isGameOver = true;
        } else {
     
            snake.unshift(newHead); //向数组的开头添加一个或多个元素,并返回新的长度
            // 蛇吃食物分析:
            // 当蛇头的坐标 和 食物的坐标重合的时候,就表示食物被吃掉了
            // 此时应该做两件事
            // 1. 让 isEated 变成 true,表示食物被吃掉
            // 2. 让 蛇 的身体增加一节
            if (snake[0].x * 25 === x && snake[0].y * 25 === y) {
     
                // 当蛇头的 x 和 Y 坐标都与 食物的 x和y坐标 重合,才证明食物被吃了
                isEated = true;
                // 如果蛇吃到了食物,就不执行 pop() 删除最后一节
            } else {
     
                // 在这里重新把 isEated 设置为 false
                isEated = false; // 重新设置食物没被吃掉
                // 表示蛇没吃到食物
                snake.pop(); //
            }
        }
        // ----------绘制贪吃蛇结束---------

        // 默认移动方向为水平向右
        // 绘制蛇头的颜色,每一届都是一个矩形
        for (var i = 0; i < snake.length; i++) {
     
            if (i === 0) {
     
                // 只有蛇头是紫色的,0 的时候
                tools.fillStyle = 'purple'
            } else {
     
                tools.fillStyle = 'blue'
            }
            tools.fillRect(snake[i].x * 25, snake[i].y * 25, 25, 25);
        }

        // -----------绘制网格开始----------
        // 找位置
        // 起点坐标
        // 规律: (0,25 * N + 0.5)(500, 25 * N + 0.5)

        for (let i = 1; i < 20; i++) {
     
            tools.moveTo(0, 25 * i + 0.5);
            // 终点坐标
            tools.lineTo(500, 25 * i + 0.5);

            tools.moveTo(25 * i + 0.5, 0);
            // 终点坐标
            tools.lineTo(25 * i + 0.5, 500);
        }

        // 设置绘制的画笔颜色
        tools.strokeStyle = 'white';
        // 绘制
        tools.stroke();
        // -----------绘制网格结束----------
    }, 1000 / 3); //1000/3 为 1秒执行 3 次操作
</script>

</html>

4.同源码(无注释)

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

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>贪吃蛇绘制二次代码编辑</title>
    <style>
        canvas {
     
            display: block;
            margin: 0 auto;
            background-color: #33cc99;
        }
    </style>
</head>

<body>
    <canvas width="500" height="500" id="huabu"></canvas>
</body>
<script>
    let huabu = document.getElementById('huabu');
    let tol = huabu.getContext('2d');

    let x = Math.floor(Math.random() * 20) * 25;
    let y = Math.floor(Math.random() * 20) * 25;

    let isEated = false;

    let snake = [{
     
        x: 3,
        y: 0
    }, {
     
        x: 2,
        y: 0
    }, {
     
        x: 1,
        y: 0
    }];

    let directionX = 1;
    let directionY = 0;

    let isGameOver = false;

    document.addEventListener('keydown', (e) => {
     
        let k = e.keyCode;
        if (k === 38) {
     
            directionX = 0;
            directionY = -1
        } else if (k === 40) {
     
            directionX = 0;
            directionY = 1
        } else if (k === 37) {
     
            directionX = -1;
            directionY = 0
        } else if (k === 39) {
     
            directionX = 1;
            directionY = 0
        }
    });

    setInterval(() => {
     
        if (isGameOver) {
     
            return
        }

        tol.clearRect(0, 0, 500, 500);

        if (isEated) {
     
            x = Math.floor(Math.random() * 20) * 25;
            y = Math.floor(Math.random() * 20) * 25;
        }

        tol.fillStyle = '#fff000';
        tol.fillRect(x, y, 25, 25);

        let oldHead = snake[0];
        let newHead = {
     
            x: oldHead.x + directionX,
            y: oldHead.y + directionY
        }

        if (newHead.y < 0 || newHead.x < 0 || newHead.x * 30 >= 600 || newHead.y * 30 >= 600) {
     
            isGameOver = true;
        } else {
     
            snake.unshift(newHead);
            if (snake[0].x * 25 === x && snake[0].y * 25 === y) {
     
                isEated = true;
            } else {
     
                isEated = false;
                snake.pop();
            }
        }

        for (var i = 0; i < snake.length; i++) {
     
            if (i === 0) {
     
                tol.fillStyle = 'purple';
            } else {
     
                tol.fillStyle = 'blue';
            }
            tol.fillRect(snake[i].x * 25, snake[i].y * 25, 25, 25);
        }

        for (let i = 1; i < 20; i++) {
     
            tol.moveTo(0, 25 * i + 0.5);
            tol.lineTo(500, 25 * i + 0.5);
            tol.moveTo(25 * i + 0.5, 0);
            tol.lineTo(25 * i + 0.5, 500);
        }

        tol.strokeStyle = 'white';
        tol.stroke();
    }, 300)
</script>

</html>

后续补全其他功能更,琢磨琢磨

你可能感兴趣的:(JavaScript学习,javascript)