贪吃蛇小游戏实现

index.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8"/>
        <title>贪吃蛇</title>
        <link rel="stylesheet" href="css/styles.css">
        
    </head>
    <body>
        <div id="map"></div>

        <script src="js/index.js"></script>
        
    </body>
</html>

style.css

#map{
    width: 800px;
    height: 600px;
    background-color: #ccc;
    position: relative;
}

index.js

tools.js
//自调用函数传入window的目的,是让变量可以被压缩
//防止undefined重命名,也可以被压缩
;(function(window,undefined){//形参
    var Tools = {
        getRandom:function(min,max){
            return Math.floor(Math.random() * (max - min +1) + min);
        }
    }
    //暴露Tools给window
    window.Tools = Tools;
})(window,undefined)//实参


//_____________________Parent.js_____//这里不建议使用继承
;(function(window){
    function Parent(options){
        options = options || {};
        this.width = options.width || 20;
        this.height = options.height || 20;
    }

    Parent.prototype.test = function(){
        console.log("test");
    }
    window.Parent = Parent;
})(window,undefined)

food.js
/*所有js文件书写代码,都是全局作用域。
为了避免命名冲突,可以使用构造函数,但是不同js文件也可能出现相同的函数名
function fn(){}
fn();
所以就要有另外解决命名冲突,自调用函数,开启一个新的作用域,避免命名冲突
(
(function(){
    console.log("1");
})()
*/

//优化方法,remove别人访问不到,如果是Food对象的方法,别人是可以访问到的
;(function(window,undefined){
    //局部作用域
    var position = "absolute";
//记录上一次创建的食物,为了删除做准备
    var elements = [];
    function Food(options){
        options = options || {};
        this.x = options.x || 0;
        this.y = options.y || 0;
        
       // this.width = options.width || 20;
        //this.height = options.height || 20;
    //————————————————————————借用构造函数,此时this就是food对象
        Parent.call(this,options);

        this.color = options.color || "green";
    }

    //——————————————原型继承——————————————
    Food.prototype = new Parent();
    Food.prototype.constructor = Food;
//渲染
Food.prototype.render = function(map){  //创建div,给出样式
    //删除之前创建的食物
    //记忆之前创建的食物
    remove();

    //随机设置x,y的值
    this.x = Tools.getRandom(0,map.offsetWidth/this.width - 1) * this.width;
    this.y = Tools.getRandom(0,map.offsetHeight/this.height - 1) * this.height;
    //动态创建div,页面上显示的食物   
    var div = document.createElement("div");
    map.appendChild(div);

    elements.push(div);//存放食物div

    //div样式
    div.style.position = position;
    div.style.left = this.x + "px";
    div.style.top = this.y + "px";
    div.style.width = this.width + "px";
    div.style.height = this.height + "px";
    div.style.backgroundColor = this.color;
}

function remove(){
    for(var i = elements.length - 1; i >= 0; i--){
        //删除div
        elements[i].parentNode.removeChild(elements[i]);
        //删除数组中的元素
        elements.splice(i,1);
    }    
}

//把Food构造函数,让外部可以访问
window.Food = Food;
})(window,undefined)


//测试
/*var map = document.getElementById('map');
var food = new Food();
food.render(map);*/

//snake.js

