JavaScript实现的贪吃蛇算法及其研究(五)

内容接上一章JavaScript实现的贪吃蛇算法及其研究(四).

本篇文章主要内容是为贪吃蛇游戏添加交互式的界面,即将贪吃蛇变成可玩的游戏.

游戏结束

为贪吃蛇加上die方法,通过die方法调用gameover函数.由于死亡的判断在之前就已经写完了,我们只需要在撞墙和自食判断成功后调用相关的方法即可.

if(eat(this.x,this.y)){  
    self.body.increase(head);
    World.Food.generate(self);
}else if(hitCheck(this.x,this.y) || eatSelfCheck(this.x,this.y)){
        die();
        return false;
}else{
    self.body.move(head);
}
return true;  

die方法:

function die(){  
    gameover(self.body.part.length);
}

gameover函数:

function gameover(length){  
    alert('Your Score: '+length);
}

方向控制

对蛇头的操作需要涉及到键盘事件,使用以下代码获得方向:

var direction;  
document.addEventListener('keydown',function(e){  
    switch(e.keyCode){
        case 37:
            direction=Direction.left;
            break;
        case 38:
            direction=Direction.up;
            break;
        case 39:
            direction=Direction.right;
            break;
        case 40:
            direction=Direction.down;
            break;
    }
});

绘图

我们使用Canvas对整个游戏进行显示,编写draw函数用以绘制游戏内容:

function draw(snake){  
    ctx.clearRect(0,0,200,200);
    var head=snake.head;
    ctx.fillStyle='black';
    ctx.fillRect(head.x*10,head.y*10,10,10);
    var body=snake.body.part;
    for(var i=body.length;i--;){
        ctx.fillRect(body[i].x*10,body[i].y*10,10,10);
    }
    ctx.fillStyle='red';
    ctx.fillRect(World.Food.x*10,World.Food.y*10,10,10);
}

游戏主循环

由于使用了键盘事件,所以游戏本身是不依赖循环进行的,大部分操作是触发式.

这里使用循环是为了调节蛇的移动速度和画面的重绘,使用setTimeout函数完成,代码如下:

(function(){
    if(snake.head.move(direction)){
        draw(snake);
        setTimeout(arguments.callee,200-snake.body.part.length)
    }
})();

其他

由于内容过多不便一一讲解,其他相关的内容请参考源代码.

代码如下:

<!DOCTYPE html>  
<html lang="en">  
<body>  
    <canvas width="200" height="200" style="border:1px solid black"></canvas>
    <script>
        (function(){
            var World={
                Food:{
                    x:undefined,
                    y:undefined,
                    generate:function(snake){
                        var head=snake.head.get(),
                            part=snake.body.part;
                        var map=[]
                        for(var x=0;x<World.Size.width;x++){
                            for(var y=0;y<World.Size.height;y++){
                                if(x==head.x && y==head.y){
                                    continue;
                                }
                                if(part.length>0){
                                    var pass=true;
                                    for(var i=part.length;i--;){
                                        if(x==part[i].x && y==part[i].y){
                                            pass=false;
                                            break;
                                        }
                                    }
                                    if(pass){
                                        map.push([x,y]);
                                    }
                                }else{
                                    map.push([x,y]);
                                }
                            }
                        }
                        var i=Math.floor(Math.random()*map.length);
                        World.Food.x=map[i][0];
                        World.Food.y=map[i][1];
                    }
                },
                Size:{
                    width:20,
                    height:20
                }
            };

            var Center={
                x:9,
                y:9
            };

            var Direction={
                up:1,
                right:2,
                down:-1,
                left:-2
            };

            function gameover(length){
                alert('Your Score: '+length);
            }

            function Snake(){

                var self=this;

                function die(){
                    gameover(self.body.part.length);
                }

                function Head(){
                    this.x=Center.x;
                    this.y=Center.y;
                    this.direction=undefined;
                    this.get=function(){
                        return {
                            x:this.x,
                            y:this.y
                        }
                    };
                    this.move=function(direction){
                        if(!direction){
                            return true;
                        }   

                        var head=this.get();

                        if(direction+this.direction==0){
                            direction=this.direction;
                        }

                        this.direction=direction;

                        switch(direction){
                            case Direction.up:
                                this.y--;
                                break;
                            case Direction.right:
                                this.x++;
                                break;
                            case Direction.down:
                                this.y++;
                                break;
                            case Direction.left:
                                this.x--;
                        }

                        if(eat(this.x,this.y)){
                            self.body.increase(head);
                            World.Food.generate(self);
                        }else if(hitCheck(this.x,this.y) || eatSelfCheck(this.x,this.y)){
                                die();
                                return false;
                        }else{
                            self.body.move(head);
                        }
                        return true;
                    };
                    function eat(x,y){
                        if(x==World.Food.x && y==World.Food.y){
                            return true;
                        }else{
                            return false;
                        }
                    }
                    function hitCheck(x,y){
                        if(x<0 || y<0 || x==World.Size.width || y==World.Size.height){
                            return true;
                        }else{
                            return false;
                        }
                    }
                    function eatSelfCheck(x,y){
                        var part=self.body.part;
                        for(var i=part.length;i--;){
                            if(x==part[i].x && y==part[i].y){
                                return true;
                            }
                        }
                        return false;
                    }
                }

                function Body(){
                    this.part=[];
                    this.move=function(head){
                        if(this.part.length>0){
                            this.part.pop();
                            this.increase(head);
                        }
                    }
                    this.increase=function(head){
                        this.part.unshift(head);
                    }
                }

                this.body=new Body();
                this.head=new Head();
            }

            function init(){
                var snake=new Snake();
                World.Food.generate(snake);
                var ctx=document.querySelector('canvas').getContext('2d');

                function draw(snake){
                    ctx.clearRect(0,0,200,200);
                    var head=snake.head;
                    ctx.fillStyle='black';
                    ctx.fillRect(head.x*10,head.y*10,10,10);
                    var body=snake.body.part;
                    for(var i=body.length;i--;){
                        ctx.fillRect(body[i].x*10,body[i].y*10,10,10);
                    }
                    ctx.fillStyle='red';
                    ctx.fillRect(World.Food.x*10,World.Food.y*10,10,10);
                }

                var direction;
                document.addEventListener('keydown',function(e){
                    switch(e.keyCode){
                        case 37:
                            direction=Direction.left;
                            break;
                        case 38:
                            direction=Direction.up;
                            break;
                        case 39:
                            direction=Direction.right;
                            break;
                        case 40:
                            direction=Direction.down;
                            break;
                    }
                });
                (function(){
                    if(snake.head.move(direction)){
                        draw(snake);
                        setTimeout(arguments.callee,200-snake.body.part.length)
                    }
                })();
            }
            init();
        })();
    </script>
