vue3+TS贪吃蛇

自己写的贪吃蛇记录一下

思路

1.盒子通过计算得出大小
2.蛇的核心通过设置新的position位置去更新蛇状态
3.将设计好的食物传进去判断吃食物,更新蛇状态
4.代码很多地方可优化,例如食物刷新的位置要加蛇的位置判断,蛇按键监听的判断需要优化,主要记录一下大概思路
5.项目是vue3+TS,但并没有使用vue3响应式的相关API,方便可以跑在别的框架里

代码

<template>
    <div class="GluttonousSnake" ref="GluttonousSnakeBox">
        <div class="gameBox" ref="gameBoxRef"></div>
    </div>
</template>
<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import { Food, Snake, initBoxDom } from "./GluttonousSnake"
const GluttonousSnakeBox = ref()
const gameBoxRef = ref()

onMounted(() => {
    init()
})
let newSnake: Snake
let newFood: Food
const init = () => {
    initBoxDom(gameBoxRef.value, GluttonousSnakeBox.value)
    newFood = new Food()
    newSnake = new Snake(newFood)

}
</script>

<style lang="scss" scoped>
.GluttonousSnake {
    height: 100%;
    position: relative;
    :deep(.gameBox) {
        border: 1px solid red;
        box-sizing: border-box;
        // position: relative;
        .snake {
            position: absolute;
            transition: all 0.5s;
        }
        .food {
            position: absolute;
        }
    }
}
</style>
type Direction = 'top' | 'bottom' | 'left' | 'right'
const HEIGHT = 20
const WIDTH = 20
let BoxDom: HTMLElement

export const initBoxDom = (dom: HTMLElement, GluttonousSnakeBox: HTMLElement) => {
    BoxDom = dom
    BoxDom.style.width = `${GluttonousSnakeBox.offsetWidth - GluttonousSnakeBox.offsetWidth % WIDTH}px`
    BoxDom.style.height = `${GluttonousSnakeBox.offsetHeight - GluttonousSnakeBox.offsetHeight % HEIGHT}px`
}
export class Snake {
    _food: Food | undefined
    _position: [number, number] = [0, 3]
    _snakeArr = [[0, 3], [0, 2], [0, 1]]
    height = HEIGHT
    width = WIDTH
    length = this._snakeArr.length
    direction: Direction = 'right' //方向
    speedTime = 500
    runSetInterval: number | undefined
    constructor(food: Food) {
        this._food = food
        this.run()
        this.onKeyWord()
    }
    set position(newValue: [number, number]) {
        this._position = newValue
        this.updata_snakeArr()
        const flag = this.isLife()
        if (flag) {
            this.eatFood()
            this.show_snakeArr()
        } else {
            clearInterval(this.runSetInterval)
            alert('game over')
        }
    }
    get position() {
        return this._position
    }
    updata_snakeArr() {
        if (this._snakeArr.length >= this.length) {
            this._snakeArr.pop()
        }
        this._snakeArr.splice(0, 0, this._position)

    }
    show_snakeArr() {
        const snakeDom = document.querySelectorAll('.snake')
        snakeDom.forEach((item) => {
            const x = item.getAttribute('x');
            const y = item.getAttribute('y');
            (item as HTMLElement).style.backgroundColor = 'red'
            const flag = this._snakeArr.some(snake => {
                if (snake[0] === Number(x) && snake[1] === Number(y)) {
                    return true
                }
            })
            if (!flag) {
                item.remove()
            }
        })
        this._snakeArr.forEach((snake, index) => {
            let Flag = true
            snakeDom.forEach(dom => {
                const x = dom.getAttribute('x')
                const y = dom.getAttribute('y')
                if (snake[0] === Number(x) && snake[1] === Number(y)) {
                    Flag = false
                }
            })
            if (Flag) {
                const position = snake
                const snakeDom = document.createElement('div')
                snakeDom.style.width = `${this.width}px`
                snakeDom.style.height = `${this.height}px`
                snakeDom.style.backgroundColor = index === 0 ? 'yellow' : 'red'
                snakeDom.className = 'snake'
                snakeDom.setAttribute('x', String(position[0]))
                snakeDom.setAttribute('y', String(position[1]))
                snakeDom.style.top = `${position[0] * this.height}px`
                snakeDom.style.left = `${position[1] * this.width}px`
                BoxDom.appendChild(snakeDom)
            }
        })
    }

