JS制作简易贪吃蛇游戏

本文介绍如何用JavaScript制作一款简易的贪吃蛇游戏。包括food.js、index.html、main.js、map.js和utils.js等关键代码实现游戏逻辑。
摘要由CSDN通过智能技术生成

简易贪吃蛇游戏制作完成

代码:
food.js

class Food{
    constructor(options){
        options = options || {}
        this.food = null
        this.map = document.querySelector('.map')
        this.width = options.width || 20
        this.height = options.height || 20
        this.x = 0
        this.y = 0
        // this.bgColor = options.color || 'pink'
        this.init()
    }
    init(){
        // 创建之前 先判断 页面有没有 .food 如果有 就先干掉
        document.querySelector('.food') ? this.map.removeChild(document.querySelector('.food')) : ''
        // 创建食物 放到地图当中去
        this.food = document.createElement('p')
        this.food.className = 'food'
        css(this.food,'width',this.width + 'px')
        css(this.food,'height',this.height + 'px')
        css(this.food,'background-color',getColor())
        css(this.food,'position','absolute')

        // 总共 可以吧地图分为 800 / 20 个网格 
        // 要设置的食物 的 left  = 网格 * 20   left 的最大值 为 780 所以网格最多只能有 800 / 20 - 1 个
        // 求 食物的位置 
        // 随机 0 - 39 个网格之间  网格 * 食物的宽度 = 我要设置的  left 值
        // 加入 地图的宽度 是 798 / 20 
        this.x = getRandom(0,Math.floor(this.map.offsetWidth / this.width - 1))
        this.y = getRandom(0,Math.floor(this.map.offsetHeight / this.height - 1))
        // 要设置的 left 值 = 0 - count 之间的随机数 * this.width
        css(this.food,'left',this.x * this.width + 'px')
        css(this.food,'top',this.y * this.height + 'px')
        this.map.appendChild(this.food)
    }
}

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
      
        *{
            margin: 0;
            padding: 0;
        }
    </style>
</head>
<body>
    <!-- <div class="map"></div> -->
    <button id="btn">一起动</button>
    <!-- 乌梢蛇 地图 食物  -->
    <script src="./utils.js"></script>
    <script src="./map.js"></script>
    <script src="./food.js"></script>
    <script src="./snake.js"></script>
    <script src="./main.js"></script>
    <script>
        
        new Main()

      
    </script>
</body>
</html>

main.js

class Main {
    constructor(btn) {
        this.btn = btn || document.querySelector("#btn")
        this.map = new Map()
        this.food = new Food()
        this.snake = new Snake({},this.food)
        this.startRun()
        this.keydownEvent()
    }
    keydownEvent() {
        on(document, 'keydown', (e) => {
            // console.log(e.keyCode)
            // 需要通过 按钮 去改变 snake 文件里面的  方向 
            const code = e.keyCode
            switch (code) {
                case 37:
                    this.snake.direction != 'right' && (this.snake.direction = 'left')
                    break
                case 38:
                    this.snake.direction != 'down' && (this.snake.direction = 'up')
                    break
                case 39:
                    this.snake.direction != 'left' && (this.snake.direction = 'right')
                    break
                case 40:
                    this.snake.direction != 'up' && (this.snake.direction = 'down')
                    break
            }
            // console.log(this.snake)
        })
    }
    startRun(){
        on(this.btn,'click',() => {
            this.snake.move()
        })
    }
}

map.js

class Map{
    constructor(options){
        options = options || {}
        this.map = options.map || null
        this.width = options.width || 800
        this.height = options.height || 600
        this.bgColor = options.bgColor || '#333'
        this.init()
    }
    // 要有个方法  创建这个地图
    init(){
        this.map = document.createElement('div')
        this.map.className = 'map'
        // 设置样式
        css(this.map,'width',this.width + 'px')
        css(this.map,'height',this.height + 'px')
        css(this.map,'background-color',this.bgColor)
        css(this.map,'margin','100px auto')
        css(this.map,'position','relative')
        // console.log(this.map)
        // 追加到页面
        document.body.appendChild(this.map)

    }
}

map.js

