chrome断网小游戏实现

谷歌断网小游戏代码粗略解析 当我们使用谷歌浏览器时,碰到断网的情况,就会看到一个小恐龙加上断网提示语的界面。 一般情况下看到这个界面我们会下意识去解决网络问题,但是当你按下空格键,你就会发现一个新大陆。 小恐龙会在空格键按下后开始跳跃,并且右边的界面会像画幅一样展开。随即小恐龙开始奔跑在一片看不到尽头的沙漠中,画面中会随机出现仙人球树和翼龙,玩家需要做的是通过空格键和下键控制小恐龙避开障碍物,一直前进。 这个小游戏从2014年9月发布以来就受到了很多人得喜爱,目前为止有超过2.7亿的月活量。如此小巧精致的一个游戏,想必其实现 阅读详情

代码部分都加了详细注释,我就不再详细多说了,主要就是几个主要的函数
Obstacle障碍物
NightMode昼夜更替
Cloud云朵
HorizonLine地面
Runner运动的配置
GameOverPanel游戏结束界面
DistanceMeter分数以及高分记录
CollisionBox碰撞检测
Trex恐龙

<script type="text/javascript">




    var div = document.querySelector('div');
    var spriteDefinition = {
        // 地面在精灵图中的位置
        HORIZON: { x: 2, y: 54 },
        // 云朵在精灵图中的位置
        CLOUD: { x: 86, y: 2 },
        STAR: { x: 645, y: 2 },//星星
        MOON: { x: 484, y: 2 },//月亮
        CACTUS_LARGE: { x: 332, y: 2 },	//大仙人掌
        CACTUS_SMALL: { x: 228, y: 2 },//小仙人掌
        PTERODACTYL: { x: 134, y: 2 }//翼龙
    },
        gameFrame = 600;
    FPS = 60,
        DEFAULT_WIDTH = 600,
        imgSprite = document.getElementById('sprite');

    // 障碍物,仙人掌和翼龙
    Obstacle.obstacles = [];//存储障碍物的数组
    Obstacle.obstacleHistory = [];//记录障碍物数组中障碍物的类型
    // 障碍物不能太密集也不能太稀疏,两个障碍物之间也应该有一个间距以供弹跳后的落脚
    // 因此设置一个障碍物最大间距系数
    Obstacle.MAX_GAP_COEFFICIENT = 1.5;
    // 每组障碍物的最大数量
    Obstacle.MAX_OBSTACLE_LENGTH = 3;
    // 相邻的障碍物类型的最大重复数
    Obstacle.MAX_OBSTACLE_DUPLICATION = 2;
    Obstacle.types = [
        {
            type: 'CACTUS_SMALL',//小仙人掌
            width: 17,//宽
            height: 35,//高
            yPos: 105,//在画布上的y坐标
            multipleSpeed: 4,
            minGap: 120,    //最小间距
            minSpeed: 0    //最低速度
        },
        {
            type: 'CACTUS_LARGE',//大仙人掌
            width: 25,
            height: 50,
            yPos: 90,
            multipleSpeed: 7,
            minGap: 120,
            minSpeed: 0
        },
        {
            type: 'PTERODACTYL',//翼龙
            width: 46,
            height: 40,
            yPos: [100, 75, 50],//有高,中,低三个高度
            multipleSpeed: 999,
            minSpeed: 8.5,
            minGap: 150,
            numFrames: 2,//有两个动画帧
            frameRate: 1000 / 6,//动画帧切换速率,这里为一秒6帧
            speedOffset: 0.8 //速度修正
        }
    ];
    // 绘制障碍物构造函数
    Obstacle.dimensions={
        WIDTH:600
    };
    // dimensions 屏幕尺寸  gapCoefficient 障碍物间隙  opt_xOffset 障碍物水平偏移量
    function Obstacle(canvas,type, spriteImgPos, gapCoefficient, speed, opt_xOffset) {
        // console.log(opt_xOffset);
        this.canvas = canvas;
        this.ctx = canvas.getContext('2d');
        this.spritePos = spriteImgPos;
        // 障碍物类型
        this.typeConfig = type;
        this.gapCoefficient = gapCoefficient;
        // 每个障碍物数量(1-3)
        this.size = getRandomNum(1, Obstacle.MAX_OBSTACLE_LENGTH);
        this.dimensions =Obstacle.dimensions;
        // 表示该障碍物是否可以被移除
        this.remove = false;
        // 水平坐标
        this.xPos = this.dimensions.WIDTH + (opt_xOffset || 0);
        this.yPos = 0;
        this.width = 0;
        this.gap = 0;
        this.speedOffset = 0;//速度修正
        // 障碍物的动画帧
        this.currentFrame = 0;
        // 动画帧切换的计时器
        this.timer = 0;
        this.init(speed);
    }
    Obstacle.prototype = {
        init: function (speed) {
            // console.log(this.typeConfig);
            // 如果随机障碍物是翼龙,则只出现一只
            // 翼龙的multipleSpeed是999,远大于speed
            if (this.size > 1 && this.typeConfig.multipleSpeed > speed) {
                this.size = 1;
            }
            //障碍物的总宽度等于单个障碍物的宽度乘以个数
            this.width = this.typeConfig.width * this.size;
            // 若障碍物的纵坐标是一个数组
            // 则随机选取一个
            if (Array.isArray(this.typeConfig.yPos)) {
                var yPosConfig = this.typeConfig.yPos;
                this.yPos = yPosConfig[getRandomNum(0, yPosConfig.length - 1)];
            } else {
                this.yPos = this.typeConfig.yPos;
            }
            this.draw();
            // 对翼龙的速度进行修正,让它看起来有的飞得快一些,有的飞的慢一些
            if (this.typeConfig.speedOffset) {
                this.speedOffset = Math.random() > 0.5 ? this.typeConfig.speedOffset : -this.typeConfig.speedOffset;

            }
            // 障碍物之间的间隙,与游戏速度有关
            this.gap = this.getGap(this.gapCoefficient, speed);
        },
        // 障碍物之间的间隔,gapCoefficient为间隔系数
        getGap: function (gapCoefficient, speed) {
            var minGap = Math.round(this.width * speed + this.typeConfig.minGap * gapCoefficient);
            var maxGap = Math.round(minGap * Obstacle.MAX_GAP_COEFFICIENT);
            return getRandomNum(minGap, maxGap);
        },
        // 判断障碍物是否移出屏幕外
        isVisible: function () {
            return this.xPos + this.width > 0;
        },
        draw: function () {
            // 障碍物宽高
            var sourceWidth = this.typeConfig.width;
            var sourceHeight = this.typeConfig.height;
            //根据障碍物数量计算障碍物在精灵图导航的x坐标
            // this.size的取值范围为1-3
            var sourceX = (sourceWidth * this.size) * (0.5 * (this.size - 1)) + this.spritePos.x;
            // console.log(sourceX);
            // 如果当前动画帧大于0,说明障碍物类型是翼龙
            // 更新翼龙的精灵图x坐标使其匹配第二帧动画
            if (this.currentFrame > 0) {
                sourceX += sourceWidth * this.currentFrame;
            };
            // console.log(this.xPos, this.yPos);
            this.ctx.drawImage(imgSprite,
                sourceX, this.spritePos.y,
                sourceWidth * this.size, sourceHeight,
                this.xPos, this.yPos,
                sourceWidth * this.size, sourceHeight);
        },
        // 单个障碍物的移动
        update: function (deltaTime, speed) {
            // 如果障碍物还没有移出屏幕外
            if (!this.remove) {
                // 如果有速度修正则修正速度
                if (this.typeConfig.speedOffset) {
                    speed += this.speedOffset;
                }
                // 更新x坐标
                this.xPos =this.xPos- Math.floor((speed * FPS / 1000) * deltaTime);
                // console.log(this.xPos);
                // Update frame
                if (this.typeConfig.numFrames) {
                    this.timer += deltaTime;
                    if (this.timer >= this.typeConfig.frameRate) {
                        // 在两个动画帧之间来回切换以达到动画效果
                        this.currentFrame = this.currentFrame == this.typeConfig.numFrames - 1 ? 0 : this.currentFrame + 1;
                        this.timer = 0;
                    }
                }
                this.draw();
                if (this.isVisible()) {
                    this.remove = true;
                }
            }
        },
        // 管理多个障碍物移动
        updateObstacles: function (deltaTime, currentSpeed) {
            // 障碍物列表,保存一个障碍物列表的副本
            var updateObstacles = Obstacle.obstacles.slice(0);
            for (var i = 0; i < Obstacle.obstacles.length; i++) {
                var obstacle = Obstacle.obstacles[i];
                obstacle.update(deltaTime, currentSpeed);
                // 移除被标记为删除的障碍物
                if (obstacle.remove) {
                    updateObstacles.shift();
                }
            }
            Obstacle.obstacles = updateObstacles;
            if (Obstacle.obstacles.length > 0) {
                // 获取障碍物列表中的最后一个障碍物
                var lastObstacle = Obstacle.obstacles[Obstacle.obstacles.length - 1];

                // 若满足条件则添加障碍物
                if (lastObstacle &&
                    lastObstacle.isVisible() &&
                    (lastObstacle.xPos + lastObstacle.width + lastObstacle.gap) <
                    this.dimensions.WIDTH) {
                    this.addNewObstacle(currentSpeed);
                }
            } else {
                // 若障碍物列表中没有障碍物则立即添加
                this.addNewObstacle(currentSpeed);
            }
        },
        // 随机添加障碍
        addNewObstacle: function (currentSpeed) {
            // 随机选取一种类型的障碍
            var obstacleTypeIndex = getRandomNum(0, Obstacle.types.length - 1);
            var obstacleType = Obstacle.types[obstacleTypeIndex];


            // 检查随机取到的障碍物类型是否与前两个重复
            // 或者检查其速度是否合法,这样可以保证游戏在低速时不出现翼龙
            // 如果检查不通过则重新再选一次直到通过为止
            if (this.duplicateObstracleCheck(obstacleType.type) || currentSpeed < obstacleType.minSpeed) {
                this.addNewObstacle(currentSpeed);
            } else {
                // 检查通过后,获取该值在精灵图中的坐标
                var obstacleSpritePos = this.spritePos[obstacleType.type];
                // 生成新的障碍物并存入数组
                Obstacle.obstacles.push(new Obstacle(this.canvas, obstacleType, obstacleSpritePos, this.dimensions, this.gapCoefficient, currentSpeed, obstacleType.width));
                // 同时将障碍物类型存入histo数组
                // 该方法向数组开头添加一个或者更多元素,并返回长度
                Obstacle.obstacleHistory.unshift(obstacleType.type);
            }
            // 若history数组的长度大于1,则清空最前面的两个
            if (Obstacle.obstacleHistory.length > 1) {
                Obstacle.obstacleHistory.splice(Obstacle.MAX_OBSTACLE_DUPLICATION);
            }
        },
        // 检查障碍物是否超过允许的最大重复数
        duplicateObstracleCheck: function (nextObstacleType) {
            var duplicateCount = 0;
            // 与history数组中的障碍物类型比较,最大只允许重得两次
            for (var i = 0; i < Obstacle.obstacleHistory.length; i++) {
                duplicateCount = Obstacle.obstacleHistory[i] === nextObstacleType ? duplicateCount + 1 : 0;
            }
            return duplicateCount >= Obstacle.MAX_OBSTACLE_DUPLICATION;
        }
    };



    // 昼夜更替效果
    NightMode.config = {
        FADE_SPEED: 0.035,//淡入淡出的速度
        HEIGHT: 40,//月亮的高度
        MOON_SPEED: 0.25,//月亮的速度
        NUM_STARS: 2,//星星的数量
        STAR_SIZE: 9,//星星的宽度
        STAR_SPEED: 0.3,//星星的速度
        STAR_MAX_Y: 70,//星星在画布上出现的位置
        WIDTH: 20//半个月度宽度
    };
    // 月亮在不同时期有不同的位置
    NightMode.phases = [140, 120, 100, 60, 40, 20, 0];
    // 时间记录
    NightMode.invertTimer = 0;
    // 是否可以进行昼夜交替
    NightMode.inverted = false;
    // 用于控制样式切换
    NightMode.invertTrigger = false;
    // 黑夜持续的时间
    NightMode.INVER_FADE_DURATION = 5000;
    // 黑夜构造函数
    function NightMode(canvas, spritePos, containerWidth) {
        this.canvas = canvas;
        this.ctx = canvas.getContext('2d');
        this.spritePos = spritePos;
        this.containerWidth = containerWidth;
        this.xPos = containerWidth - 50;//月亮的x坐标
        this.yPos = 30;//月亮的y坐标
        this.currentPhase = 0;
        this.opacity = 0;
        this.stars = [];//用于储存星星
        this.drawStars = false;//是否绘制星星
        this.placeStars();//放置星星
    };
    NightMode.prototype = {
        update: function (activated) {
            // 若夜晚模式处于激活状态并且opacity=0时
            // 对月亮周期进行更新
            if (activated && this.opacity == 0) {
                this.currentPhase++;
                if (this.currentPhase >= NightMode.phases.length) {
                    this.currentPhase = 0;
                }
            }
            // 淡入
            if (activated && (this.opacity < 1 || this.opacity == 0)) {
                this.opacity += NightMode.config.FADE_SPEED;
            } else if (this.opacity > 0) {
                this.opacity -= NightMode.config.FADE_SPEED;
            }
            // 当opacity大于0时移动月亮的位置
            if (this.opacity > 0) {
                this.xPos = this.updateXPos(this.xPos, NightMode.config.MOON_SPEED);

                // 移动星星
                if (this.drawStars) {
                    for (var i = 0; i < NightMode.config.NUM_STARS; i++) {
                        this.stars[i].x = this.updateXPos(this.stars[i].x, NightMode.config.STAR_SPEED);
                    };
                }
                this.draw();
            } else {
                this.opacity = 0;
                this.placeStars();
            }
            this.drawStars = true;
        },
        updateXPos: function (currentPos, speed) {
            if (currentPos < -NightMode.config.WIDTH) {
                currentPos = this.containerWidth;
            } else {
                currentPos -= speed;
            }
            return currentPos;
        },
        draw: function () {
            // 周期为3时画满月
            var moonSourceWidth = this.currentPhase == 3 ? NightMode.config.WIDTH * 2 : NightMode.config.WIDTH;
            var moonSourceHeight = NightMode.config.HEIGHT;
            // 从精灵图上获取月亮正确的形状
            var moonSourceX = this.spritePos.x + NightMode.phases[this.currentPhase];
            // console.log(moonSourceX);
            var moonOutputWidth = moonSourceWidth;
            var starSize = NightMode.config.STAR_SIZE;
            var starSourceX = spriteDefinition.STAR.x;
            this.ctx.save();
            // 画布透明度随之变化
            this.ctx.globalAlpha = this.opacity;
            if (this.drawStars) {
                for (var i = 0; i < NightMode.config.NUM_STARS; i++) {
                    this.ctx.drawImage(imgSprite,
                        starSourceX, this.stars[i].sourceY,
                        starSize, starSize,
                        Math.round(this.stars[i].x), this.stars[i].y,
                        NightMode.config.STAR_SIZE, NightMode.config.STAR_SIZE);
                }
            }
            this.ctx.drawImage(imgSprite,
                moonSourceX, this.spritePos.y,
                moonSourceWidth, moonSourceHeight,
                Math.round(this.xPos), this.yPos,
                moonOutputWidth, NightMode.config.HEIGHT);
            this.ctx.globalAlpha = 1;
            this.ctx.restore();
        },
        placeStars: function () {
            // 将画布分为若干组
            var segmentSize = Math.round(this.containerWidth / NightMode.config.NUM_STARS);
            for (var i = 0; i < NightMode.config.NUM_STARS; i++) {
                this.stars[i] = {};
                // 每组星星位置随机
                this.stars[i].x = getRandomNum(segmentSize * i, segmentSize * (i + 1));
                this.stars[i].y = getRandomNum(0, NightMode.config.STAR_MAX_Y);
                this.stars[i].sourceY = spriteDefinition.STAR.y + NightMode.config.STAR_SIZE * i;
            }
        },
        invert: function (deltaTime) {
            this.update(NightMode.inverted);
            // 黑夜持续时间为5秒
            if (NightMode.invertTimer > NightMode.INVER_FADE_DURATION) {
                NightMode.invertTimer = 0;
                NightMode.invertTrigger = false;
                // 切换类
                NightMode.inverted = div.classList.toggle('inverted', NightMode.invertTrigger)

            } else if (NightMode.invertTimer) {
                NightMode.invertTimer += deltaTime;
            } else {
                // 每700米触发黑夜,
                NightMode.invertTrigger = !(gameFrame % 500);
                if (NightMode.invertTrigger && NightMode.invertTimer === 0) {
                    NightMode.invertTimer += deltaTime;
                    NightMode.inverted = div.classList.toggle('inverted', NightMode.invertTrigger);
                }
            }
        },
        reset: function () {
            this.currentPhase = 0;
            this.opacity = 0;
            this.update(false);
        }

    };

    //  云朵构造函数
    Cloud.config = {
        HEIGHT: 14,//云朵sprite的高度
        MAX_CLOUD_GAP: 400,//两朵云之间最大的间隙
        MAX_SKY_LEVEL: 30,//云朵 的最大高度
        MIN_CLOUD_GAP: 100,//两朵云之间的最小间隙
        MIN_SKY_LEVEL: 71,//云朵的最小高度
        WIDTH: 46,//云朵sprite的宽度
        MAX_CLOUDS: 6,//最多云朵数量
        CLOUD_FREQUENCY: 0.5//云朵出现频率
    };
    // 储存云朵
    Cloud.clouds = [];
    function getRandomNum(min, max) {
        return Math.floor(Math.random() * (max - min + 1)) + min;
    }
    // canvas用于绘制的画布
    // spritePos在雪碧图中的坐标
    // containerWidth容器宽度
    function Cloud(canvas, spritePos, containerWidth) {

        this.canvas = canvas;
        this.ctx = canvas.getContext('2d');
        this.spritePos = spritePos;
        this.xPos = containerWidth;//云朵初始x坐标在屏幕外
        this.yPos = 0;//云朵初始高度
        this.remove = false;//是否移除

        // console.log(this.spritePos.x);
        // 云朵之间的间隙400-100
        this.cloudGap = getRandomNum(Cloud.config.MAX_CLOUD_GAP, Cloud.config.MIN_CLOUD_GAP);
        this.init();
    }
    // 主要逻辑代码在Cloud的原型链中
    Cloud.prototype = {
        init: function () {
            // 设置云朵的高度为随机30-71
            this.yPos = getRandomNum(Cloud.config.MAX_SKY_LEVEL, Cloud.config.MIN_SKY_LEVEL);
            this.draw();
        },
        draw: function () {
            // 不使用save和restore下一次绘制会在第一次绘制的基础上继续绘制,添加过后第二次绘制会回归初始化
            this.ctx.save();
            var sourceWidth = Cloud.config.WIDTH,
                sourceHeight = Cloud.config.HEIGHT;
            this.ctx.drawImage(imgSprite,
                this.spritePos.x, this.spritePos.y,
                sourceWidth, sourceHeight,
                this.xPos, this.yPos,
                sourceWidth, sourceHeight);
            this.ctx.restore();
        },
        // 添加云朵并控制其移动
        updateClouds: function (speed) {
            var numClouds = Cloud.clouds.length;
            if (numClouds) {
                for (var i = numClouds - 1; i >= 0; i--) {
                    Cloud.clouds[i].update(speed);
                };
                var lastCloud = Cloud.clouds[numClouds - 1];
                // 若当前存在的云朵数量小于最大云朵数量
                // 并且云朵位置大于间隙时
                // 随机添加云朵
                // 云朵数量小于设定最大值,屏幕宽度-初始坐标大于最后一个云朵
                if (numClouds < Cloud.config.MAX_CLOUDS &&
                    (DEFAULT_WIDTH - lastCloud.xPos) > lastCloud.cloudGap &&
                    Cloud.config.CLOUD_FREQUENCY > Math.random()) {
                    this.addCloud();
                };
                // 过滤掉已经移出屏幕外的云朵
                Cloud.clouds = Cloud.clouds.filter(function (obj) {
                    return !obj.remove;
                });
            } else {
                this.addCloud();
            }
        },
        update: function (speed) {
            // 仅绘制符合条件的云朵
            if (!this.remove) {
                // 向左移动
                this.xPos -= Math.ceil(speed);
                this.draw();
                if (!this.isVisible()) {
                    this.remove = true;
                }
            }
        },
        // 判断云朵是否移除屏幕外
        isVisible: function () {
            return this.xPos + Cloud.config.WIDTH > 0;
        },
        // 将云朵添加至数组
        addCloud: function () {
            var cloud = new Cloud(this.canvas, spriteDefinition.CLOUD, DEFAULT_WIDTH);
            Cloud.clouds.push(cloud);
        }
    };

    // canvas地面绘制在画布上
    // spritpos 地面在精灵图上的坐标
    function HorizonLine(canvas, spritePos) {
        this.spritePos = spritePos;
        this.canvas = canvas;
        this.ctx = canvas.getContext("2d");
        this.sourceDimensions = {};
        this.dimensions = HorizonLine.dimensions;
        // 在精灵图坐标为2处和602处分别为不同的地形
        this.sourceXPos = [this.spritePos.x, this.spritePos.x + this.dimensions.WIDTH];
        this.xPos = [];//地面在画布中的x坐标
        this.yPos = 0;//地面在画布中的y坐标
        this.bumpThreshold = 0.5;	//随机地形系数

        this.setSourceDimesions();
        this.draw();
    }

    HorizonLine.dimensions = {
        WIDTH: 600,	//宽600
        HEIGHT: 12,	//高12像素
        YPOS: 127	//在画布中的位置
    };
    // HorizonLine原型中的方法
    HorizonLine.prototype = {
        setSourceDimesions: function () {
            for (var dimension in HorizonLine.dimensions) {
                this.sourceDimensions[dimension] = HorizonLine.dimensions[dimension];
                this.dimensions[dimension] = HorizonLine.dimensions[dimension];
            }
            //地面在画布上的位置
            this.xPos = [0, HorizonLine.dimensions.WIDTH];//0,600
            this.yPos = HorizonLine.dimensions.YPOS;
        },
        //随机地形
        getRandomType: function () {
            return Math.random() > this.bumpThreshold ? this.dimensions.WIDTH : 0;
        },
        draw: function () {
            //要绘制的图像,图像将要被绘制的区域的左上角的坐标,图像所要绘制区域的大小,所要绘制的图像区域的左上角坐标,图像区域所要绘制的画布大小
            this.ctx.drawImage(imgSprite,
                this.sourceXPos[0], this.spritePos.y,
                this.sourceDimensions.WIDTH, this.sourceDimensions.HEIGHT,
                this.xPos[0], this.yPos,
                this.dimensions.WIDTH, this.dimensions.HEIGHT);

            this.ctx.drawImage(imgSprite,
                this.sourceXPos[1], this.spritePos.y,
                this.sourceDimensions.WIDTH, this.sourceDimensions.HEIGHT,
                this.xPos[1], this.yPos,
                this.dimensions.WIDTH, this.dimensions.HEIGHT);

        },
        updateXPos: function (pos, increment) {
            var line1 = pos,
                line2 = pos === 0 ? 1 : 0;

            this.xPos[line1] -= increment;
            this.xPos[line2] = this.xPos[line1] + this.dimensions.WIDTH;
            // 若第一段地面完全移出canvas外
            if (this.xPos[line1] <= -this.dimensions.WIDTH) {
                // 则将其移动至canvas外右侧
                this.xPos[line1] += this.dimensions.WIDTH * 2;
                // 同时将第二段地面移动至canvas内
                this.xPos[line2] = this.xPos[line1] - this.dimensions.WIDTH;
                // 随机选择地形
                this.sourceXPos[line1] = this.getRandomType() + this.spritePos.x;
            }
        },
        update: function (deltaTime, speed) {
            //FPS每秒传真率(刷新率)
            var increment = Math.floor(speed * (FPS / 1000) * deltaTime);
            if (this.xPos[0] <= 0) {
                // 交换地面一和地面二
                this.updateXPos(0, increment);
            } else {
                this.updateXPos(1, increment);
            }
            this.draw();
        },
        reset: function () {
            this.xPos[0] = 0;
            this.xPos[1] = HorizonLine.dimensions.WIDTH;
        }
    };


    //不写onload方法就显示不出图片
    window.onload = function () {
        var canvas = document.createElement('canvas'),
            a = document.getElementById('runner-container'),
            ctx = canvas.getContext('2d');
        canvas.id = 'c';
        canvas.width = 600;
        canvas.height = 150;
        a.appendChild(canvas);
        // console.log(spriteDefinition.CLOUD.x);
        
        var speed = 3;
        var h = new HorizonLine(canvas, spriteDefinition.HORIZON);
        var cloud = new Cloud(canvas, spriteDefinition.CLOUD, DEFAULT_WIDTH);
        var night = new NightMode(canvas, spriteDefinition.MOON, DEFAULT_WIDTH);
        var obstacle = new Obstacle(canvas,Obstacle.types[0] ,spriteDefinition,  0.6, 1);
        // console.log(obstacle);
        var startTime = 0;
        var deltaTime;
        (function draw(time) {
            gameFrame++;
            if (speed < 13.5) {
                speed += 0.01;
            }
            // 清空矩形区域内的绘图
            ctx.clearRect(0, 0, 600, 150);
            time = time || 0;
            deltaTime = time - startTime;
            h.update(deltaTime, speed);
            cloud.updateClouds(0.2);
            night.invert(deltaTime);
            obstacle.updateObstacles(deltaTime, speed);
            // console.log(speed);
            startTime = time;
            window.requestAnimationFrame(draw, canvas);
        })();
    };
