200多行网页版贪吃蛇,请指教

92 阅读2分钟

主要使用html表格渲染,通过改变单元格class实现,目前还缺很多功能,如速度控制,网格大小控制等

<html>
<head>
    <meta charset="utf-8">
</head>
<style>
    .snake table {
        margin: 20px 0;
        color: #2bbc8a
    }
    .snake tbody {
        width: 100%
    }
    .snake td {
        border: 1px solid #0e6e4d;
        background: white;
        padding: 10px
    }
    .snake td.active.head {
        background: #a94292
    }
    .snake td.active {
        background: #2bbc8a
    }

    .snake td.food {
        background: rebeccapurple
    }
</style>

<div class="snake">
    <table>
        <caption>按空格键开始或暂停</caption>
        <tbody>
        </tbody>
    </table>
</div>
<script>
    let rows = 20 // 行数
    let cols = 30 // 列数
    let snakeList = [] // 蛇身坐标集
    let food = null


    let keyPress = false
    let pause = false

    function init() {
        snakeList = [{x: 2, y: 15}, {x: 1, y: 15}, {x: 0, y: 15}] // 初始蛇身坐标
        pause = true
        keyPress = false
        render()
    }

    // 创建网格
    function createTable() { 
        let temp = document.createDocumentFragment()
        for (let i = 0; i < rows; i++) {
            let tr = document.createElement('tr')
            for (let j = 0; j < cols; j++) {
                let td = document.createElement('td')
                td.setAttribute('row', i)
                td.setAttribute('col', j)
                td.setAttribute("data", 'x' + j + 'y' + i)
                if (i == 0 && j == 0) {
                    td.setAttribute('class', 'active')
                }
                tr.appendChild(td)
            }
            temp.appendChild(tr)
        }

        let tbody = document.querySelector('.snake  tbody')
        tbody.appendChild(temp)
    }

    // 创建食物
    function tryCreateFood() { 
        if (food == null) {
            food = {
                x: Math.floor(cols * Math.random()),
                y: Math.floor(rows * Math.random())
            }

            let td = getTd(food.x, food.y)
            td.setAttribute('class', 'food')
        }
    }

    // 自动前进的函数,定时调用
    function autoMove() {
        let head = snakeList[0]
        let second = snakeList[1]

        let next = {
            x: head.x,
            y: head.y
        }

        // 通过头两个块判断方向
        let dx = head.x - second.x
        let dy = head.y - second.y


        // x 方向
        if (dx != 0) {
            next.x += dx
        }
		// y 方向
        if (dy != 0) {
            next.y += dy
        }

        // 身体的每一块都前进, 相当于尾巴少一块,脑袋多一块
        snakeList.unshift(next) // insert to first
        snakeList.pop() // remove the last

    }

    function getTd(x, y) {
        let selectors = 'td[data=x' + x + 'y' + y + ']'
        return document.querySelector(selectors)
    }

    function render() {
        let tdList = document.querySelectorAll('td')

        // 清空样式
        for (let i = 0; i < tdList.length; i++) {
            let td = tdList[i]
            td.removeAttribute('class')
        }

        // 设置样式
        for (let i = 0; i < snakeList.length; i++) {
            let body = snakeList[i] 
            let td = getTd(body.x, body.y)
            if (td != null) {
                td.setAttribute('class', 'active')
                if(i == 0){
                    td.setAttribute('class', 'active head')
                }
            }
        }

        if (food != null) {
            let foodTD = getTd(food.x, food.y)
            foodTD.setAttribute('class', 'food')
        }

    }

    function check() {
        let head = snakeList[0]

        // 判断撞墙
        if (head.x < 0 || head.y > cols || head.y < 0 || head.y > rows) {
            alert('撞墙了')
            init()
            return false
        }

        // 判断吃到食物
        if (food != null && food.x == head.x && food.y == head.y) {
            let last = snakeList[snakeList.length - 1]
            if (last) {
                snakeList.push({
                    x: last.x,
                    y: last.y
                })
            }
            food = null

        }

        return true
    }

    function frame() {
        if (pause) {
            return
        }

        tryCreateFood()
        if (keyPress == false) {
            autoMove()
            if (!check()) {
                return
            }
        }
        render()

        keyPress = false
    }


    // 开始执行
    createTable()
    init()

    // 监听按键
    document.addEventListener('keydown', function (e) {
        if (e.code == 'Space') {
            // 开始或暂停
            pause = !pause

            e.preventDefault()
            return
        }

	// 防抖
        if (keyPress) {    
            return
        }
		
	e.preventDefault()

        // 步进数, 根据按键增减
        let dx = 0, dy = 0
        switch (e.code) {
            case 'ArrowUp':
                dy--
                break
            case 'ArrowDown':
                dy++
                break
            case 'ArrowLeft':
                dx--
                break
            case 'ArrowRight':
                dx++
                break
            default:
                return
        }

        let head = snakeList[0]
        let second = snakeList[1]

        // 计算下一格
        let next = {
            x: head.x + dx,
            y: head.y + dy
        }
        snakeList.unshift(next) // insert to first
        snakeList.pop() // remove the last

        check()

        keyPress = true
    })

    setInterval(frame, 400)
</script>
</html>