js:面向对象:贪吃蛇小游戏(生成地图,边界碰撞,刷新食物,障碍物产生)

目录

index.html

Game.js

Snake.js

Block.js

Food.js

Map.js


 

1. 贪吃蛇项目

我们决定使用面向对象的方式来书写该游戏

因为面向对象是最适合书写游戏的

我们将这个游戏看成是一个类(Game类)

既然把游戏看成是一个类,它可以拥有属性和方法

有一个地图属性

有一个蛇属性

有一个食物属性

有一个障碍物属性

2. 我们可以把地图看成一个类

有一个行属性                                有一个列属性

有宽度属性(总宽度)                      有高度属性(总高度)

有一个数组属性是一个二维数组,里面存储的都是一个小方格元素

我们可以把蛇也看成是一个类

有一个数组属性,存储蛇的每一节身体

有一个方向属性,用于控制蛇的方向

蛇增长的方法

蛇转向的方法

3. 我们可以把食物看成一个类

有一个x属性

有一个y属性

有一个图片属性

我们可以把障碍物看成一个类

数组属性

index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hungry Snake</title>
    <style>
        .box {
            margin: 50px auto;
            border: 1px solid #ccc;
        }

        .row {
            display: flex;
        }

        .col {
            border: 1px solid #ccc;
            flex: 1;
            background-size: cover;
        }
    </style>
</head>

<body>

    <script src="./js/Block.js"></script>
    <script src="./js/Food.js"></script>
    <script src="./js/Map.js"></script>
    <script src="./js/Snake.js"></script>
    <script src="./js/Game.js"></script>
    <script>
        //实例化各个类
        var block = new Block('./img/block.jpg');
        //行的数量、列的数量、总宽、总高
        // var map = new Map(30, 300);
        var map = new Map(10, 10, 300, 300);
        // var map = new Map(20, 20, 600, 600);
        var food = new Food(6, 2, './img/food.jpg');
        var Snake = new Snake({
            head: ['./img/1.jpg', './img/2.jpg', './img/3.jpg', './img/4.jpg'],
            body: ['./img/5.jpg'],
            tail: ['./img/8.jpg', './img/9.jpg', './img/6.jpg', './img/7.jpg']
        });
        //将上面四个属性传递给Game类
        var game = new Game(Snake, map, food, block, 500);

    </script>
</body>

</html>

Game.js

//游戏类
//1.负责渲染 2. 游戏交互(判断游戏结束)


function Game(snake, map, food, block, time) {
    //存储属性
    this.snake = snake;
    this.map = map;
    this.food = food;
    this.block = block;
    //获取句柄
    this.timebar = null;
    //循环的时间
    this.time = time || 1000;
    //游戏是否可以正常进行
    this.start = true;
    //初始化 
    this.init();
}
//实现初始化方法
Game.prototype.init = function () {
    //将地图初始化
    // this.map.init();
    this.renderMap();
    //渲染食物
    this.renderFood();
    //绘制障碍物
    this.renderBlock();
    //绘制蛇
    this.renderSnake();

    //启动游戏
    this.state();
    //绑定事件
    this.bindEvent();
}
//渲染地图
Game.prototype.renderMap = function () {
    this.map.init();
}

//渲染食物
Game.prototype.renderFood = function () {
    //根据食物的横纵坐标,在地图中找到对应的元素,设置其背景图片
    this.map.arr[this.food.y][this.food.x].style.backgroundImage = 'url(' + this.food.img + ')';
    // console.log(this.map, this.food, this.food.y)
    // console.log(this.map.arr[this.food.y][this.food.x]);
}

//渲染障碍物
Game.prototype.renderBlock = function () {
    // console.log(Game.prototype);
    //遍历障碍物数组成员,将其一一绘制出来
    for (var i = 0, len = this.block.arr.length; i < len; i++) {
        //将其渲染//y对应的是行,x:列
        //缓存障碍物
        var item = this.block.arr[i];
        this.map.arr[item.y][item.x].style.backgroundImage = 'url(' + this.block.img + ')'
    }
}

