天天看點

Html Canvas JavaScript自制貪吃蛇小遊戲

單檔案html,直接複制可用。

可調節速度和随機大小食物。

<html >
<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>Snake</title>
    <style>
        .text{
            width: 100%;
            line-height: 30px;
            height: 30px;
            font-size: 16pt;
            text-align: center;
        }
        canvas{
            display: block;
            margin: 30px auto;
            border: solid;
            border-width: 1px;
            border-color:rgb(51, 51, 51);
            box-shadow: 0 0 12px 2px rgba(51, 51, 51, 0.8);
            
        }
        .slider-contain{
            text-align: center;
            position: relative;
        }
        .slider-bar{
            display: inline-block;
            height: 8px;
            width: 300px;
            background-color: rgb(218, 218, 218);            
            border: solid;
            border-color: rgb(218, 218, 218);            
            border-width: 1px;
            box-shadow: 0 0 8px 2px rgba(97, 97, 97, 0.8);
        }
        .slider-block{
            position: absolute;
            height: 23px;
            width: 10px;
            background-color: rgb(8, 85, 8);
            margin-top: -7px;
            cursor: pointer;
        }
        
    </style>
</head>
<body>
    <div class="text" id="score">0</div>
    <canvas id="canvas"></canvas>    
    <div class="slider-contain">     
        <abbr title="拖動調節速度">     
            <div class="slider-bar">
                <div class="slider-block"></div>
            </div>  
        </abbr>                                  
    </div>    
    <br><br>
    <div class="text">
        上下左右控制方向
    </div>    
    <div class="text">
        F5重新開始|空格暫停/繼續
    </div>    
</body>
    
<script>


const direction={
    UP:0,
    RIGHT:1,
    DOWN:2,
    LEFT:3
}

var snake={
    color:'#04573D',
    headColor:'#211E1E',
    size:10,
    length:3,
    direction:direction.RIGHT,
    speed:100,// 200ms/move 1 time  200-50
    body:[
        {x:25,y:145},
        {x:15,y:145},
        {x:5,y:145}
    ],//每一節身體的位置
    tail:[]//記錄所有的尾巴,以便吃到食物後增長,按食物大小增長長度    
}

var food={
    color:'#8F401F',
    size:10,
    position:{x:200,y:200},//食物出現的位置
}

var map={
    color:'#868686',
}

var Game=function(){
    this.score=0;//計分
    this.isDealControl=true;//是否處理了方向控制
    this.pause=false;//遊戲暫停
    this.direction=direction;//方向枚舉
    this.snake=snake;//蛇
    this.food=food;//食物
    this.map=map;    //地圖
    this.canvas=document.getElementById('canvas');
    this.canvas.width = 300;
    this.canvas.height = 300;
    this.cxt=this.canvas.getContext("2d");

    let speed=localStorage.getItem("snake_speed");
    if(speed)
        this.snake.speed=parseInt(speed);
    else
        localStorage.setItem('snake_speed',this.snake.speed);    
    this.init();
    this.draw_map();
    this.create_food();
    window.onkeyup=this.control.bind(this);
}

