【学习笔记64】原生JS实现贪吃蛇

一、案例效果

二、HTML和CSS代码

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

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>

    <style>
        * {
            margin: 0;
            padding: 0;
        }

        .map {
            width: 1000px;
            height: 600px;
            border: 5px solid #333;
            margin: 50px auto;
            position: relative;

            background-image: url(./imgs/bg.png);
            background-repeat: repeat;
            background-size: 20px 20px;
        }

        .map>div {
            width: 20px;
            height: 20px;
            position: absolute;
            background-size: 20px 20px;
            background-repeat: no-repeat;
        }

        .map>.food {
            background-image: url(./imgs/food.png);
        }

        .map>.head {
            background-image: url(./imgs/head.png);
        }

        .map>.body {
            background-image: url(./imgs/body.png);
        }
    </style>

</head>

<body>


    <button id="btn1">开始</button>
    <button id="btn2">暂停</button>
    <button id="btn3">重新开始</button>

    <div class="map" id="map"></div>

    <script type="module">
        import Game from './game.js'
        const g = new Game('#map')

        document.querySelector('#btn1').onclick = function () {
            g.start()
        }
        document.querySelector('#btn2').onclick = function () {
            g.pause()
        }
        document.querySelector('#btn3').onclick = function () {
            g.reload()
        }

        // 当按下方向按键的时候, 修改蛇的方向
        document.onkeydown = function (e) {
            if (e.keyCode == 37) {
                // left
                g.setDes('left')
            } else if (e.keyCode == 38) {
                // top
                g.setDes('top')
            } else if (e.keyCode == 39) {
                // right
                g.setDes('right')
            } else if (e.keyCode == 40) {
                // bottom
                g.setDes('bottom')
            }
        }

        // .....
    </script>


    <script>
        /**
         *  贪吃蛇分析
         * 
         *      网页游戏: 一定要分工明确
         * 
         *      1. 食物 类
         *          * 负责生成一个 食物
         *          * 随机出现在地图上的某一个位置
         *          * 根据游戏规则 通知我 然后去换位置
         *      2. 蛇 类
         *          * 要负责生成一个蛇
         *          * 负责 走一步, 由游戏规则通知我多长时间走一步
         *          * 负责判断 蛇头是否和食物重叠
         *          * 负责判断 是否超界
         *      3. 游戏规则 类
         *          * 通知 食物类 在地图上创建一个 食物
         *          * 通知 蛇类 在地图上创建一个 蛇
         *          * 开始游戏
         *              * 每间隔固定的时间, 通知蛇走一步
         *          * 暂停游戏
         *              * 停止通知蛇走一步
         *          * 重新开始
         *              * 重新走一遍(初始化)
         *          * 和页面联动
         *              * 监听到键盘按键, 然后通知蛇修改方向
        */
    </script>

</body>

</html>

三、JS代码

1、food.js


export default class Food {
    constructor(ele) {
        // 1. 地图
        this.map = document.querySelector(ele);
        // 2.1 生成一个 DIV
        this.oDiv = document.createElement("div");
        // 2.2 给生成的 DIV 添加类名
        this.oDiv.classList.add("food");
        // 2.3 将当前div 插入到 地图中
        this.map.appendChild(this.oDiv);
        // 3. 记录 食物 x 和 y 的坐标
        this.x = 0;
        this.y = 0;

        // 调用随机改变食物的位置
        this.changeFood();
    }
    // 原型
    changeFood() {
        // 0. 拿到宽度和高度
        const w = parseInt(window.getComputedStyle(this.map).width);
        const h = parseInt(window.getComputedStyle(this.map).height);
        // 1. 计算每行 可以有 多少个 小格子
        const row = w / 20;
        //  2. 计算每列 可以有 多少个 小格子
        const clo = h / 20;
        /**
         * 3. 求 随机在第几个 格子上(在行上)
         *      Math.floor(Math.random() * (row - 1)) + 1 不含最大值,含最小值
         */
        // const posX = (Math.floor(Math.random() * (row - 1)) + 1) * 20;
        // const posX = (Math.floor(Math.random() * (row - 0)) + 0) * 20;
        const posX = (Math.floor(Math.random() * row)) * 20;
        // 4. 求 在某一列的随机坐标
        // const posY = (Math.floor(Math.random() * (clo - 1)) + 1) * 20;
        // const posY = (Math.floor(Math.random() * (clo - 0)) + 0) * 20;
        const posY = (Math.floor(Math.random() * clo)) * 20;

        // 5. 求到随即坐标后, 修改 this.x 和 this.y
        this.x = posX;
        this.y = posY;

        // 6. 修改食物的坐标
        this.oDiv.style.left = this.x + "px";
        this.oDiv.style.top = this.y + "px";
    }
}

