原生JS实现贪吃蛇

  

 

HTML

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        *{margin:0;padding:0}
        button,input{padding:20px;outline:none;}
        .map{
            width:800px;
            height:600px;
            background: url('./imgs/bg.png') repeat;
            border:10px solid purple;
            margin:100px auto;
            position:relative;
        }
        .map>.food{
            width:20px;
            height:20px;
            background-image:url('./imgs/food.png');
            position:absolute;
            left:100px;
            top:100px;
        }
        .map>.head{
            width:20px;
            height:20px;
            background-image:url('./imgs/head.png');
            position:absolute;
            left:0;
            top:0;
        }
        .map>.body{
            width:20px;
            height:20px;
            background-image:url('./imgs/body.png');
            position:absolute;
            left:80px;
            top:0;
        }
        .popup{
            position:fixed;
            border:2px double yellow;
            /* height:80px; */
            /* width:200px; */
            margin: 20px auto -60px;
            padding:0 20px;
            background-color: rgb(84,84,84);
            color:#fff;
            text-align:center;
            line-height:80px;
            border-radius:20px;
            left:50%;
            top:50px;
            font-size:20px;
            transform:translateX(-50%);
            display:none;
        }
        .popup::after{
            content:'\1F609';
            position:absolute;
            left:100%;
        }
    </style>
</head>
<body>
    <button class="start">start</button>
    <button class="stop">stop</button>
    <button class="restart">restart</button>
    <input type="text" value='0' class='score' disabled>
    <div class="popup">popup</div>
    <div class="map">
        <!-- <div class="food"></div>
        <div class="head"></div>
        <div class="body"></div>
        <div class="body"></div> -->
    </div>
    <script src="./main.js" type='module'></script>
    <script>
        // const map=document.querySelector('.map')
        // const f=new Food(map)
        // console.log(f)
        // const snake=new Snake(map)
        // console.log(snake)
        
    </script>
</body>
</html>

 

greedysnake.js

import Food from './food.js'
import Snake from './snake.js'

export default class GreedySnake{
    constructor(elem,score,popup){
        this.map=document.querySelector(elem)
        this.timer=null
        this.level=1
        this.food=new Food(this.map)
        this.snake=new Snake(this.map)
        this.score=document.querySelector(score)
        this.count=0
        this.steer()
        this.popup=document.querySelector(popup)
        this.flag=true
    }
    start(){
        this.timer=setInterval(()=>{
            this.alert()
            this.snake.motion()
            let flag=this.snake.achieve(this.food)
            // console.log('this.food',this.food,this.food.food.offsetLeft,this.food.food.offsetTop)
            if(flag){
                this.updateScore()
                this.snake.defineHead()
                this.food.generator()
            }
            if(this.snake.termination()){
                clearInterval(this.timer)
                // window.alert('Game Over!!!')
                if(window.confirm('Oops! Game Over! Do you want to restart ?')){
                    this.restart()
                }
            }
        },400-this.level*50)
    }
    stop(){
        window.clearInterval(this.timer)
    }
    restart(){
        window.location.reload()
    }
    steer(){
        document.addEventListener('keydown',e=>{
            e.preventDefault()
            // console.log(e.key)
            switch(e.key){
                case 'ArrowUp': this.snake.bearing='top';break
                case 'ArrowDown': this.snake.bearing='bottom';break
                case 'ArrowLeft': this.snake.bearing='left';break
                case 'ArrowRight': this.snake.bearing='right';break
            }
        })
    }
    alert(){
        if(this.count%1===0 && this.flag){
            if(this.count !== 0){
                this.popup.innerHTML=`^_^ Congratulations, promote to level: ${this.level}`
            }else{
                this.popup.innerHTML=`^_^ level: ${this.level}`
            }
            this.popup.style.display='block';
            this.flag=false
            setTimeout(()=>{
                this.popup.style.display='none'
            },1500)
        }
    }
    updateScore(){
        this.count++
        // console.log('count: ',this.count,typeof this.score.value,this.score.value)
        this.score.value=parseInt(this.score.value)+this.level*10
        if(this.count%1===0){
            this.flag=true
            this.level++
            this.stop()
            this.start()
        }
    }
}

 

snake.js

export default class Snake{
    constructor(map){
        this.map=map
        this.snake=[]
        this.bearing='right'
        this.initialize()
    }
    getBearing(){
        const head=this.snake[0]
        if(!head) return {left:0,right:0}
        const bearing={left:head.offsetLeft,top:head.offsetTop}
        switch(this.bearing){
            case 'top': bearing.top-=20;break
            case 'right': bearing.left+=20;break
            case 'bottom': bearing.top+=20;break
            case 'left': bearing.left-=20;break
        }
        // console.log('getBearing',bearing)
        return bearing
    }
    defineHead(){
        const position=this.getBearing()
        // console.log('defineHead',position)
        const head=this.snake[0]
        if(head) head.className='body'
        const div=document.createElement('div')
        div.className='head'
        div.style.left=position.left+'px'
        div.style.top=position.top+'px'
        this.snake.unshift(div)
        this.map.appendChild(div)
    }
    initialize(){
        const num=3;
        for(let i=0;i<num;++i){
            this.defineHead()
        }
    }
    motion(){
        const body=this.snake.pop()
        body.remove()
        this.defineHead()
    }
    termination(){
        const head=this.snake[0]
        const x=head.offsetLeft
        const y=head.offsetTop
        if(x<0 || y<0 || x>this.map.clientWidth || y>this.map.clientHeight){
            return true
        }else{
            let flag=false
            const body=this.snake.slice(1)
            body.forEach((value,index,array)=>{
                if(value.offsetLeft === x && value.offsetTop === y) flag=true
            })
            return flag
        }
    }
    achieve(food){
        const x=this.snake[0].offsetLeft
        const y=this.snake[0].offsetTop
        if(x === food.food.offsetLeft && y === food.food.offsetTop) return true
        return false
    }
}

 

food.js

export default class Food{
    constructor(map){
        this.map=map
        this.food=document.createElement('div')
        this.food.className='food'
        this.map.appendChild(this.food)
        this.x=0
        this.y=0
        this.generator()
    }
    generator(){
        const numberX=this.map.clientWidth/this.food.clientWidth
        const numberY=this.map.clientHeight/this.food.clientHeight
        const x=Math.floor(Math.random()*numberX)
        const y=Math.floor(Math.random()*numberY)
        this.food.style.left=x*20+'px'
        this.food.style.top=y*20+'px'
        // console.log('food',x,y,this.food.offsetLeft,this.food.offsetTop)
    }
}

 

main.js

import GreedySnake from './greedysnake.js'

const pastime=new GreedySnake('.map','.score','.popup')
const startButton=document.querySelector('.start')
startButton.addEventListener('click',()=>pastime.start())
const stopButton=document.querySelector('.stop')
stopButton.addEventListener('click',()=>pastime.stop())
const restartButton=document.querySelector('.restart')
restartButton.addEventListener('click',()=>pastime.restart())

 

body.png

 

 

head.png

 

 

bg.png

 

 

food.png

 

 

posted @ 2021-04-03 11:31  ascertain  阅读(113)  评论(0编辑  收藏  举报