美文网首页
JS高级特性简单实现—贪吃蛇

JS高级特性简单实现—贪吃蛇

作者: amanohina | 来源:发表于2020-12-21 19:05 被阅读0次

搭建页面

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link rel="stylesheet" href="css/index.css">
</head>
<body>
  <div class="map" id="map"></div>
  <!-- 引入多个js文件 -->
  <script src="js/tools.js"></script>
  <script src="js/food.js"></script>
  <script src="js/snake.js"></script>
  <script src="js/game.js"></script>
  <script src="js/main.js"></script>
</body>
</html>

index.css

* {
margin:0;
padding:0;
}
.map {
   position:relative;
   width:800px;
   height:600px;
   background-color:lightgray;
}

分析对象

  • 游戏对象
  • 蛇对象
  • 食物对象

食物对象

1.创建构造函数Food,设置属性

  • x
  • y
  • width
  • height
  • color
  1. 通过原型设置方法
    • render 随机创建一个食物对象,并且输出到map上

3.通过自调用函数,进行封装,通过window暴露Food对象

// 缩小定义的构造函数的作用域
// 匿名函数,自调用函数.IIFE,关住作用域
(function(){
    var ps = "absolute";
    // 创建食物的构造函数
function Food(option){
    // 避免传入的参数的数据类型不对,或者没有传参
    option = option instanceof Object ? option : {};
    // 传入的数据可能是类似于数组等对象,需要进一步进行判断
    this.width = option.width || 20;
    this.height = option.height || 20;
    this.x = option.x || 20;
    this.y = option.y || 20;
    this.color = option.color || "green";
    // 增加一个属性,存储将来这个对象渲染出来的所有的div元素
    this.elements = [];
}
// 渲染一个元素到页面之上,需要添加到原型对象的方法中
// 全局变量设置不变的属性,更加优化
Food.prototype.render = function(map){
    // 创建一个新的div元素
    var ele = document.createElement("div");
    // 每次设置样式之前,随机获取一个x和y
    this.x = Tools.getRandom(0,map.clientWidth / this.width - 1) * this.width;
    this.y = Tools.getRandom(0,map.clientHeight / this.height - 1) * this.height;
    // 给生成的元素添加对应的样式
    ele.style.width = this.width + "px";
    ele.style.height = this.height + "px";
    ele.style.left = this.x + "px";
    ele.style.top = this.y + "px";
    ele.style.background = this.color;
    ele.style.position = ps;
    // 让新元素添加到指定的父级中
    map.appendChild(ele);
    // 将新元素添加到数组中,方便后期调用删除
    this.elements.push(ele);
};
// 删除一个食物div元素的方法
Food.prototype.remove = function(map,i){
    // 通过一些方法,获取删除食物的下标
    //将元素从html结构中删除
    map.removeChild(this.elements[i]);
    // 将元素从数组中删除
    this.elements.splice(i,1);
};
// 利用windows对象,暴露Food函数,可以给外部使用
window.Food = Food;
})();

// 测试
// 获取父级地图元素
var map = document.getElementById("map");   
var food = new Food();
food.render(map);
// 在外面调用到这个Food构造函数

蛇对象

  • 创建Snake构造函数,设置属性
    1.width:蛇节宽度,默认20
    2.height:蛇节高度,默认20
    3.body:数组,蛇的头部和身体,第一个位置是蛇头
    4.direction:蛇运动的方向,默认right,可以是left,bottom,top
  • 通过原型设置方法
    • render:随机创建一个蛇对象,输出到map
  • 通过自调用函数,封装,通过window暴露Snake
