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

时间:2023-03-09 09:29:17
Chrome自带恐龙小游戏的源码研究(一)

目录

Chrome自带恐龙小游戏的源码研究(一)——绘制地面

Chrome自带恐龙小游戏的源码研究(二)——绘制云朵

Chrome自带恐龙小游戏的源码研究(三)——昼夜交替

Chrome自带恐龙小游戏的源码研究(四)——绘制障碍物

Chrome自带恐龙小游戏的源码研究(五)——绘制霸王龙

Chrome自带恐龙小游戏的源码研究(六)——跳跃

Chrome自带恐龙小游戏的源码研究(七)——碰撞检测

Chrome自带恐龙小游戏的源码研究(完)——游戏高分与其它要素

  

  众所周知,Chrome浏览器在网络不通的情况下,会出现一个霸王龙翻越障碍的小游戏: aaarticlea/png;base64,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" alt="" />

  这个游戏做得小巧精致,于是探究了一下它的源码,发现代码写得相当严谨并且富有技巧性,用来学习再好不过了。

  游戏虽然看起来简单,但也有几千行的代码量。主要包括五个构造函数:

  • 游戏逻辑控制函数Runner

  • 背景管理函数Horizon

    • 地面 (HorizonLine)
    • 云朵 (Cloud)
    • 昼夜更替 (NightMode)
    • 障碍物 (Obstacle)
  • 霸王龙函数Trex

  • 分数记录函数DistanceMeter

  • 游戏结束操作面板函数GameOverPanel

  其余的方法还包含一些对移动设备的适配、针对不同屏幕加载不同的资源 、声音的播放等等。这是游戏用到的雪碧图:

aaarticlea/png;base64,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" alt="" />

  为方便研究,从简单的背景管理函数开始。首先是地面的绘制。地面绘制通过HorizonLine完成:

 //定义属性

 HorizonLine.dimensions = {
WIDTH:600, //宽600
HEIGHT:12, //高12像素
YPOS:127 //在canvas中的位置
}; var spriteDefinition = {
HORIZON: {x: 2, y: 54}//地面在雪碧图中的位置
}; /**
* canvas 地面将绘制到此画布上
* spritePos 地面在雪碧图中的坐标
*/
function HorizonLine(canvas,spritePos) {
this.spritePos = spritePos;
this.canvas = canvas;
this.ctx = canvas.getContext("2d");
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原型链中的方法:

 HorizonLine.prototype = {
setSourceDimesions:function() {
//地面在画布上的位置
this.xPos = [0,this.dimensions.WIDTH];//0,600
this.yPos = this.dimensions.YPOS;
},
//随机地形
getRandomType:function() {
//返回第一段地形或者第二段地形
return Math.random() > this.bumpThreshold ? this.dimensions.WIDTH : 0;
},
draw:function() {
//使用9参数的drawImage方法
this.ctx.drawImage(imgSprite,
this.sourceXPos[0], this.spritePos.y,
this.dimensions.WIDTH, this.dimensions.HEIGHT,
this.xPos[0],this.yPos,
this.dimensions.WIDTH,this.dimensions.HEIGHT); this.ctx.drawImage(imgSprite,
this.sourceXPos[1], this.spritePos.y,
this.dimensions.WIDTH, this.dimensions.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) {
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] = this.dimensions.WIDTH;
}
};

原型链中的方法实现了地面的运动和随机地形。

最后测试一下这一段代码:

 window.onload = function () {
var h = new HorizonLine(canvas,spriteDefinition.HORIZON);
var startTime = 0;
(function draw(time) {
ctx.clearRect(0,0,600,150);
time = time || 0;
h.update(time - startTime,3);
startTime = time;
window.requestAnimationFrame(draw);
})();
};

运行效果:

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

// this.bumpThreshold ? this.dimensions.WIDTH : 0;
},
draw:function() {
this.ctx.drawImage(imgSprite,
this.sourceXPos[0], this.spritePos.y,
this.dimensions.WIDTH, this.dimensions.HEIGHT,
this.xPos[0],this.yPos,
this.dimensions.WIDTH,this.dimensions.HEIGHT);

this.ctx.drawImage(imgSprite,
this.sourceXPos[1], this.spritePos.y,
this.dimensions.WIDTH, this.dimensions.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;

if(this.xPos[line1]

这样地面的绘制及滚动就完成了。