资讯专栏INFORMATION COLUMN

Chrome 小恐龙游戏源码探究三 -- 进入街机模式

yeooo / 752人阅读

摘要:文章首发于我的博客前言上一篇文章小恐龙游戏源码探究二让地面动起来实现了地面的移动。街机模式的效果就是游戏开始后,进入全屏模式。例如可以看到,进入街机模式之前,有一段开场动画。

文章首发于我的 GitHub 博客
前言

上一篇文章:《Chrome 小恐龙游戏源码探究二 -- 让地面动起来》 实现了地面的移动。这一篇文章中,将实现效果:1、浏览器失焦时游戏暂停,聚焦游戏继续。 2、开场动画。 3、进入街机模式。

街机模式的效果就是:游戏开始后,进入全屏模式。例如:

可以看到,进入街机模式之前,有一段开场动画。我们先来实现一下这个开场动画。

这里先只实现地面的开场动画,小恐龙的后续再去实现。
实现开场动画

首先修改 CSS 样式:

.offline .runner-container {
  position: absolute;
  top: 35px;
- width: 100%;
+ width: 44px;
  max-width: 600px;
  height: 150px;
  overflow: hidden;
}

canvas 初始只显示 44px 的宽度。

然后在 Runner 的原型链上添加方法:

Runner.prototype = {
  // 游戏被激活时的开场动画
  // 将 canvas 的宽度调整到最大
  playIntro: function () {
    if (!this.activated && !this.crashed) {
      this.playingIntro = true; // 正在执行开场动画

      // 定义 CSS 动画关键帧
      var keyframes = "@-webkit-keyframes intro { " +
          "from { width:" + Trex.config.WIDTH + "px }" +
          "to { width: " + this.dimensions.WIDTH + "px }" +
        "}";
      // 将动画关键帧插入页面中的第一个样式表
      document.styleSheets[0].insertRule(keyframes, 0);

      this.containerEl.style.webkitAnimation = "intro .4s ease-out 1 both";
      this.containerEl.style.width = this.dimensions.WIDTH + "px";

      // 监听动画。当触发结束事件时,设置游戏为开始状态
      this.containerEl.addEventListener(Runner.events.ANIMATION_END,
        this.startGame.bind(this));

      this.setPlayStatus(true); // 设置游戏为进行状态
      this.activated = true;    // 游戏彩蛋被激活
    } else if (this.crashed) {
      // 这个 restart 方法的逻辑这里先不实现
      this.restart();
    }
  },
  // 设置游戏为开始状态
  startGame: function () {
    this.playingIntro = false; // 开场动画结束
    this.containerEl.style.webkitAnimation = "";
  },
};

补充数据:

Runner.events = {
  // ...

+ ANIMATION_END: "webkitAnimationEnd",
};

这里用到了小恐龙类里的数据,我们先临时定义一下(后面讲到小恐龙那一章时,需要把这段临时代码删除):

function Trex() {}

Trex.config = {
  WIDTH: 44,
};

然后在 Runnerupdate 方法中调用上面定义的 playIntro 方法:

Runner.prototype = {
  update: function () {
    // ...

    if (this.playing) {
      this.clearCanvas();

+     // 刚开始 this.playingIntro 未定义 !this.playingIntro 为真
+     if (!this.playingIntro) {
+       this.playIntro(); // 执行开场动画
+     }

+     // 直到开场动画结束再移动地面
+     if (this.playingIntro) {
+       this.horizon.update(0, this.currentSpeed);
+     } else {
+       deltaTime = !this.activated ? 0 : deltaTime;
        this.horizon.update(deltaTime, this.currentSpeed);
+     }
    }

    // ...
  },
};

解释一下这段代码:

if (this.playingIntro) {
  this.horizon.update(0, this.currentSpeed);
} else {
  deltaTime = !this.activated ? 0 : deltaTime;
  this.horizon.update(deltaTime, this.currentSpeed);
}