</body>  
</html>  

修改后的最终代码:

<!DOCTYPE html>  
<html lang="en">  
<body>  
    <canvas width="200" height="200" style="border:1px solid black"></canvas>
    <script>
        (function(){
            var World={
                Food:{
                    x:null,
                    y:null,
                    generate:function(snake){
                        var head=snake.head.get(),
                            part=snake.body.part;
                        var map=[]
                        for(var x=0;x<World.Size.width;x++){
                            for(var y=0;y<World.Size.height;y++){
                                if(x==head.x && y==head.y){
                                    continue;
                                }
                                if(part.length>0){
                                    (function(){
                                        for(var i=part.length;i--;){
                                            if(x==part[i].x && y==part[i].y){
                                                return;
                                            }
                                        }
                                        map.push([x,y]);
                                    })();
                                }else{
                                    map.push([x,y]);
                                }
                            }
                        }
                        var i=Math.floor(Math.random()*map.length);
                        World.Food.x=map[i][0];
                        World.Food.y=map[i][1];
                    }
                },
                Size:{
                    width:20,
                    height:20
                }
            };

            var Center={
                x:9,
                y:9
            };

            var Direction={
                up:1,
                right:2,
                down:-1,
                left:-2
            };

            function gameover(length){
                alert('Your Score: '+length);
            }

            function Snake(){

                var self=this;

                function die(){
                    gameover(self.body.part.length);
                }

                function Head(){
                    this.x=Center.x;
                    this.y=Center.y;
                    this.direction=null;
                    this.get=function(){
                        return {
                            x:this.x,
                            y:this.y
                        }
                    };
                    this.move=function(direction){
                        if(!direction){
                            return true;
                        }

                        var head=this.get();

                        this.direction=direction=direction+this.direction?direction:this.direction;

                        switch(direction){
                            case Direction.up:
                                this.y--;
                                break;

                            case Direction.right:
                                this.x++;
                                break;
                            case Direction.down:
                                this.y++;
                                break;
                            case Direction.left:
                                this.x--;
                        }

                        if(eat(this.x,this.y)){
                            self.body.increase(head);
                            World.Food.generate(self);
                        }else if(hitCheck(this.x,this.y) || eatSelfCheck(this.x,this.y)){
                            die();
                            return false;
                        }else{
                            self.body.move(head);
                        }
                        return true;
                    };
                    function eat(x,y){
                        return x==World.Food.x && y==World.Food.y;
                    }
                    function hitCheck(x,y){
                        return x<0 || y<0 || x==World.Size.width || y==World.Size.height;
                    }
                    function eatSelfCheck(x,y){
                        var part=self.body.part;
                        for(var i=part.length;i--;){
                            if(x==part[i].x && y==part[i].y){
                                return true;
                            }
                        }
                        return false;
                    }
                }

                function Body(){
                    this.part=[];
                    this.move=function(head){
                        if(this.part.length>0){
                            this.part.pop();
                            this.increase(head);
                        }
                    }
                    this.increase=function(head){
                        this.part.unshift(head);
                    }
                }

                this.body=new Body();
                this.head=new Head();
            }

            function init(){
                var snake=new Snake();
                World.Food.generate(snake);
                var ctx=document.querySelector('canvas').getContext('2d');

                function draw(snake){
                    ctx.clearRect(0,0,200,200);
                    var head=snake.head;
                    ctx.fillStyle='black';
                    ctx.fillRect(head.x*10,head.y*10,10,10);
                    var body=snake.body.part;
                    for(var i=body.length;i--;){
                        ctx.fillRect(body[i].x*10,body[i].y*10,10,10);
                    }
                    ctx.fillStyle='red';
                    ctx.fillRect(World.Food.x*10,World.Food.y*10,10,10);
                }

                var direction,dict_direction=[];
                dict_direction[37]=Direction.left;
                dict_direction[38]=Direction.up;
                dict_direction[39]=Direction.right;
                dict_direction[40]=Direction.down;

                document.addEventListener('keydown',function(e){
                    direction=dict_direction[e.keyCode]||direction;
                });

                (function(){
                    if(snake.head.move(direction)){
                        draw(snake);
                        setTimeout(arguments.callee,200-snake.body.part.length)
                    }
                })();
            }
            init();
        })();
    </script>
</body>  
</html>