//渲染蛇
Game.prototype.renderSnake = function () {
    //特殊绘制,头 尾
    var head = this.snake.arr[0];
    var tail = this.snake.arr[this.snake.arr.length - 1];
    //绘制头
    this.map.arr[head.y][head.x].style.backgroundImage = 'url(' + this.snake.headImge + ')';
    //绘制身体,从第二张绘制到倒数第二张
    for (var i = 1, len = this.snake.arr.length - 1; i < len; i++) {
        //缓存身体元素
        var body = this.snake.arr[i];
        //绘制身体
        this.map.arr[body.y][body.x].style.backgroundImage = 'url(' + this.snake.bodyImge + ')';
    }
    //绘制尾部
    this.map.arr[tail.y][tail.x].style.backgroundImage = 'url(' + this.snake.tailImge + ')';
}

//清空地图
Game.prototype.clear = function () {
    //通过地图对象清空
    this.map.clear()
}

//启动游戏
Game.prototype.state = function () {
    //缓存thi;s
    var me = this;
    //启动定时器
    this.timebar = setInterval(function () {
        //开始移动蛇
        me.snake.move();
        //蛇移动之后,开始判断边界
        me.checkMap();
        //检测碰撞障碍物
        me.checkBlock();
        //检测是否碰到身体
        me.checkSnake();
        //检测是否吃到食物
        me.checkFood();
        //如果游戏结束,停止绘制游戏
        //如果游戏正常运行,正常绘制游戏
        if (me.state) {
            //清空后再渲染
            me.clear();
            //渲染墙和食物
            me.renderBlock();
            me.renderFood();
            //重新渲染
            me.renderSnake();
        }
    }, this.time)
}

//绑定事件,控制蛇的移动
Game.prototype.bindEvent = function () {
    //缓存this
    var me = this;
    //监听键盘事件
    document.onkeydown = function (e) {
        // console.log(e.keyCode);
        //改变运行方向
        me.snake.change(e.keyCode);
    }
}

//游戏结束
Game.prototype.gameOver = function () {
    //终止游戏,停止移动循环
    clearInterval(this.timebar);
    //游戏结束
    this.state = false;
    //提示用户GG
    alert('Victory QAQ  ' + '您的分数是:' + this.snake.arr.length);
    // console.log('Victory', '您的分数:' + this.snake.arr.length);
}
//检测边界
Game.prototype.checkMap = function () {
    //判断蛇的头部,及位置
    var head = this.snake.arr[0];
    //般的是否超出边界
    if (head.x < 0 || head.y < 0 || head.x >= this.map.col || head.y >= this.map.row) {
        this.gameOver();
    }
    // console.log(head.x < 0 || head.y < 0 || head.x > this.map.col || head.y > this.map.row);
}

//检测是否碰撞障碍物
Game.prototype.checkBlock = function () {
    //判断头部位置
    var head = this.snake.arr[0];
    //遍历障碍物
    for (var i = 0; i < this.block.arr.length; i++) {
        //比较头部与障碍物是否在同一个位置
        //获取障碍物位置
        var block = this.block.arr[i];
        //开始比较(即:x,y相同)
        if (block.x === head.x && block.y === head.y) {
            this.gameOver();
            //打断执行//经测试好像可以省略
            return;
        }
    }
}

//这是两个对象,对两个对象的判断,需要放到Game里面判断
//蛇是否吃的食物
Game.prototype.checkFood = function () {
    //蛇头的位置与食物的位置 是否相等
    if (this.snake.arr[0].x === this.food.x && this.snake.arr[0].y === this.food.y) {
        //恰到饭了,蛇变长,增加身体长度(个数)
        //这是一个对象(蛇)的变化,放到蛇的对象内描写
        this.snake.growUp();
        //重置食物
        this.resetFood();
    }
}

