Chrome自带恐龙小游戏的源码研究(三)
在上一篇《Chrome自带恐龙小游戏的源码研究(二)》中实现了云朵的绘制和移动,这一篇主要研究如何让游戏实现昼夜交替。
昼夜交替的效果主要是通过样式来完成,但改变样式的时机则由脚本控制。 首先对游戏容器使用transition创建一个贝塞尔渐变:
.game-body{
transition:filter 1.5s cubic-bezier(0.65, 0.05, 0.36, 1),
background-color 1.5s cubic-bezier(0.65, 0.05, 0.36, 1);
/*告诉浏览器即将对某元素执行什么动画,这样浏览器可以提前进行准备来优化动画效果,使动画更为流畅*/
will-change: filter,background-color;
}
渐变作用于两个属性:filter及background-color。
.inverted{
filter: invert(100%); /*反色*/
background-color: #000; /*改变背景为黑色(入夜效果)*/
}
当符合条件时,游戏容器将添加inverted样式,以达到昼夜交替效果。
接下来看一下代码实现:
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.INVERT_FADE_DURATION = 5000;
function NightMode(canvas,spritePos,containerWidth) {
this.spritePos = spritePos;
this.canvas = canvas;
this.ctx = canvas.getContext("2d");
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];
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.INVERT_FADE_DURATION) {
NightMode.invertTimer = 0;
NightMode.invertTrigger = false;
NightMode.inverted = document.body.classList.toggle('inverted',NightMode.invertTrigger);
} else if(NightMode.invertTimer) {
NightMode.invertTimer += deltaTime;
} else {
//每500帧触发黑夜,这里只是为了模拟效果,完整游戏中是每700米触发一次黑夜
NightMode.invertTrigger = !(gameFrame % 500);
if(NightMode.invertTrigger && NightMode.invertTimer === 0) {
NightMode.invertTimer += deltaTime;
NightMode.inverted = document.body.classList.toggle('inverted',NightMode.invertTrigger);
}
}
},
reset: function() {
this.currentPhase = 0;
this.opacity = 0;
this.update(false);
}
};
最后添加测试代码:
window.onload = function () {
var h = new HorizonLine(c,spriteDefinition.HORIZON);
var cloud = new Cloud(c,spriteDefinition.CLOUD,DEFAULT_WIDTH);
var night = new NightMode(c,spriteDefinition.MOON,DEFAULT_WIDTH);
var startTime = 0;
var deltaTime;
(function draw(time) {
gameFrame++;
ctx.clearRect(0,0,600,150);
time = time || 0;
deltaTime = time - startTime;
h.update(deltaTime,3);
cloud.updateClouds(0.2);
night.invert(deltaTime);
startTime = time;
window.requestAnimationFrame(draw,c);
})();
};
下面是运行效果(每500帧切换一次):
// 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] = 0; i--) {
Cloud.clouds[i].update(speed);
}
var lastCloud = Cloud.clouds[numClouds - 1];
if(numClouds 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);
}
};
//endregion
//region NightMode
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.INVERT_FADE_DURATION = 5000;
function NightMode(canvas,spritePos,containerWidth) {
this.spritePos = spritePos;
this.canvas = canvas;
this.ctx = canvas.getContext("2d");
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 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.INVERT_FADE_DURATION) {
NightMode.invertTimer = 0;
NightMode.invertTrigger = false;
NightMode.inverted = a.classList.toggle('inverted',NightMode.invertTrigger);
} else if(NightMode.invertTimer) {
NightMode.invertTimer += deltaTime;
} else {
//每500帧触发黑夜,这里只是为了模拟效果,完整游戏中是每700米触发一次黑夜
NightMode.invertTrigger = !(gameFrame % 500);
if(NightMode.invertTrigger && NightMode.invertTimer === 0) {
NightMode.invertTimer += deltaTime;
NightMode.inverted = a.classList.toggle('inverted',NightMode.invertTrigger);
}
}
},
reset: function() {
this.currentPhase = 0;
this.opacity = 0;
this.update(false);
}
};
//endregion
window.onload = function () {
var h = new HorizonLine(c,spriteDefinition.HORIZON);
var cloud = new Cloud(c,spriteDefinition.CLOUD,DEFAULT_WIDTH);
var night = new NightMode(c,spriteDefinition.MOON,DEFAULT_WIDTH);
var startTime = 0;
var deltaTime;
(function draw(time) {
gameFrame++;
ctx.clearRect(0,0,600,150);
time = time || 0;
deltaTime = time - startTime;
h.update(deltaTime,3);
cloud.updateClouds(0.2);
night.invert(deltaTime);
startTime = time;
window.requestAnimationFrame(draw,c);
})();
};
})();
// ]]>
Chrome自带恐龙小游戏的源码研究(三)的更多相关文章
- Chrome自带恐龙小游戏的源码研究(四)
在上一篇<Chrome自带恐龙小游戏的源码研究(三)>中实现了让游戏昼夜交替,这一篇主要研究如何绘制障碍物. 障碍物有两种:仙人掌和翼龙.仙人掌有大小两种类型,可以同时并列多个:翼龙按高. ...
- Chrome自带恐龙小游戏的源码研究(一)
目录 Chrome自带恐龙小游戏的源码研究(一)——绘制地面 Chrome自带恐龙小游戏的源码研究(二)——绘制云朵 Chrome自带恐龙小游戏的源码研究(三)——昼夜交替 Chrome自带恐龙小游戏 ...
- Chrome自带恐龙小游戏的源码研究(七)
在上一篇<Chrome自带恐龙小游戏的源码研究(六)>中研究了恐龙的跳跃过程,这一篇研究恐龙与障碍物之间的碰撞检测. 碰撞盒子 游戏中采用的是矩形(非旋转矩形)碰撞.这类碰撞优点是计算比较 ...
- Chrome自带恐龙小游戏的源码研究(完)
在上一篇<Chrome自带恐龙小游戏的源码研究(七)>中研究了恐龙与障碍物的碰撞检测,这一篇主要研究组成游戏的其它要素. 游戏分数记录 如图所示,分数及最高分记录显示在游戏界面的右上角,每 ...
- Chrome自带恐龙小游戏的源码研究(六)
在上一篇<Chrome自带恐龙小游戏的源码研究(五)>中实现了眨眼睛的恐龙,这一篇主要研究恐龙的跳跃. 恐龙的跳跃 游戏通过敲击键盘的Spacebar或者Up来实现恐龙的跳跃.先用一张图来 ...
- Chrome自带恐龙小游戏的源码研究(五)
在上一篇<Chrome自带恐龙小游戏的源码研究(四)>中实现了障碍物的绘制及移动,从这一篇开始主要研究恐龙的绘制及一系列键盘动作的实现. 会眨眼睛的恐龙 在游戏开始前的待机界面,如果仔细观 ...
- Chrome自带恐龙小游戏的源码研究(二)
在上一篇<Chrome自带恐龙小游戏的源码研究(一)>中实现了地面的绘制和运动,这一篇主要研究云朵的绘制. 云朵的绘制通过Cloud构造函数完成.Cloud实现代码如下: Cloud.co ...
- WinFom中经典小游戏(含源码)
最近整理了若干经典的小游戏,无聊时可以打发时间.程序本身不大,练手非常不错,主要是GDI编程,主界面地址如下图所示 源码下载方式 1,关注微信公众号:小特工作室(也可直接扫描签名处二维码) 2,发送: ...
- github下载下来的C#控制台小游戏[含源码]
早就听说了github是世界最大的源码库,但自己却不是很懂,今天去研究了下,注册了一个帐号,然后在上面搜索了一下C# game,然后发现有许多的游戏. 随意地选择了一个,感觉比较简单,于是就下载了下来 ...
随机推荐
- MacPro 系统空间竟占90G,如何清理--OmniDiskSweeper
MacPro 经常提示我磁盘空间已满,管理磁盘空间. 然后我就管理了一下,发现系统竟占90个G,有点懵逼.然后网上查了资料,发现这个超级好用的工具OmniDiskSweeper. 打开是这样的! 然后 ...
- SQL2005、SQL2008如何压缩日志文件(log) 如何清除日志
原文发布时间为:2010-11-01 -- 来源于本人的百度文章 [由搬家工具导入] ALTER DATABASE [DataBaseName] SET ...
- ashx接收参数 ashx传递参数
原文发布时间为:2009-09-30 -- 来源于本人的百度文章 [由搬家工具导入] Handler.ashx文件: <%@ WebHandler Language="C#" ...
- 禁止复制(copy),禁用鼠标右键!
<SCRIPT> //加入页面保护 function rf() {return false; } document.oncontextmenu = rf function keydown( ...
- java常见设计模式简要总结
设计模式六大原则 1.开放封闭原则:对扩展开放,对修改封闭,意即程序拓展时不要动原有的代码 2.LSP原则:任何基类可以出现的地方,子类一定可以出现 3.依赖倒置原则:使用接口,依赖于抽象而不是具体 ...
- AtCoder Regular Contest 090 F - Number of Digits
题目链接 Description For a positive integer \(n\), let us define \(f(n)\) as the number of digits in bas ...
- python+requests接口自动化测试框架
1.首先,我们先来理一下思路. 正常的接口测试流程是什么? 脑海里的反应是不是这样的: 确定测试接口的工具 —> 配置需要的接口参数 —> 进行测试 —> 检查测试结果(有的需要数据 ...
- android init.rc命令快速对照表
注1:另外还讲述了怎样输出log: Debugging notes---------------By default, programs executed by init will drop stdo ...
- 最简单方法远程调试Python多进程子程序
Python 2.6新增的multiprocessing,即多进程,给子进程代码调试有点困难,比如python自带的pdb如果直接在子进程代码里面启动会抛出一堆异常,原因是子进程的stdin/out/ ...
- Centos7 安装docker ce
一. 安装docker 1.升级rpm包 yum -y update 2. 通过命令设置Docker CE 资源库: yum install -y yum-utils yum-config-manag ...