JS 面向对象-----贪吃蛇


一、效果展示

在这里插入图片描述

二、实现过程

1.前期样式

代码如下(示例):

<link rel="stylesheet" href="css/index.css">
<div class="content">
        <div class="btn startBtn"><button></button></div>
        <div class="btn pauseBtn"><button></button></div>
        <div id="snakeWrap"></div>
    </div>
    <script src="js/index.js"></script>
* {
    margin: 0;
    padding: 0;
    outline: none;
    border: none;
}

.content {
    position: relative;
    width: 640px;
    height: 640px;
    margin: 50px auto;
}

.btn {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: 2;
}

.btn button {
    border: none;
    background-size: 100% 100%;
    cursor: pointer;
    outline: none;
    background-color: #FCE4EC;
    position: absolute;
    left: 50%;
    top: 50%;
}

.startBtn button {
    margin-top: -75px;
    margin-left: -100px;
    width: 200px;
    height: 150px;
    background-size: 200px 150px;
    background-image: url(../images/btn1.gif);
}

.pauseBtn {
    display: none;
}

.pauseBtn button {
    margin-top: -35px;
    margin-left: -35px;
    width: 70px;
    height: 70px;
    background-size: 100% 100%;
    background-image: url(../images/btn4.png);
}


/* 设置蛇的范围和样式 */

#snakeWrap {
    position: relative;
    width: 600px;
    height: 600px;
    background: #FCE4EC;
    border: 20px solid #F8BBD0;
}

.snakeHead {
    background-image: url(../images/snake.png);
    background-size: cover;
}

.snakeBody {
    background-color: #9CCC65;
    border-radius: 50%;
}

.food {
    background-size: cover;
    background-image: url(../images/food2.png);
    background-position: 0 -3px;
}                                                           

2. js样式搭建

2.1 项目分析

  • 把蛇要走的路径看成一个个方格,需要一个构造方块的函数;
  • 蛇在吃掉食物后身体会增长,需要一个构造新增蛇身的函数;
  • 蛇吃的食物会随机出现,需要一个构造食物的函数;
  • 贪吃蛇本身是一个游戏需要开始和结束、暂停,需要一个构造游戏过程的函数;

2.2 构造方格函数及方法

// 把整个蛇移动的范围比作一个个由方块组成的,蛇在方块中行动。
var sw = 20,
    sh = 20,
    tr = 30,
    td = 30;

var snake = null; // 一个蛇的实例
var food = null; // 食物的实例
var game = null; // 游戏的实例
// 构造一个方块函数
function Square(x, y, classname) {
    this.x = x * sw;
    this.y = y * sh;
    this.class = classname;
    
    this.viewContent = document.createElement('div');
    this.viewContent.className = this.class;
    this.parent = document.getElementById('snakeWrap');
}
// 创建方法 作用在原型上 
Square.prototype.create = function() {
    this.viewContent.style.position = 'absolute';
    this.viewContent.style.width = sw + 'px';
    this.viewContent.style.height = sh + 'px';
    this.viewContent.style.left = this.x + 'px';
    this.viewContent.style.top = this.y + 'px';

    this.parent.appendChild(this.viewContent);
}
Square.prototype.remove = function() {
        this.parent.removeChild(this.viewContent);
}

2.3 构造蛇身

    //  蛇 在移动过程中是蛇头和蛇尾的移动
function Snake() {
    this.head = null; // 保存一个蛇头的信息
    this.tail = null; // 保存一个蛇尾的信息
    this.pos = []; // 存储蛇身上的每一个方块的位置  二维数组
    this.directionNum = {
        left: { x: -1, y: 0, rotate: 180 }, // 蛇头需要旋转
        right: { x: 1, y: 0, rotate: 0 },
        up: { x: 0, y: -1, rotate: -90 },
        down: { x: 0, y: 1, rotate: 90 }
    }; // 存储蛇走的方向 
}
// 蛇对象的初始化
Snake.prototype.init = function() {
        var snakeHead = new Square(2, 0, 'snakeHead');
        snakeHead.create();
        this.head = snakeHead; // 存储蛇头信息
        this.pos.push([2, 0]); // 存储位置
        var snakebody1 = new Square(1, 0, 'snakeBody');
        snakebody1.create();
        this.tail = snakebody1; // 存储蛇头信息
        this.pos.push([1, 0]); // 存储位置
        var snakebody2 = new Square(0, 0, 'snakeBody');
        snakebody2.create();
        this.tail = snakebody2; // 存储蛇头信息
        this.pos.push([0, 0]); // 存储位置
        // 使蛇头和蛇尾形成链表关系
        snakeHead.last = null;
        snakeHead.next = snakebody1;

        snakebody1.last = snakeHead;
        snakebody1.next = snakebody2;

        snakebody2.last = snakebody1;
        snakebody2.next = null;
        // 设置蛇默认开始的方向
        this.direction = this.directionNum.right;
    }
    // 获取蛇头下一个位置对应的元素 要根据元素对象不同做不同的事情