//重置食物
Game.prototype.resetFood = function () {
    //随机一个食物的位置
    let x = parseInt(Math.random() * this.map.col);
    let y = parseInt(Math.random() * this.map.row);
    //判断坐标是否在:1.食物不能出现在墙体中;2.食物不能出现在蛇的身体上
    for (var i = 0; i < this.block.arr.length; i++) {
        //1.食物不能出现在墙体中 //缓存一个障碍物
        let block = this.block.arr[i];
        //判断坐标是否重合
        if (block.x === x && block.y === y) {
            //在墙体中,所以要重新随机食物
            this.resetFood();
            //中断执行
            return;
        }
    }
    //2.食物不能出现在蛇的身体上
    for (var i = 0; i < this.snake.arr.length; i++) {
        //缓存蛇的部位
        let snake = this.snake.arr[i];
        //判断坐标是否重合
        if (snake.x === x && snake.y === y) {
            //在墙体中,所以要重新随机食物
            this.resetFood();
            //中断执行
            return;
        }
    }
    //更改食物位置
    //要对食物内部的数据进行修改,所以放到Food.js文件中
    this.food.reset(x, y);
    console.log(x, y);
}

//碰撞身体
Game.prototype.checkSnake = function () {
    //获取头部
    var head = this.snake.arr[0];
    //从第二个开始遍历身体
    for (var i = 1; i < this.snake.arr.length; i++) {
        // //缓存身体
        // var body = this.snake.arr[i];
        //获取身体部件位置
        var body = this.snake.arr[i];
        if (head.x === body.x && head.y === body.y) {
            this.gameOver();
            //打断执行//经测试好像可以省略
            return;
        }
    }
}

// //获取键盘四个箭头的键码
// window.onkeydown = function (e) {
//     console.log(e.keyCode);
// }

Snake.js

//蛇的类
function Snake(img) {
    //存储坐标
    this.arr = [
        // { x: 7, y: 4 },
        { x: 6, y: 4 },
        { x: 5, y: 4 },
        { x: 4, y: 4 },
        // { x: 3, y: 4 },
        // { x: 2, y: 4 },
    ];
    //存储图片 
    this.img = img;
    // this.headImage = img.head;
    // this.bodyImage = img.body;
    // this.tailImage = img.tail;
    //蛇的方向//左:37, 上:38, 右:39, 下:40
    //根据方向确定头部图片(direction-37).jpg就是数组中,图片的序号
    this.direction = 39;//为什么要等于39啊啊啊啊啊
    this.headImge = this.img.head[this.direction - 37];
    this.bodyImge = this.img.body;
    this.tailImge = this.img.tail[this.direction - 37];
    // console.log(this.direction);
}

//移动蛇
Snake.prototype.move = function () {
    //获取新的头部位置
    // var head = this.arr[0];//不能直接引用
    var head = { x: this.arr[0].x, y: this.arr[0].y };
    //👆:赋值了一个新的头部//switch是引用类型的,新的头部和原来的头部不能引用同一个对象
    //判断方向
    switch (this.direction) {
        //向左移动,x-
        case 37:
            head.x -= 1;
            break;

        case 38:
            //向上移动:y-1
            head.y -= 1;
            break;
        case 39:
            //向右移动:x+1
            head.x += 1;

            break;
        case 40:
            //向下移动:y+1
            head.y += 1;
            break;
        default:
            break;
    }
    //删除尾部,更新头部
    this.arr.pop();
    this.arr.unshift(head);
    //尾部的图片,始终是受到倒数二个元素影响
    var tail = this.arr[this.arr.length - 1];
    var second = this.arr[this.arr.length - 2];
    //判断,tail与second的位置关系(x与y比较),并改变尾部图片方向
    if (tail.x === second.x) {
        //比较垂直方向的
        if (tail.y > second.y) {
            //向上移动
            this.tailImge = this.img.tail[1];
        } else {
            //说明向下移动
            this.tailImge = this.img.tail[3];
        }
    } else {
        //比较水平方向的
        if (tail.x > second.x) {
            //向左移动
            this.tailImge = this.img.tail[0];
        } else {
            //向右移动
            this.tailImge = this.img.tail[2];
        }
    }
    this.lock = false;
}

