文章首发于我的
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,
};
然后在 Runner
的 update
要领中挪用上面定义的 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
值为 true
,deltaTime
值大于零,计算出的地面位移就不再为 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 小恐龙游戏源码探讨四 — 随机绘制云朵 |