2、snake.js


export default class Snake {
    constructor(ele) {
        // 1. 地图
        this.map = document.querySelector(ele);
        // 2. 方向
        this.des = "bottom";
        // 3. 存储一条蛇
        this.snake = [];

        // 4. 创建一条蛇
        this.makeSnake();
    }

    // 1. 创建一节蛇
    addSnake() {
        /**
         *  1. 当前 this.snake 为空数组, 此时只需要创建一节蛇头即可
         *  2. 当前 this.snake 数组已经有值了, 除了要创建一节蛇头意外, 还要把原本的蛇头更改蛇身
         */
        // 1. 拿到数组的第一项(约定此项为蛇头)
        const head = this.snake[0];
        // 2. 如果 数组内有数据, 将原本的蛇头更改为 蛇身
        if (head !== undefined) {
            head.className = "body";
        }
        // 3. 创建 蛇头
        const oHaed = document.createElement("div");
        oHaed.className = "head";
        this.map.appendChild(oHaed); // 添加到 DOM 节点
        this.snake.unshift(oHaed); // 把新建的蛇头 添加到 数组开头(约定了 蛇头添加到 数组开头)
        // 4. 调整蛇头的定位
        const pos = {
            x: 0,
            y: 0,
        };
        if (head !== undefined) {
            /**
             *  如果当前方向是 向 右, left += 20    top 不变
             *                  左, left -= 20  top 不变
             *                  下, left 不变   top += 20
             *                  上, left 不变   top -= 20
             */
            // 获取到原本 蛇头的 偏移量
            pos.x = head.offsetLeft;
            pos.y = head.offsetTop;
            switch (this.des) {
                case "right":  pos.x += 20; break;
                case "left":  pos.x -= 20; break;
                case "bottom": pos.y += 20; break;
                case "top": pos.y -= 20; break;
            }
        }
        oHaed.style.left = pos.x + "px";
        oHaed.style.top = pos.y + "px";
    }
    /**
     *  需求: 一条蛇 默认 5节
     *
     *      分析: 书写一个 创建一节蛇的函数, 然后调用5次
     *
     *  有两个函数
     *      1---创建一节蛇
     *          只需要创建一个节点
     *      2---创建一条蛇
     *          只需要调用 创建一节蛇的函数 5次
     */
    // 2. 创建一条蛇
    makeSnake() {
        for (let i = 0; i < 5; i++) {
            this.addSnake();
        }
    }
    // 3. 走一步
    move () {
        // 3.1 删除数组末尾元素
        const body = this.snake.pop()
        // 3.2 删除对应 DOM 节点
        body.remove()
        // 3.3 新增一个蛇头
        this.addSnake()
    }
    // 4. 判断蛇头是否和食物重叠
    isEat(foodX, foodY) {
        const head = this.snake[0]  // 因为约定了 蛇头放在 [0] 的位置上
        if (head.offsetTop == foodY && head.offsetLeft == foodX) {
            return true
        }
        return false
    }
    // 5. 判断蛇头 是否超界
    isDie () {
        // 1. 拿到蛇头
        const head = this.snake[0]

        if (
            head.offsetTop < 0 ||
            head.offsetLeft < 0 ||
            head.offsetTop >= this.map.clientHeight ||
            head.offsetLeft >= this.map.clientWidth
        ) {
            return true
        }
        return false
    }
}

3、snake.js 

import Food from "./food.js";
import Snake from "./snake.js";

export default class Game {
    constructor(ele) {
        this.food = new Food(ele);
        this.snake = new Snake(ele);

        this.timer = 0; // 记录定时器ID, 用于结束定时器

        this.sum = 0;
        this.level = 1;
    }
    // 1. 开始游戏
    start() {
        let newTime = 400 - this.level * 100;

        this.timer = setInterval(() => {
            this.snake.move();

            this.up();
            // 判断蛇头是否和食物重叠
            if (this.snake.isEat(this.food.x, this.food.y)) {
                this.food.changeFood();
                this.snake.addSnake();
                this.sum++;
            }
            // 判断蛇头是否超界
            if (this.snake.isDie()) {
                clearInterval(this.timer);
                alert("GG");
            }
        }, newTime);
    }

    // 2. 暂停游戏
    pause() {
        clearInterval(this.timer);
    }

    // 3. 重新开始
    reload() {
        window.location.reload();
    }

    // 4. 修改蛇方向
    setDes(type) {
        this.snake.des = type;
    }

    // 5.
    up() {
        if (this.sum == 2) {
            this.level++;
            this.sum = 0;
            this.start();
        }
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值