</script>

参考了博客园逐影的文章,讲的非常详细。

爷青回|用Python制作【谷歌恐龙游戏】当你离线没网的时候,这将成为一种乐趣~ 阅读详情

相关推荐

Chrome小恐龙快跑小游戏——Python实现

Chrome小恐龙快跑小游戏——Python实现

timberman666的博客 8038

不用网的cmd小游戏来啦

小游戏来啦!

lijc0124的博客 1万+

微信小游戏实战--cocos creator实现wordle游戏(一)

前言 前段时间赋闲在家,整理电脑上的资料时才发现从业这么多年都是在给公司打工,没为自己写过点什么,所以决定为自己写一个完整的“产品”。同时,为了检验一下自己的学习能力,选择了从未接触过的游戏领域。由于是独立开发,做不了大型游戏,只好瞄准了休闲类的小游戏。接下来就是确定要做一个什么样的休闲小游戏了。在试玩各种休闲类的小游戏过程中,偶然发现了一款去年在国外很火的游戏《Wordle》,甚至于纽约时报花了大价钱去收购它。于是想当然的就决定山寨一把,没准就成为爆款走向人生巅峰了(实时证明是我想太多......

aidscooler的专栏 4792

无网络游戏1 小恐龙部分

众所周知Python代码可以在没有网络的状态下运行,所以我们要做一个无网络的游戏。这是一个非常简单的程序适合初学者学。

pythonwyw123321的博客 2426

断网也不怕!手把手教你用开发者工具玩转Chrome小恐龙隐藏功能

本文详细介绍了如何利用Chrome开发者工具深度定制离线小恐龙游戏。通过注入无敌代码等技巧,玩家可实现无敌模式、速度控制、自动化游戏及外观修改等隐藏功能,无需断网即可在chrome://dino页面开启技术探索之旅。

weixin_29290947的博客 825

用Python复刻Chrome断网小恐龙游戏:Pygame实战教程(附完整源码)

本文详细介绍了如何使用Python和Pygame库复刻Chrome浏览器中的经典断网小恐龙游戏。从环境准备、游戏架构设计到角色系统、障碍物实现和游戏主循环,教程逐步引导开发者完成整个项目,并附完整源码。通过这个实战项目,读者不仅能学习Python游戏开发技巧,还能深入理解游戏设计核心逻辑。

weixin_30872337的博客 538

Chrome自带恐龙小游戏的源码研究(一)

  目录 Chrome自带恐龙小游戏的源码研究(一)——绘制地面 Chrome自带恐龙小游戏的源码研究(二)——绘制云朵 Chrome自带恐龙小游戏的源码研究(三)——昼夜交替 Chrome自带恐龙小游戏的源码研究(四)——绘制障碍物 Chrome自带恐龙小游戏的源码研究(五)——绘制霸王龙 Chrome自带恐龙小游戏的源码研究(六)——跳跃 Chrome自带恐龙小游戏的源码研...

总有些事,值得你去努力 1万+

让程序帮你玩chrome断网恐龙小游戏

效果如上视频,让计算机帮你玩转chrome断网恐龙小游戏,具体代码如下。

qq_30895747的博客 592

用Python复刻Chrome断网小恐龙游戏:从零开始的Pygame实战教程

本文是一份详细的Pygame实战教程,指导开发者从零开始使用Python复刻经典的Chrome断网小恐龙游戏。教程涵盖了项目初始化、核心类设计(恐龙与障碍物)、游戏主循环构建以及碰撞检测等关键开发环节,帮助Python初学者通过趣味项目掌握游戏开发的核心概念与技能。

river的博客 649

尚硅谷Redis 7学习笔记

尚硅谷Redis 7学习笔记

不更了,主打一个陪伴 2万+

Android Qcom Display学习(十一)

该系列文章总目录链接与各部分简介:本章主要是基于MIPI Payload传输接口的学习,包括Generic和DCS的接口不同。底层都是基于去封装的,下面第一部分调用原生接口,第二部分分析高通是如何进行读写操作的。

Android驱动开发 3181

python谷歌小恐龙,这还是你断网时的样子嘛~_断网小恐龙

废话不多说,让我们愉快地开始吧~先给你们看一下运你效果🦖🦖🦖。

2401_84140442的博客 596

那些年我们create generate clock遇到的坑

想必大家对create_generate_clock这个命令熟悉的不能再熟悉了,然而,在真正做实际项目的时候,会出现各式各样的问题,比如在report timing时,generate clock路径不是我们预想的,source latency丢失等等,今天小编就跟大家重点聊聊source latency丢失的情况。clock时,首先我们要对一个项目的时钟结构要比较清晰,同时针对一些复杂的生成时钟,我们也要学会画波形图进行分析;接下来,我们就来看看哪些case会导致source latency丢失。

芯冰乐 136

edge同步chrome书签_等了这么久,Edge 终于成为我的默认浏览器

文章首发于微信公众号:效率视界Microsoft Edge 是 Win10 系统自带的浏览器软件。新版的 Microsoft Edge 香在哪里?支持同步扩展、书签不用再辛苦地「翻山越岭」了,可以直接在 Edge 上下载浏览器扩展插件,同时登陆 Microsoft 账号之后还可以同步扩展程序和收藏夹。Microsoft Edge 自己的扩展程序,是不是很香。也可以从 Chrome Web Stor...

weixin_31031155的博客 6912

【微服务】SpringBoot 整合 FastExcel 实现导入导出操作详解

SpringBoot 整合 FastExcel 实现导入导出操作详解

congge 4234

Spring Boot 调用豆包大模型如何开启联网搜索?

场景推荐方案有权限创建 Bot + 需强集成✅ 使用bot-xxxx无 Bot 权限 / 追求通用性✅ 自行集成搜索引擎 + 普通豆包模型仅需基础对话❌ 无需考虑联网豆包大模型确实支持联网搜索,但前提是:你用的是“智能体 Bot”,且它被配置了“网页搜索”插件,并调用正确的 API 端点。希望本文能帮你少走弯路,快速构建出真正“知天下事”的智能助手!

lpfasd123的博客 1123

鲜为人知,微软和谷歌开发的两款休闲小游戏

前言 当你在上网浏览网页的时候发现突然断网了,这时候可能要去执行以下网络修复,但是这个过程又比较长,因此,为了减少人们的焦虑,微软的Edge浏览器和谷歌的Chrome浏览器,都内置了一个小游戏,可能很多人都不知道,今天土叔就分享给大家,也好缓解一下断网时焦虑的心情。 微软的Edge浏览器的小游戏是一个冲浪游戏,而谷歌的Chrome浏览器的小游戏是一个跳跳恐龙游戏,下面将详细介绍下这两款休闲小游戏。 介绍 1. 微软冲浪 The surf game是一款网页冲浪小游戏,整体风格偏休闲,对于缓解焦虑很

土狗子大哥的专栏 8063

BACnet协议栈基本对象BI、BO、AI、AO、AV以及device和File对象介绍

BACnet协议栈基本对象BI、BO、AI、AO、AV以及device和File对象介绍

shufawangzhang的博客 3986

谷歌恐龙游戏HTML,谷歌浏览器自带的恐龙跑酷小游戏

Chrome浏览器,以及一些chromium内核的浏览器在断网时打开网页其实有一个小彩蛋。这个时候如果你敲一下空格键(手机用户则是按一下触摸屏),就会发现小恐龙动起来啦~(不同的浏览器可能不是小恐龙,谷歌原版浏览器是小恐龙)。 然后你就可以进入恐龙跑酷小游戏啦~这个游戏在国外,尤其是网络不发达的地区是及其火爆的。游戏很简单,却有很可爱有趣。看你能玩到多少分吧。为什么是个小恐龙形象?因为做这个的设计...

weixin_31235395的博客 6263

基于Mask-RCNN网络的书架图书书脊分割和检测(含代码)

图书馆的书架上的图书书脊分割/检测对于图书书名等信息的识别具有重要意义,因此,该博客基于Mask-RCNN网络实现了图书书脊的分割和检测,有利于进一步利用ocr文字识别技术实现图书书名等信息的入库。如下图所示,为书架原始数据和分割/检测后的结果。(本文已训练模型,无需训练)

qq_44619530的博客 2025

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

chrome浏览器断网游戏

chrome浏览器,谷歌浏览器,断网游戏。 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="runner-container" class="runner-container offline" style="height:

qq_38419885的博客 3857

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

异名一文带你读懂Chrome小恐龙跑酷!

在chrome浏览器的断网页面,按空格键或者向上键会出现一个小恐龙跑酷小游戏,这个2D小游戏在设计上精致小巧,在代码上也只有三千多行,思路清晰严谨,很有学习价值demo在非断网情况下,可...

奎特尔星球 1499

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

Chrome断网小恐龙背后的Canvas游戏开发与碰撞检测实现

网页游戏与浏览器内置功能看似简单,实则暗含许多经典前端技术实践。以Chrome浏览器离线状态下出现的像素小恐龙为例,它并非临时拼凑的动画,而是基于Canvas 2D API绘制、由JavaScript驱动的完整小游戏。游戏中涉及精灵图绘制、视差滚动、requestAnimationFrame主循环、重力模拟以及矩形碰撞检测等核心概念。对于前端开发者和游戏爱好者而言,剖析这只小恐龙的实现细节,不仅能理解浏览器内置页面的技术边界,也能掌握横版跑酷游戏的基础架构。无论断网消遣还是研究代码,这个隐藏彩蛋都提供了一个

weixin_34239169的博客 304

谷歌联网断网都可以玩的恐龙小游戏(内容有不死加速挂)

联网断网都可以访问网址:chrome://dino/ 来玩谷歌恐龙小游戏。 访问后是这个样子,然后按空格键即可开始玩。 开始玩的样子

lgz0921的博客 5万+
上一篇: 算法之枚举
下一篇: jQuery超详细入门教程
今天你写算法了吗
博客等级 码龄6年 84粉丝 35原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值