//自调用函数,会开启一个新的作用域,防止命名冲突
;(function(window,undefined){
    var position = "absolute";
    //存放之前创建的蛇
    var elements = [];

    function Snake(options){
        options = options || {};
       // this.width = options.width || 20;
       // this.height = options.height || 20;
        //_________________借用构造函数
        Parent.call(this,options);

        this.direction = options.direction || "right";

        this.body = [
            {x: 3, y: 2, color: "red"},
            {x: 2, y: 2, color: "blue"},
            {x: 1, y: 2, color: "blue"}
        ];
    }
    Snake.prototype = new Parent();
    Snake.prototype.constructor = Snake;

    Snake.prototype.render = function(map){
        //把蛇身节点渲染到地图上
        //删除之前创建的蛇
        remove();

        for(var i = 0,len = this.body.length; i < len; i++){
            //蛇节
            var object = this.body[i];
            //
            var div = document.createElement("div");
            map.appendChild(div);

            //记录当前蛇
            elements.push(div);

            //设置样式
            div.style.position = position;
            div.style.width = this.width + "px";
            div.style.height = this.height + "px";
            div.style.left = object.x * this.width + "px";
            div.style.top = object.y * this.height + "px";
            div.style.backgroundColor = object.color;
        }
    }

     //私有成员
    function remove(){
        for(var i = elements.length - 1; i >= 0; i--){
            //删除div
            elements[i].parentNode.removeChild(elements[i]);
            //删除数组元素
            elements.splice(i,1);
        }
    }
    //控制蛇移动的方法
    Snake.prototype.move = function (food,map){
        //控制蛇身体移动(当前蛇节-上一个蛇节的位置)把每一个蛇节映射到地图上
        for(var 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;  
        }
        //控制蛇头的方向

        var head = this.body[0];
        switch(this.direction){
            case "right":
                head.x += 1;
                break;
            case "left":
                head.x -= 1;
                break;
            case "top":
                head.y -= 1;
                break;
            case "bottom":
                head.y += 1;
                break;
        }
        // 2.3.1 判断蛇是否和食物的坐标重合
        var headX = head.x * this.width;//蛇节的宽度this.width,move方法是蛇对象的方法,this就指向蛇对象
        var headY = head.y * this.height;
        if(headX === food.x && headY === food.y){
            //让蛇增加一节
            //获取蛇的最后一节
            var last = this.body[this.body.length - 1];
           /* this.body.push({
                x: last.x,
                y: last.y,
                color: last.color
            });
            */
           var obj = {};
           
           //对象拷贝
           extend(last,obj);
           this.body.push(obj);

            //随机在地图上重新生成食物
            food.render(map);
            //render方法需要传入map,由于蛇对象无地图,Snake.prototype.render方法中有地图但获取不到,
            //所以需要在Snake.prototype.move = function(food,map)在多传入一个参数
            //move方法中增加一个参数,调用的位置也需要参数,game.js中要添加that.map至that.snake.move(that.food,that.map)
            //做以上工作主要是为了在food.render()需要渲染到指定位置
        }
    }

    function extend(parent,child){
        for(var key in parent){
            if(child[key]){
                continue;
            }
            child[key] = parent[key];
        }
    }
    window.Snake = Snake;
})(window,undefined)
/*测试以上函数是否有问题
var map = document.getElementById("map");
var snake = new Snake();
snake.render(map);
*/

///game.js

//使用自调用函数,创建新的局部作用域,防止命名冲突
;(function(window,undefined){
    var that;//记录游戏对象,在runSnake()调用
    function Game(){
        this.food = new Food();
        this.snake = new Snake();
        this.map = map;
        that = this;//将this值给that,可以在将来自调用函数的任何位置获取游戏对象
    }

    Game.prototype.start = function (){
        //1.把蛇和食物对象,渲染到地图上来
        this.food.render(this.map);
        this.snake.render(this.map);
    /*测试
        this.snake.move();
        this.snake.render(this.map);
        this.snake.move();
        this.snake.render(this.map);
    */
        //2.开始游戏逻辑
        //2.1让蛇移动
        runSnake();
        //2.2通过键盘控制蛇的移动
        bindKey();
        //2.3当蛇遇到食物,作相应的处理

        //2.4蛇遇到边界游戏结束
    }
    //通过键盘控制蛇移动的方向
    function bindKey(){
        //document.onkeydown = function(){}
        document.addEventListener("keydown",function(e){//function(e){}事件处理函数,this指触发事件的对象,指的是document
            /*  console.log(e.keyCode); //用于查看按键的编码数据
            //left - 37; top - 38;  right - 39; bottom - 40   */
            switch(e.keyCode){
                case 37:
                    this.snake.direction = "left";
                    break;
                case 38:
                    this.snake.direction = "top";
                    break;
                case 39:
                    this.snake.direction = "right";
                    break;
                case 40:
                    this.snake.direction = "bottom";
                    break;
            }
        }.bind(that),false); //第三个参数为false是事件冒泡阶段,加上bind里第一个参数that就改变了function中的this对象
    }

    //私有函数,让蛇移动
    function runSnake(){
        var timerId = setInterval(function(){//如果不将that改为this,this默认指向window,将此函数内that全部改为this
            //每隔150ms让蛇走一格
            //在定时器中,this指向window对象,而不是game对象,this.snake不是指向game对象,而是指向window 
            //获取游戏对象中的蛇属性,利用that
            this.snake.move(this.food,this.map);//判断是否吃到食物
            this.snake.render(this.map); //将蛇渲染到地图上

            //2.2通过键盘控制蛇的移动
            //获取蛇头的坐标
            var maxX = this.map.offsetWidth/that.snake.width;  //一行有多少蛇节
            var maxY = this.map.offsetHeight/that.snake.height;
            var headX = this.snake.body[0].x;
            var headY = this.snake.body[0].y;
            if(headX < 0 || headX >= maxX || headY < 0 || headY >= maxY){
                alert("game over!");
                clearInterval(timerId);
            }
        }.bind(that),150);    //bind(that)里存放当前游戏对象
    }
    //暴露构造函数给外部
    window.Game = Game;
})(window,undefined);


//测试
/*var map = document.getElementById("map");
var game = new Game(map);
game.start();*/

/main.js
;(function(window,undefined){
    var map = document.getElementById("map");
    var game = new Game(map);//等价于 var game = new window.Game(map);只是windowkeyishenglue
     game.start(window,undefined);
})()


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值