//改变方向
Snake.prototype.change = function (code) {
    //如果锁住了,不能改变方向
    //设置节流锁:的目的,确保每一步都按时按量执行,不会插队(覆盖)别的指令
    //阻止了一个bug:先按一个方向的键,然后迅速按下垂直方向的键,
    //会导致直接指向后按下的键及其指令,且会覆盖前面的指令与限制条件(不能同方向回头)
    if (this.lock) {
        return;
    }
    //锁住
    this.lock = true;
    //改变方向
    //向左右移动只能上下改变移动方向,//向上下移动只能左右改变移动方向
    //获取方向键值之差
    var num = Math.abs(code - this.direction);
    //同向或相反方向不能改变方向
    if (num === 0 || num === 2) {
        return;
    }
    //可以改变方向
    this.direction = code;
    //更改头部图片方向
    this.headImge = this.img.head[this.direction - 37];
    this.bodyImge = this.img.body;
    this.tailImge = this.img.tail[this.direction - 37];
}

//让蛇成长
Snake.prototype.growUp = function () {
    //获取尾部。蛇变长了
    var tail = this.arr[this.arr.length - 1];
    //尾部添加
    this.arr.push(tail)
}


Block.js

//障碍物类
function Block(img) {
    //障碍物绘制图片
    this.img = img;
    //障碍物数组
    this.arr = [
        //每一个成员代表一个障碍物,保存它的位置
        { x: 3, y: 6 },
        { x: 4, y: 6 },
        { x: 5, y: 6 },
        { x: 6, y: 6 },
        { x: 7, y: 6 },
        { x: 8, y: 6 }
    ]
}

Food.js

//食物类
function Food(x, y, img) {
    this.x = x;
    this.y = y;
    this.img = img;
}
//重置位置
Food.prototype.reset = function (x, y) {
    //更新食物坐标
    this.x = x;
    this.y = y;
}

Map.js

/**
 *  地图类
 *      @row:行的数量       @col:列的数量
 *      @width:总宽度       @height:总高度
 * 
 * **/

function Map(row, col, width, height) {
    this.row = row;
    this.col = col;
    this.height = height;
    this.width = width;
    //二维数组
    this.arr = [];
    //定义容器元素
    this.dom = document.createElement('div');
}
//初始化//绘制地图
Map.prototype.init = function () {
    //遍历行列
    for (var i = 0; i < this.row; i++) {
        //创建行元素
        var rowDom = document.createElement('div');
        //设置类
        rowDom.className = 'row';
        rowDom.style.height = this.height / this.row + 'px';
        //定义行数组
        var rowArr = [];
        //遍历改行的每列
        for (var j = 0; j < this.col; j++) {
            //创建每一个列元素
            var colDom = document.createElement('div');
            //设置类
            colDom.className = 'col';
            //将单元格放入行元素中
            rowDom.appendChild(colDom);
            //在数组中,存储单元格的映射
            rowArr.push(colDom);
        }
        //将行元素渲染,并存储
        this.dom.appendChild(rowDom);
        //存储行数组
        this.arr.push(rowArr);
    }
    //设置容器元素类
    this.dom.className = 'box';
    this.dom.style.width = this.width + 'px';
    this.dom.style.height = this.height + 'px';
    //上树
    document.body.appendChild(this.dom)
}

//清空地图
Map.prototype.clear = function () {
    //清空的本质就是删除每一个元素的背景
    for (var i = this.arr.length - 1; i >= 0; i--) {
        //遍历列
        for (var j = this.arr.length - 1; j >= 0; j--) {
            //清空背景
            this.arr[i][j].style.backgroundImage = ''
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值