当程序走 if 逻辑的时候,this.horizon.update 接收到的第一个参数为 0,这样在这个方法内部计算出来的位移也是 0。所以只要还在执行开场动画,地面就不会移动。当程序走 else 逻辑的时候,开场动画执行完毕,此时 playIntro 函数已经执行结束,this.activated 值为 truedeltaTime 值大于零,计算出的地面位移就不再为 0

这样,就实现了地面的开场动画:

查看添加或修改的代码,戳这里
监听窗口 blur、focus 事件

接下来要实现的效果是:浏览器窗口失焦时游戏暂停,聚焦时游戏继续。

Runner 原型链上添加方法,来判断浏览器窗口是否失焦:

Runner.prototype = {
  // 当页面失焦时,暂停游戏,否则进行游戏
  onVisibilityChange: function (e) {
    if (document.hidden || document.webkitHidden || e.type == "blur" ||
      document.visibilityState != "visible") {
      this.stop();
    } else if (!this.crashed) {
      this.play();
    }
  },
  play: function () {
    if (!this.crashed) {
      this.setPlayStatus(true);
      this.paused = false;
      this.time = getTimeStamp();
      this.update();
    }
  },
  stop: function () {
    this.setPlayStatus(false);
    this.paused = true;
    cancelAnimationFrame(this.raqId);
    this.raqId = 0;
  },
};

startGame 方法中添加对 blur、focus 事件的监听:

Runner.prototype = {
  startGame: function () {
    // ...

+   window.addEventListener(Runner.events.BLUR,
+     this.onVisibilityChange.bind(this));

+   window.addEventListener(Runner.events.FOCUS,
+     this.onVisibilityChange.bind(this));
  },
};

补充数据:

Runner.events = {
  // ...

+ BLUR: "blur",
+ FOCUS: "focus"
};

效果如下:

查看添加或修改的代码,戳这里
实现街机模式

Runner 原型链上添加方法:

Runner.prototype = {
  // 设置进入街机模式时 canvas 容器的缩放比例
  setArcadeModeContainerScale: function () {
    var windowHeight = window.innerHeight;
    var scaleHeight = windowHeight / this.dimensions.HEIGHT;
    var scaleWidth = window.innerWidth / this.dimensions.WIDTH;
    var scale = Math.max(1, Math.min(scaleHeight, scaleWidth));
    var scaledCanvasHeight = this.dimensions.HEIGHT * scale;

    // 将 canvas 横向占满屏幕,纵向距离顶部 10% 窗口高度处
    var translateY = Math.ceil(Math.max(0, (windowHeight - scaledCanvasHeight -
        Runner.config.ARCADE_MODE_INITIAL_TOP_POSITION) *
        Runner.config.ARCADE_MODE_TOP_POSITION_PERCENT)) *
        window.devicePixelRatio;
    this.containerEl.style.transform = "scale(" + scale + ") translateY(" +
        translateY + "px)";
  },
  // 开启街机模式(全屏)
  setArcadeMode: function () {
    document.body.classList.add(Runner.classes.ARCADE_MODE);
    this.setArcadeModeContainerScale();
  },
};

补充数据:

Runner.config = {
  // ...
  
+ ARCADE_MODE_INITIAL_TOP_POSITION: 35,  // 街机模式时,canvas 距顶部的初始距离
+ ARCADE_MODE_TOP_POSITION_PERCENT: 0.1, // 街机模式时,canvas 距页面顶部的距离,占屏幕高度的百分比
};

Runner.classes = {
  // ...

+ ARCADE_MODE: "arcade-mode",
};

定义 CSS 类 arcade-mode 里的样式:

.arcade-mode,
.arcade-mode .runner-container,
.arcade-mode .runner-canvas {
  image-rendering: pixelated;
  max-width: 100%;
  overflow: hidden;
}

.arcade-mode .runner-container {
  left: 0;
  right: 0;
  margin: auto;
  transform-origin: top center;
  transition: transform 250ms cubic-bezier(0.4, 0.0, 1, 1) .4s;
  z-index: 2;
}