    run() {
        this.runSetInterval = setInterval(() => {
            let newPosition: [number, number] = [0, 0]
            switch (this.direction) {
                case 'top':
                    newPosition = [this._position[0] - 1, this._position[1]]
                    break;
                case 'bottom':
                    newPosition = [this._position[0] + 1, this._position[1]]
                    break;
                case 'left':
                    newPosition = [this._position[0], this._position[1] - 1]
                    break;
                case 'right':
                    newPosition = [this._position[0], this._position[1] + 1]
                    break;

                default:
                    break;
            }
            this.position = newPosition
        }, this.speedTime)
    }
    isLife() {
        const [SnakeTop, SnakeLeft] = this._position
        const BoxBottom = BoxDom.offsetHeight
        const BoxRight = BoxDom.offsetWidth
        if (SnakeTop * this.height < 0 || SnakeLeft * this.width < 0 || (SnakeTop + 1) * this.height > BoxBottom || (SnakeLeft + 1) * this.width > BoxRight) {
            return false
        } else {
            let Flag = true
            if (this.length > 4) {
                Flag = !this._snakeArr.slice(1).some(item => {
                    const [x, y] = item
                    if (x === SnakeTop && y === SnakeLeft) {
                        return true
                    }
                })
            }
            return Flag
        }
    }
    onKeyWord() {
        document.body.onkeydown = (e) => {
            const ev = e || window.event;
            switch (ev.keyCode) {
                case 38:
                    if (this.direction != 'bottom') {  // 不允许返回,向上的时候不能向下
                        this.direction = "top";
                    }
                    break;
                case 40:
                    if (this.direction != "top") {
                        this.direction = "bottom";
                    }
                    break;
                case 37:
                    if (this.direction != "right") {
                        this.direction = "left";
                    }
                    break;
                case 39:
                    if (this.direction != "left") {
                        this.direction = "right";
                    }
                    break;
            }
        };
    }
    eatFood() {
        if (this._food?.position[0] === this._position[0] && this._food?.position[1] === this._position[1]) {
            this.length += 1
            this._food?.rest()
        }
    }
}


export class Food {
    _position: [number, number] = [0, 0]
    width = WIDTH
    height = HEIGHT
    constructor() {
        this.createPosition()
    }
    set position(newValue: [number, number]) {
        this._position = newValue
        this.show_Food()
    }
    get position() {
        return this._position
    }
    show_Food() {
        const position = this._position
        const foodDom = document.createElement('div')
        foodDom.style.width = `${this.width}px`
        foodDom.style.height = `${this.height}px`
        foodDom.style.backgroundColor = 'red'
        foodDom.className = 'food'
        foodDom.setAttribute('x', String(position[0]))
        foodDom.setAttribute('y', String(position[1]))
        foodDom.style.top = `${position[0] * this.height}px`
        foodDom.style.left = `${position[1] * this.width}px`
        BoxDom.appendChild(foodDom)
    }
    createPosition() {
        const heightX = BoxDom.offsetHeight / HEIGHT
        const widthY = BoxDom.offsetWidth / WIDTH

        const x = this.random(0, heightX)
        const y = this.random(0, widthY)
        this.position = [x, y]
    }
    rest() {
        const foodDom = document.querySelector('.food')
        foodDom?.remove()
        this.createPosition()
    }
    random(min: number, max: number) {
        return Math.floor(Math.random() * (max - min)) + min;
    }
}


效果图

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值