// 使用自调用函数关住作用域
(function(){
    // 全局变量
    ps = "absolute";
    // 创建蛇的构造函数
    function Snake(option){
        // 避免传入的参数的数据类型不对,或者没有传参
        option = option instanceof Object ? option : {};
        // 给对象添加属性
        //蛇节的宽高
        this.width = option.width || 20;
        this.height = option.height || 20;
        // 设置蛇身数据
        this.body = [
            {x : 3,y: 2,color:"red"},
            {x : 2,y: 2,color:"blue"},
            {x : 1,y: 2,color:"blue"},
        ];
        // 设置蛇的移动方向
        this.direction = "right";
        // 添加一个元素的数组,存储所有的渲染的元素
        this.elements = [];
    }
    // 将元素渲染到页面上的方法
    Snake.prototype.render = function(map){
        // 生成对应个数的div元素
        // 遍历body数组
        for(var i =0,length = this.body.length; i < length;i++){
            // 根据数组的每一项的数据生成一个新的div元素
            var piece = this.body[i];
            // 创建元素
            var ele = document.createElement("div");
            // 添加样式
            ele.style.width = this.width + "px";
            ele.style.height = this.height + "px";
            ele.style.left = piece.x * this.width + "px";
            ele.style.top = piece.y * this.height + "px";
            ele.style.position = ps;
            ele.style.background = piece.color;
            // 渲染到指定的父级内部
            map.appendChild(ele);
            // 添加的新元素存到数组里
            this.elements.push(ele);
        }
    };
    // 添加蛇的运动方法
    Snake.prototype.move = function(){
        // 1.蛇身的每一节都要变成上一节的位置
        // 循环要从最后一项开始,防止前面的数据放生变化
        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];
        // 2.蛇头要根据方向发生位置变化
        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;
        }
    };
    // 删除上一次渲染的蛇的所有div元素
    Snake.prototype.remove = function (map) {
        // 遍历删除所有的元素
        for(var i = this.elements.length-1; i >=0 ;i--){
            map.removeChild(this.elements[i]);
        }
        // 数组也要清空
        this.elements = [];
    };
    window.Snake = Snake;
})();

// 测试
// var map = document.getElementById("map");
// var snake = new Snake();
// snake.render(map);

游戏对象

  • 创建 Game 的构造函数,并设置属性
    1.food
    2.snake
    3.map
  • 通过原型设置方法
    • start 开始游戏(绘制所有游戏对象,渲染食物对象和蛇对象)
  • 通过自调用函数,进行封装,通过 window 暴露 Game 对象
// 自调用函数封闭作用域
(function(){
    // 定义一个全局变量,存储this
    var that;
    function Game(map){
        // 设置三个属性,蛇,食物,地图
        this.food = new Food();
        this.snake = new Snake();
        this.map = map;
        that = this;
    }
    // 添加一个游戏开始方法,方法内初始化蛇和食物
    Game.prototype.start = function(){
        // 添加蛇和食物到地图上
        this.food.render(this.map);
        this.food.render(this.map);
        this.food.render(this.map);
        this.snake.render(this.map);
        // 让游戏逻辑开始
        // 让蛇动起来
        runSnake();
        // 上下左右箭头控制蛇的运动方向
        bindKey();
    }
    // 封装一个私有函数,这个函数只能在模块内部进行调用
    function runSnake(){
        // 开启一个定时器,让蛇连续运动
        var timer = setInterval(function(){
            // 定时器函数内部的this指向的是window,不能直接使用this。
            that.snake.move();
            // 删掉上一次的蛇
            that.snake.remove(that.map);
            // 渲染新的蛇
            that.snake.render(that.map);
            // 记录一下最大的位置
            var maxX = that.map.offsetWidth / that.snake.width;
            var maxY = that.map.offsetHeight / that.snake.height;
            // 找到当前蛇头的位置
            var headX = that.snake.body[0].x;
            var headY = that.snake.body[0].y;
            // 每一次这个蛇走到新的位置,都要判断一下是否吃到食物了,让自己增加一节
            // 记录一下食物的坐标
            // var foodX = that.food.x;
            // var foodY = that.food.y;
            // 获取一下蛇头的具体坐标值(px)
            var hX = headX * that.snake.width;
            var hY = headY * that.snake.height;
            // 判断蛇头和食物是否重叠
            // 将食物数组的每一个都要进行对比,谁被吃掉,删除自己,渲染一个新的元素
            for(var i = 0;i<that.food.elements.length;i++){
                if(that.food.elements[i].offsetLeft === hX && that.food.elements[i].offsetTop === hY){
                    // 吃到食物
                    // 让食物删除,渲染一个新的食物
                    that.food.remove(that.map,i);
                    that.food.render(that.map);
                    // 添加一个新的蛇节
                    var last = that.snake.body[that.snake.body.length - 1];
                    that.snake.body.push({
                        x: last.x,
                        y: last.y,
                        color: last.color
                    });
                }
            }
            // 每次移动都要判断是否出了地图,游戏是否结束
            // 进行判断
            if(headX < 0 || headX >= maxX || headY < 0 || headY >= maxY){
                // 停止定时器,弹出提醒,游戏结束
                clearInterval(timer);
                alert("Game Over");
            }
        },150)
    };
    // 封装一个私有函数控制上下左右按键更改的方向
    function bindKey() {
        // 给整个文档绑定键盘按下事件
        document.onkeydown = function(e){
            // console.log(e.keyCode);
            // 键盘的编码
            // 37是left,38是top,39是right,40是bottom
            switch(e.keyCode){
                case 37:
                    that.snake.direction = "left";
                    break;
                case 38:
                    that.snake.direction = "top";
                    break;
                case 39:
                    that.snake.direction = "right";
                    break;
                case 40:
                    that.snake.direction = "bottom";
                    break;
            }
        };
    }
    // 将构造函数通过window暴露
    window.Game = Game;
})();

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