Snake.prototype.getNextPos = function() {
        var nextPos = [
            this.head.x / sw + this.direction.x,
            this.head.y / sh + this.direction.y
        ];
        // 当下一个是自己 游戏结束
        var selfCollied = false;
        this.pos.forEach(function(value) {
            if (value[0] == nextPos[0] && value[1] == nextPos[1]) {
                selfCollied = true;
            }
        });
        if (selfCollied) {
            console.log('撞到自己了');
            this.strategies.die();
            return;
        }
        // 撞到墙 游戏结束
        if (nextPos[0] < 0 || nextPos[1] < 0 || nextPos[0] > td - 1 || nextPos[1] > tr - 1) {
            console.log('撞墙了');
            this.strategies.die();
            return;
        }
        // 下一个是食物 吃掉并加长
        if (food && food.pos[0] == nextPos[0] && food.pos[1] == nextPos[1]) {
            // 说明下一个是食物
            this.strategies.eat.call(this);

            return;
        }
        // 什么都没有直接走
        this.strategies.move.call(this);



    }
    // 处理碰撞后的事
Snake.prototype.strategies = {
    move: function(format) { // format 用来决定删不删蛇尾
        // 创建一个新身体
        var newBody = new Square(this.head.x / sw, this.head.y / sh, 'snakeBody');
        // 更新链表的关系
        newBody.next = this.head.next;
        newBody.next.last = newBody;
        newBody.last = null;
        this.head.remove(); // 删除旧蛇头
        newBody.create(); // 把新身体插进旧蛇头
        // 创建新蛇头 下一个点
        var newHead = new Square(this.head.x / sw + this.direction.x, this.head.y / sh + this.direction.y, 'snakeHead')
            // 更新关系
        newHead.next = newBody;
        newHead.last = null;
        newBody.last = newHead;
        newHead.viewContent.style.transform = 'rotate(' + this.direction.rotate + 'deg)';
        newHead.create();
        // 蛇身上的每一个坐标也要更新
        this.pos.splice(0, 0, [this.head.x / sw + this.direction.x, this.head.y / sh + this.direction.y]);
        this.head = newHead;
        // 删掉蛇尾巴
        // format 为false 表示 需要删除 除了吃之外的操作
        //    传了参数就表示吃
        if (!format) {
            this.tail.remove();
            this.tail = this.tail.last;
            this.pos.pop();
        }
    },
    eat: function() {
        this.strategies.move.call(this, true);
        createFood();
        // 在吃到食物后使蛇的速度加快
        clearInterval(game.timer);
        if (timeNum <= 20 && game.score > 10) {
            timeNum = 20;
        } else {
            timeNum -= 10;
        }
        game.timer = setInterval(function() {
            snake.getNextPos();
        }, timeNum);
        game.score++;
    },
    die: function() {
        console.log('die');
        game.over();
    }
}

2.4 构造食物

// 创建食物
function createFood() {
    // 随机的坐标
    var x = null;
    var y = null;
    var include = true; // true 表示食物在蛇身上继续循环 false 表示不在蛇身上 不循环了
    // 利用循环跳出的条件
    while (include) {
        x = Math.round(Math.random() * (td - 1));
        y = Math.round(Math.random() * (tr - 1));
        snake.pos.forEach(function(value) {
            if (value[0] != x && value[1] != y) { //随机坐标在蛇身上没有找到
                include = false;
            }
        });

    }
    // 生成食物
    food = new Square(x, y, 'food');
    food.pos = [x, y]; // 存储生成食物的坐标 与蛇头作对比
    var fooddom = document.querySelector('.food');
    if (fooddom) {
        fooddom.style.left = x * sw + 'px';
        fooddom.style.top = y * sh + 'px';
    } else {
        food.create();
    }

}

