回归原生,使用javascript编写小游戏 --- 贪食蛇

需求分析

  1. 生成地图。

    • 将应用抽象成一个对象。
    • 地图使用一个二维数组作为结构。
  2. 生成食物。

    • 生成食物的范围。
    • 食物不能和身体生成位置重合。
  3. 生成蛇,开始移动。

    • 蛇碰到墙壁,计算出穿过墙的范围.
    • 蛇碰到自己的身体,Game Over .
    • 蛇吃到食物,长度加一,并生成新的食物
  4. 监听键盘事件。

    • 对上下左右移动做出反应。

代码实现(也可以查看 github

html (index.html)

<!DOCTYPE html>
<html lang="en">
<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>Document</title>
    <link rel="stylesheet" href="./style/index.css">
</head>
<body>
    <div class="wrap">
        <div id="container">
        </div>
    </div>
    <script src="./js/index.js" ></script>
</body>
</html>

css (./style/index.css)

* {
    margin: 0;
    padding: 0;
}

.wrap{
    display: flex;
    justify-content: center;
    align-content: center;
}

#container{
    width: 100%;
    text-align: center;
}

#container div{
    /* width: 20px;
    height: 20px; */
    float: left;
    border: 1px solid #000;
}

js(./js/index.js)

    (function(self){

    function GreedySnake(gridXN,gridYN){
        // 地图
        this.gridXN = gridXN >= 10 ? gridXN : 10; 
        this.gridYN = gridYN >= 10 ? gridYN : 10;
        this.gridSize = 20; //每个格子的固定大小
        this.map = []; //保存地图中dom对象的二维数组
        this.container = document.getElementById('container');
        

        // 食物属性
        this.food = null;
        this.foodX = null;
        this.foodY = null;

        // 蛇
        this.snake = [];//将蛇抽象成一个二维列表,列为身体长度,排为坐标数组 
        this.snakeLen = null ; //默认蛇长为3
        // 初始蛇头坐标
        this.snakeHead = []

        // 蛇的移动方向
        this.direction = 'top'

        // 速度
        this.speed = 1 ;

        this.timer = null
    }

    GreedySnake.prototype.init = function(){
        this.initMap();
        this.createSnake();
        this.createFood();
        this.snakeMove()
        this.listenKeyBoardEvents()
    }

    GreedySnake.prototype.restart = function(gridXN,gridYN){
    
        // 地图
        this.gridXN = null
        this.gridYN = null
        this.gridSize = null
        this.map = null
        this.container = null
        
        // 食物属性
        this.food = null;
        this.foodX = null;
        this.foodY = null;

        // 蛇
        this.snake = null;
        this.snakeLen = null; 
        // 初始蛇头坐标
        this.snakeHead = null;

        // 蛇的移动方向
        this.direction = null;

        // 速度
        this.speed = null;

    }


    // 初始化地图
    GreedySnake.prototype.initMap = function(){
        var gridYN = this.gridYN,
            gridXN = this.gridXN,
            w = gridXN * this.gridSize + gridXN + 1 ,
            h = gridYN * this.gridSize + gridYN + 1;
        
        this.container.style.width = w + "px";
        this.container.style.height = h  + 'px';

        for(let y = 0 ; y < gridXN ; y++){
            this.map[y] = [] //初始化二维数组
            for(let x = 0 ; x < gridYN ; x++){
                this.createGrid(x,y)
            }
        }   
    }
    // 创建每一个格子,x轴格子索引,y轴格子索引
    GreedySnake.prototype.createGrid = function(idxX,idxY){
        // 当idxY > 0 时,对应的grid的向左移动1px,将格子之间的线重合;
        // 当idxX > 0 时,对应的grid的向上移动1px,将格子之间的线重合;
        let grid = document.createElement('div');
        grid.style.width = this.gridSize + 'px';
        grid.style.height = this.gridSize + "px";
        this.map[idxY][idxX] = grid
        if(idxX > 0 ){
            grid.style.marginLeft = '-1px';
        }
        if(idxY > 0 ){
            grid.style.marginTop = '-1px';
        }
        this.container.appendChild(grid);
    }

    // 创建食物:原理就是根据在范围内的格子数,生成两个随机数,作为元素的坐标
    GreedySnake.prototype.createFood = function(){
        var gridYN = this.gridYN,
            gridXN = this.gridXN,
            temp = null ,
            flag = true;
        this.foodX = Math.floor(Math.random() * gridXN); //缓存
        this.foodY = Math.floor(Math.random() * gridYN);

        for(var i = 0 ; i<this.snake.length ; i++){
            temp = this.snake[i]
            // 检测食物是否和蛇的身体重合
            while(temp[0] == this.foodY && temp[1] == this.foodX){
                flag = false
                this.createFood();
            }
        }
        if(flag){
            this.food = this.map[this.foodY][this.foodX];
            this.food.style.backgroundColor = '#f00';
        }
    }

    // 生成蛇
    GreedySnake.prototype.createSnake = function(){
        this.snakeLen = 3 ; //默认蛇长为3
        let i = this.snakeLen - 1;
        for(i ; i >= 0; i--){
            this.snake.push([this.gridYN - 1 - i ,this.gridXN - 1 - 3])
            this.map[this.gridYN - 1 - i][this.gridXN - 1 - 3].style.backgroundColor = 'blue'
        }
        // 初始蛇头坐标
        this.snakeHead = this.snake[0]
        // console.log(this.snake)
    }
    
    // 开始移动,移动后蛇头坐标 +-1 
    GreedySnake.prototype.snakeMove = function(){
        let _this = this,
            sH = this.snakeHead,
            y = null,
            x = null,
            tempH = null,
            last = null,
            alive = true

        function common(){
            
            _this.map[tempH[0]][tempH[1]].style.backgroundColor = 'blue'
            _this.snake.unshift(tempH);
            _this.snakeHead = tempH;
            // 检测蛇头是否和蛇的身体相撞
            for(var i = 1 ; i < _this.snake.length;i++){
                if(_this.snakeHead[0] == _this.snake[i][0] && _this.snakeHead[1] ==  _this.snake[i][2]){
                    alert('GAME OVER');
                    alive = false
                    return false
                }
            }
            // 当蛇吃到食物后再重新创建食物
            if(sH[0] === _this.foodY && sH[1] === _this.foodX){
                _this.createFood()
                return false
            }
            last = _this.snake.pop();
            _this.map[last[0]][last[1]].style.backgroundColor = ''
        }

        switch(this.direction){
            case 'top':
                y = sH[0] //缓存
                tempH = [--y , sH[1] ];
                if(tempH[0] < 0){
                    tempH = [this.gridYN - 1 , sH[1]]
                }
                common();
                break;
            case 'bottom':
                y = sH[0]
                tempH = [++y , sH[1] ];
                // 边界判断
                if(tempH[0] > this.gridYN - 1){
                    tempH = [ 0 , sH[1]]
                }
                common()
                break;
            case 'left':
                x = sH[1]
                tempH = [sH[0] , --x ];
                if(tempH[1] < 0){
                    tempH = [ sH[0] , this.gridXN - 1]
                }
                common()
                break;
            case 'right':
                x = sH[1]
                tempH = [sH[0] , ++x ];
                if(tempH[1] > this.gridXN - 1){
                    tempH = [  sH[0] ,  0 ]
                }
                common()
                break;
        }

        alive && setTimeout(function(){
            _this.snakeMove()
        },500 / this.speed)
    }

    // 注册键盘事件
    GreedySnake.prototype.listenKeyBoardEvents = function(){
        let _this = this
        self.onkeyup = function(e){
            let dir = _this.direction
            switch(e.keyCode){
                case 37:
                    if(dir != 'right'){
                       _this.direction = 'left';
                    }
                    break;
                case 38:
                    if(dir != 'bottom'){
                        _this.direction = 'top';
                    }
                    break;
                case 39:
                    if(dir != 'left'){
                        _this.direction = 'right';
                    }
                    break;
                case 40:
                    if(dir != 'top'){
                        _this.direction = 'bottom';
                    }
                    break;
            }
        }
    }

    GreedySnake.prototype.print = function(){
        return this.map
    }
    
    // 参数含义:x轴,y轴上的格子数量
    var greedySnake = new GreedySnake(20,20);
    greedySnake.init()
})(window)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值