class Snake {
    constructor(options,food) {
        options = options || {}
        this.food = food 
        this.width = options.width || 20
        this.height = options.height || 20
        this.map = document.querySelector('.map')
        this.direction = 'right'
        this.timer = null
        this.body = [
            {
                x: 3,
                y: 2,
                ele: document.createElement('div'),
                color: '#0f0'
            },
            {
                x: 2,
                y: 2,
                ele: document.createElement('div'),
                color: 'pink'
            }, {
                x: 1,
                y: 2,
                ele: document.createElement('div'),
                color: 'pink'
            }
        ]
        this.init()
    }
    init() {
        this.body.forEach(item => {
            // console.log(item)
            css(item.ele, 'width', this.width + 'px')
            css(item.ele, 'height', this.height + 'px')
            css(item.ele, 'background-color', item.color)
            css(item.ele, 'position', 'absolute')
            css(item.ele,'borderRadius','50%')
            css(item.ele, 'left', item.x * this.width + 'px')
            css(item.ele, 'top', item.y * this.height + 'px')
            this.map.appendChild(item.ele)
        })
    }
    move() {
        this.timer = setInterval(() => {
            // 我要遍历数组  让数组里面后一节身体 = 前一节身体的坐标
            for (let i = this.body.length - 1; i > 0; i--) {
                this.body[i].x = this.body[i - 1].x
                this.body[i].y = this.body[i - 1].y
            }
            // console.log(this.body)
            // 让 头 往 右边走 
            // 只有当 方向 为 right 的时候 才执行 this.body[0].x++
            const head = this.body[0]
            switch (this.direction) {
                case 'right':
                    head.x++
                    break
                case 'left':
                    head.x--
                    break
                case 'up':
                    head.y--
                    break
                case 'down':
                    head.y++
                    break
            }
            
            this.body.forEach(item => {
                css(item.ele, 'left', item.x * this.width + 'px')
                css(item.ele, 'top', item.y * this.height + 'px')
            })
            // 边界判断
            if(head.x < 0 || head.x > Math.floor(this.map.offsetWidth / this.width) - 1 || head.y <0 ||  head.y > Math.floor(this.map.offsetHeight / this.height) - 1){
                alert('gameover')
                clearInterval(this.timer)
            }
            // 判断是否吃到食物  头的坐标是否和食物重合
            if(head.x === this.food.x && head.y === this.food.y){

                // 吃到食物以后 食物重新生成一次 
                this.food.init()
                // 蛇 添加一节 this.body
                this.body.push({
                    x: this.body[this.body.length - 1].x,
                    y: this.body[this.body.length - 1].y,
                    ele: document.createElement('div'),
                    color: getColor()
                })
                this.init()
            }
            
        }, 100)
    }
}

utils.js

* 
    工具类函数

*/
/* 
    检测一个字符串是否是可回文字符串
    @param a 要检测的字符串
    @return 返回值是一个布尔值  
*/
function fn(a) {
    return a.split('').reverse().join('') === a ? true : false
}
/* 
    求一个范围之间的随机数
    @param a 范围下限
    @param b 范围上限
    @return 范围之间的随机数
*/
function getRandom(a, b) {
    return Math.floor(Math.random() * (b - a + 1) + a)
}

/* 
    随机颜色
    @return  随机颜色字符串
*/
function getColor() {
    return "rgb(" + getRandom(0, 255) + "," + getRandom(0, 255) + "," + getRandom(0, 255) + ")"
}

/* 
    通过选择器获取元素
    @param selector 要获取的元素的 id/class/标签名
    @param context 从哪个范围获取
    @return 获取到的元素或者元素伪数组
*/
function my$(selector, context) {
    context = context || document
    if (selector.indexOf('#') === 0) {
        return document.getElementById(selector.slice(1))
    }
    if (selector.indexOf('.') === 0) {
        return context.getElementsByClassName(selector.slice(1))
    }
    return context.getElementsByTagName(selector)
}
/* 
    获取/设置元素的样式 
    @param ele 元素
    @param attr 要获取/设置的属性
    @param value 要获取/设置的属性值
    @return 获取/设置好的属性值
*/
function css(ele, attr, value) {
    if (value) {
        ele.style[attr] = value
    }
    // 有第三个参数就是设置 没有就是获取
    return window.getComputedStyle ? window.getComputedStyle(ele)[attr] : ele.currentStyle[attr]
}

/* 
    绑定事件 兼容处理
    @param ele  要绑定事件的元素
    @param type 事件类型
    @param fn 事件处理函数
*/
function on(ele, type, fn) {
    if (ele.addEventListener) {
        if (type.indexOf('on') === 0) {
            type = type.slice(2)
        }
        ele.addEventListener(type, fn)
    } else {
        if (type.indexOf('on') !== 0) {
            type = 'on' + type
        }
        ele.attachEvent(type, fn)
    }
}

function off(){
    
}
/* 
    对 ie8 不支持  pageX pageY 做的兼容处理
    @param e event 对象
    @return 光标距离页面左边和上边的距离数据对象
*/
function page(e) {
    if (e.pageX) {
        return { x: e.pageX, y: e.pageY }
    }
    var _x = document.documentElement ? document.documentElement.scrollLeft + e.clientX : document.body.scrollLeft + e.clientX 
    var _y = document.documentElement ? document.documentElement.scrollTop + e.clientY : document.body.scrollTop + e.clientY
    return { x:_x,y:_y}
}


/* 
    运动框架函数
    @param ele 执行运动的元素 
    @param options 终点值 是一个对象
    @param duration 运动的总时间
    @param fn 运动执行完毕的回调函数

*/
function animate(ele, options, duration,fn) {
    
    clearInterval(ele.timer)
    const start = {}, range = {}
    for (var key in options) {
        start[key] = parseFloat(css(ele, key))
        range[key] = options[key] - start[key]
    }
    const startTime = +new Date()
    ele.timer = setInterval(() => {
        let nowTime = +new Date()
        let timeDifference = Math.min(nowTime - startTime,duration)
        for (let attr in options) {
            let result = start[attr] + range[attr] / duration * timeDifference
            result = attr === 'opacity' ? result : result + 'px'
            ele.style[attr] = result
        }
        if (timeDifference === duration) {
            clearInterval(ele.timer)
            fn && fn()
        }
    }, 10)
}
/* 
    淡入

*/
function fadeIn(ele,time,fn){
    css(ele,'display','block')
    css(ele,'opacity','0')
    animate(ele,{opacity:1},time,fn)
}
/* 
    淡出
*/
function fadeOut(ele,time,fn){
    css(ele,'display','block')
    css(ele,'opacity','1')
    animate(ele,{opacity:0},time,fn)

}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值