2.5 构造游戏过程

function Game() {
    this.timer = null;
    this.score = 0;
}
// 开启游戏
Game.prototype.init = function() {
    snake.init();
    // snake.getNextPos();
    createFood();
    document.onkeydown = function(e) {
        // 按下左键时,蛇不能正在往右走
        if (e.which == 37 && snake.direction != snake.directionNum.right) {
            snake.direction = snake.directionNum.left;
        } else if (e.which == 38 && snake.direction != snake.directionNum.down) {
            snake.direction = snake.directionNum.up;
        } else if (e.which == 39 && snake.direction != snake.directionNum.left) {
            snake.direction = snake.directionNum.right;
        } else if (e.which == 40 && snake.direction != snake.directionNum.up) {
            snake.direction = snake.directionNum.down;
        }
    }
    this.start();
}
var timeNum = 200;
Game.prototype.start = function() {
        this.timer = setInterval(function() {
            snake.getNextPos();
        }, timeNum);
    }
    // 暂停游戏
Game.prototype.pause = function() {
        clearInterval(this.timer);
    }
    // 结束游戏
Game.prototype.over = function() {
    clearInterval(this.timer);
    alert('你的得分为:' + game.score);
    var snakeWrap = document.getElementById('snakeWrap');
    snakeWrap.innerHTML = '';
    timeNum = 200;
    snake = new Snake();
    game = new Game();
    var startBtn = document.querySelector('.startBtn');
    startBtn.style.display = 'block';
}

3. 执行过程


snake = new Snake();
game = new Game();
var startBtn = document.querySelector('.startBtn button');
startBtn.addEventListener('click', function() {
    startBtn.parentNode.style.display = 'none';
    game.init();
});
var pauseBtn = document.querySelector('.pauseBtn button');
snakeWrap.addEventListener('click', function() {
    game.pause();
    startBtn.parentNode.style.display = 'none';
    pauseBtn.parentNode.style.display = 'block';
});
pauseBtn.addEventListener('click', function() {
    game.start();
    pauseBtn.parentNode.style.display = 'none';

});

利用面向对象的方法,实现贪吃蛇。 1. 利用面向对象的思想实现——一个食物对象、一个蛇对象、一个游戏总控对象。 2. 在使用××.prototype= {}重原型对象的时候,一定要加上一句constructor:该对象。不然会造成实例化出来的实例的constructor为object。 3. 在underscore中,使用_.random(a,b)即可获得a-b中的一个随机数。 4. 在求食物的随机位置的时候,用到了panel.clientHeight/this.height - 1) * this.height。 原理是使用盒子的高度/小球的高度,可以算得最多放多少个小球。因为要控制小球不能超过边界,所以总数量要减去1,数量×高度即为随机位置的最大值。 5. 在蛇对象中,用body数组存放蛇身体每一个部分对象。蛇的绘制过程就是遍历body,在面板上绘制。 6. 蛇的移动分为两部分。 ① 蛇节移动到前一个蛇节的位置。直到蛇头后一个蛇节移动到蛇头的位置。 ② 根据direction判断蛇头如何移动。 注意:在游戏绘制的过程中,界面的每一次绘制都要**删除**之前的绘制,不然会叠加到一起。 7. 在蛇的闭包中建一个局部数组,存储蛇对象,可以更加方便的删除操作。 8. 只有在原型对象中的方法和属性,外界是可以调用的。 9. 蛇的移动(动画)必然需要定时器协助。定时器的时间,即象征着刷新速度,也就是难度。 10. this所在的函数在哪一个对象中,this就指向谁。单独一个函数的时候,如果调用之前对象的this,需要备份指针(将对象的this赋值给另一个变量)。 11. JavaScript原生的键盘按下事件(keydown) 中,事件有一个keyCode属性,其值代表按下的键。其中:37—left、38—top、39—right、40—bottom。 12. 边界控制。通过判断蛇头与最大X和Y的关系,判断是否碰到边界。 13. confirm()方法用于显示一个带有指定消息和确认及取消按钮的对话框。 14. window.location.reload(); 重新加载当前文档 15. window.close() 方法用于关闭浏览器窗口。 16. 与食物的碰撞检测:如果蛇头和食物坐标重叠,将蛇尾添加到body中。并重新绘制一个食物点,将之前的食物删掉。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值