Game.prototype.draw_map=function(){        
    this.cxt.fillStyle=this.map.color;
    this.cxt.fillRect(0,0,this.canvas.width,this.canvas.height);    
}
Game.prototype.reset_map=function(){
    this.cxt.clearRect(0,0,this.canvas.width,this.canvas.height);
    this.draw_map(); 
    this.draw_snake(); 
    this.draw_food();  
}
Game.prototype.draw_snake=function(){      
    for(var i in this.snake.body){
        if(i==0)//頭部        
            this.cxt.fillStyle=this.snake.headColor;                    
        else
            this.cxt.fillStyle=this.snake.color;        
        this.cxt.fillRect(this.snake.body[i].x-this.snake.size/2,this.snake.body[i].y-this.snake.size/2,this.snake.size,this.snake.size);
    }            
}
Game.prototype.create_food=function(){
    this.food.size=10+Math.round(Math.random()*20);
    this.food.position.x=Math.round(Math.random()*this.canvas.width);
    this.food.position.y=Math.round(Math.random()*this.canvas.height);
    if(this.food.position.x<this.food.size/2)this.food.position.x=this.food.size/2;
    if(this.food.position.y<this.food.size/2)this.food.position.y=this.food.size/2;
    if(this.food.position.x>this.canvas.width-this.food.size/2)this.food.position.x=this.canvas.width-this.food.size/2
    if(this.food.position.y>this.canvas.height-this.food.size/2)this.food.position.y=this.canvas.height-this.food.size/2
}
Game.prototype.draw_food=function(){
    this.cxt.fillStyle=this.food.color;
    this.cxt.fillRect(this.food.position.x-this.food.size/2,this.food.position.y-this.food.size/2,this.food.size,this.food.size);
}
Game.prototype.move=function(){
    const json=JSON.stringify(this.snake.body);    
    const oldbody=JSON.parse(json);
    this.snake.tail.push(oldbody[oldbody.length-1]); 
    //續上尾巴個數
    let tailCount=1;
    //每截身體移動:頭部移動,之後的每一節=前一節    
    for(var i=0;i<this.snake.length;i++){
        if(i==0)
        {
            switch(this.snake.direction){
                case direction.UP:this.snake.body[i].y-=this.snake.size;break;
                case direction.RIGHT:this.snake.body[i].x+=this.snake.size;break;
                case direction.DOWN:this.snake.body[i].y+=this.snake.size;break;
                case direction.LEFT:this.snake.body[i].x-=this.snake.size;break;
            }            
        }
        else{
            if(i<this.snake.body.length){
                this.snake.body[i]=oldbody[i-1]              
            }else{
                this.snake.body[i]=this.snake.tail[this.snake.tail.length-tailCount]
                tailCount++;
            }
        }                             
    } 
    this.isDealControl=true;   
    //碰撞檢測
    //碰撞牆壁
    if(this.snake.body[0].x-this.snake.size/2<0||this.snake.body[0].x+this.snake.size/2>this.canvas.width||this.snake.body[0].y-this.snake.size/2<0||this.snake.body[0].y+this.snake.size/2>this.canvas.height){
        return false;      
    }   
    //碰撞自己 xy均有重疊
    for(var i=1;i<this.snake.body.length;i++) {        
        if(
        Math.abs(this.snake.body[0].x - this.snake.body[i].x) < this.snake.size/2 + this.snake.size/2
        &&
        Math.abs(this.snake.body[0].y - this.snake.body[i].y) < this.snake.size/2 + this.snake.size/2
        ){
            return false;
        }        
    }
    //碰撞食物
    if(
      Math.abs(this.snake.body[0].x - this.food.position.x) < this.snake.size/2 + this.food.size/2
      &&
      Math.abs(this.snake.body[0].y - this.food.position.y) < this.snake.size/2 + this.food.size/2
    ){
      this.create_food();
      this.snake.length+=Math.ceil(this.food.size/this.snake.size);
      this.score+=this.food.size;
    }
    return true;
}
Game.prototype.control=function(e){
    if(!this.isDealControl)
        return;
    switch(e.keyCode){
        case 37:if(this.snake.direction!==direction.RIGHT){this.snake.direction=direction.LEFT;this.isDealControl=false;}break;
        case 38:if(this.snake.direction!==direction.DOWN){this.snake.direction=direction.UP;this.isDealControl=false;}break;
        case 39:if(this.snake.direction!==direction.LEFT){this.snake.direction=direction.RIGHT;this.isDealControl=false;}break;
        case 40:if(this.snake.direction!==direction.UP){this.snake.direction=direction.DOWN;this.isDealControl=false;}break;
        case 32:
            this.pause=!this.pause;
            if(!this.pause)this.go();           
            break;
    }
}
Game.prototype.init=function(){
    let that=this;
    let slider_bar=document.getElementsByClassName('slider-bar')[0];
    let slider_block=document.getElementsByClassName('slider-block')[0];
    let moveX; 
    slider_block.style.left = (slider_bar.offsetLeft+(200-this.snake.speed)*2)+'px';
    slider_block.onmousedown=function(event){
        var event = event || window.event;
        var diffX = event.clientX - slider_block.offsetLeft;
        var diffY = event.clientY - slider_block.offsetTop;
        if(typeof slider_block.setCapture !== 'undefined'){
            slider_block.setCapture(); 
        }           
        document.onmousemove = function(event){
            var event = event || window.event;
            moveX = event.clientX - diffX;
            if(moveX<slider_bar.offsetLeft)
                moveX=slider_bar.offsetLeft;
            if(moveX>slider_bar.offsetLeft+slider_bar.offsetWidth)
                moveX=slider_bar.offsetLeft+slider_bar.offsetWidth
            // var moveY = event.clientY - diffY;
            if(moveX < 0){
                moveX = 0
            }else if(moveX > window.innerWidth - slider_block.offsetWidth){
                moveX = window.innerWidth - slider_block.offsetWidth
            }
            // if(moveY < 0){
            //     moveY = 0
            // }else if(moveY > window.innerHeight - slider_block.offsetHeight){
            //     moveY =  window.innerHeight - slider_block.offsetHeight
            // }
            slider_block.style.left = moveX + 'px';
            // slider_block.style.top = moveY + 'px'
        }
        document.onmouseup = function(event){
            this.onmousemove = null;
            this.onmouseup = null;
            //value:0-300  speed:200-50
            let value=moveX-slider_bar.offsetLeft;
            let speed=200-value*0.5;
            localStorage.setItem('snake_speed',speed);
            that.snake.speed=speed;
            //低版本ie bug  
            if(typeof slider_block.releaseCapture!='undefined'){  
                slider_block.releaseCapture();  
            }  
        }
    }
            
}
Game.prototype.go=function(){
    if(!this.move())
    {                
        alert('你沒了');
        return
    }
    this.reset_map();
    //計分
    document.getElementById('score').innerHTML=this.score;
    if(this.pause)return;
    setTimeout(() => {
        this.go();
    }, this.snake.speed);
}

var game=new Game();
game.go();



</script>
</html>
           

繼續閱讀