最后调用 setArcadeMode 方法,就可以进入街机模式:

Runner.prototype = {
  startGame: function () {
+   this.setArcadeMode();      // 进入街机模式

    // ...
  },
};

效果如下:

查看添加或修改的代码,戳这里

Demo 体验地址:https://liuyib.github.io/blog/demo/game/google-dino/arcade-mode/

上一篇 下一篇
Chrome 小恐龙游戏源码探究二 -- 让地面动起来 Chrome 小恐龙游戏源码探究四 -- 随机绘制云朵

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/103883.html

相关文章

  • Chrome 恐龙游戏源码探究四 -- 随机绘制云朵

    摘要:文章首发于我的博客前言上一篇文章小恐龙游戏源码探究三进入街机模式实现了开场动画和街机模式。 文章首发于我的 GitHub 博客 前言 上一篇文章:《Chrome 小恐龙游戏源码探究三 -- 进入街机模式》 实现了开场动画和街机模式。这一篇文章中,将实现云朵的随机绘制。 云朵类 Cloud 定义云朵类 Cloud: /** * 云朵类 * @param {HTMLCanvasEle...

    svtter 评论0 收藏0
  • Chrome 恐龙游戏源码探究一 -- 绘制静态地面

    摘要:首先是绘制静态的地面。上一篇下一篇无小恐龙游戏源码探究二让地面动起来 文章首发于我的 GitHub 博客 目录 Chrome 小恐龙游戏源码探究一 -- 绘制静态地面 Chrome 小恐龙游戏源码探究二 -- 让地面动起来 Chrome 小恐龙游戏源码探究三 -- 进入街机模式 Chrome 小恐龙游戏源码探究四 -- 随机绘制云朵 Chrome 小恐龙游戏源码探究五 -- 随机绘...

    lixiang 评论0 收藏0
  • Chrome 恐龙游戏源码探究八 -- 奔跑的恐龙

    摘要:例如,将函数修改为小恐龙眨眼这样小恐龙会不停的眨眼睛。小恐龙的开场动画下面来实现小恐龙对键盘按键的响应。接下来还需要更新动画帧才能实现小恐龙的奔跑动画。 文章首发于我的 GitHub 博客 前言 上一篇文章:《Chrome 小恐龙游戏源码探究七 -- 昼夜模式交替》实现了游戏昼夜模式的交替,这一篇文章中,将实现:1、小恐龙的绘制 2、键盘对小恐龙的控制 3、页面失焦后,重新聚焦会重置...

    paulquei 评论0 收藏0
  • Chrome 恐龙游戏源码探究七 -- 昼夜模式交替

    摘要:文章首发于我的博客前言上一篇文章小恐龙游戏源码探究六记录游戏分数实现了游戏分数最高分数的记录和绘制。这一篇文章中将实现昼夜模式交替的的效果。原来的游戏中,昼夜交替每米触发一次,这里为了演示,改成了米触发一次。 文章首发于我的 GitHub 博客 前言 上一篇文章:《Chrome 小恐龙游戏源码探究六 -- 记录游戏分数》实现了游戏分数、最高分数的记录和绘制。这一篇文章中将实现昼夜模式...

    curried 评论0 收藏0
  • Chrome 恐龙游戏源码探究九 -- 游戏碰撞检测

    摘要:文章首发于我的博客前言上一篇文章小恐龙游戏源码探究八奔跑的小恐龙实现了小恐龙的绘制以及键盘对小恐龙的控制,这一篇文章中将实现游戏的碰撞检测。 文章首发于我的 GitHub 博客 前言 上一篇文章:《Chrome 小恐龙游戏源码探究八 -- 奔跑的小恐龙》实现了小恐龙的绘制以及键盘对小恐龙的控制,这一篇文章中将实现游戏的碰撞检测。 碰撞检测原理 这个游戏采用的检测方法是盒子碰撞,这种检...

    cpupro 评论0 收藏0

发表评论

0条评论

yeooo

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<