main主执行代码

项目结构
main主执行代码,该文件引用必须要放在所有js文件引用之后
// 使用自调用函数,关住作用域
(function(){
    var map = document.getElementById("map");
    var game = new Game(map);
    game.start();
})();

减少浏览器http请求

将所有的js代码整合到一个js文件里,然后就只发送一次请求,进行优化


自调用函数之后一定要加分号,不然会有程序错误

JS代码压缩

https://tool.oschina.net/jscompress

压缩之后代码运行速度会有提高,同时,压缩后的文件名一般为xxx.min.js

自调用函数的参数

  • window
  • undefined
    两个参数
    作用:1.window,将全局变为局部变量,避免了跳出本层作用域,将来进行代码压缩的时候,如果没有进行传参,速度会稍微减慢一点
    作用:2.undefined,IE8及以下浏览器的undefined是可变的,会被更改

相关文章

  • JS高级特性简单实现—贪吃蛇

    搭建页面 index.css 分析对象 游戏对象 蛇对象 食物对象 食物对象 1.创建构造函数Food,设置属性 ...

  • 前端大神作品推荐

    js开发实现简单贪吃蛇游戏(20行代码)

  • 贪吃蛇练习(html 、JS)

    用JS完成一个 贪吃蛇,在HTML中写写内容,CSS中写样式,JS中写动作,基本可以实现贪吃蛇这个简单的操作。

  • js实现简单贪吃蛇游戏

    编程心得 最近一段时间对于js部分理解起来比较吃力,掌握的内容少之又少,在编程的过程中也是属于懵懵懂懂的状态,凭借...

  • JS 高级特性

    什么是对象 万物皆对象 我们可以从两个层次来理解(1)对象是单个事物的抽象(2)对象是一个容器,封装来属性(pro...

  • iOS下JS与OC互相调用--MessageHandler

    种简单的方式。那就是利用WKWebView的新特性MessageHandler来实现JS调用原生方法。 Messa...

  • 贪吃蛇

    js 贪吃蛇代码

  • JavaScript基础系列之——继承

    一、基本概念: JavaScript基于原型实现面向对象特性,在JS编程中,面向对象的继承是重点,本文将从简单...

  • JavaScript继承

    js继承方式js是一门弱类型动态语言,封装和继承是他的两大特性 原型链继承 优点: 简单易于实现 实例是子类的实例...

  • JS编写的贪吃蛇Dome

    最近在家玩了贪吃蛇大作战,突发兴致下就想到用JS来写一个贪吃蛇的简单Dome,这里并没有用canvas来编写,有空...

网友评论

      本文标题:JS高级特性简单实现—贪吃蛇

      本文链接:https://www.haomeiwen.com/subject/tsurnktx.html