在上一篇《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自带恐龙小游戏的源码研究(三)的更多相关文章

  1. Chrome自带恐龙小游戏的源码研究(四)

    在上一篇<Chrome自带恐龙小游戏的源码研究(三)>中实现了让游戏昼夜交替,这一篇主要研究如何绘制障碍物. 障碍物有两种:仙人掌和翼龙.仙人掌有大小两种类型,可以同时并列多个:翼龙按高. ...

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

    目录 Chrome自带恐龙小游戏的源码研究(一)——绘制地面 Chrome自带恐龙小游戏的源码研究(二)——绘制云朵 Chrome自带恐龙小游戏的源码研究(三)——昼夜交替 Chrome自带恐龙小游戏 ...

  3. Chrome自带恐龙小游戏的源码研究(七)

    在上一篇<Chrome自带恐龙小游戏的源码研究(六)>中研究了恐龙的跳跃过程,这一篇研究恐龙与障碍物之间的碰撞检测. 碰撞盒子 游戏中采用的是矩形(非旋转矩形)碰撞.这类碰撞优点是计算比较 ...

  4. Chrome自带恐龙小游戏的源码研究(完)

    在上一篇<Chrome自带恐龙小游戏的源码研究(七)>中研究了恐龙与障碍物的碰撞检测,这一篇主要研究组成游戏的其它要素. 游戏分数记录 如图所示,分数及最高分记录显示在游戏界面的右上角,每 ...

  5. Chrome自带恐龙小游戏的源码研究(六)

    在上一篇<Chrome自带恐龙小游戏的源码研究(五)>中实现了眨眼睛的恐龙,这一篇主要研究恐龙的跳跃. 恐龙的跳跃 游戏通过敲击键盘的Spacebar或者Up来实现恐龙的跳跃.先用一张图来 ...

  6. Chrome自带恐龙小游戏的源码研究(五)

    在上一篇<Chrome自带恐龙小游戏的源码研究(四)>中实现了障碍物的绘制及移动,从这一篇开始主要研究恐龙的绘制及一系列键盘动作的实现. 会眨眼睛的恐龙 在游戏开始前的待机界面,如果仔细观 ...

  7. Chrome自带恐龙小游戏的源码研究(二)

    在上一篇<Chrome自带恐龙小游戏的源码研究(一)>中实现了地面的绘制和运动,这一篇主要研究云朵的绘制. 云朵的绘制通过Cloud构造函数完成.Cloud实现代码如下: Cloud.co ...

  8. WinFom中经典小游戏(含源码)

    最近整理了若干经典的小游戏,无聊时可以打发时间.程序本身不大,练手非常不错,主要是GDI编程,主界面地址如下图所示 源码下载方式 1,关注微信公众号:小特工作室(也可直接扫描签名处二维码) 2,发送: ...

  9. github下载下来的C#控制台小游戏[含源码]

    早就听说了github是世界最大的源码库,但自己却不是很懂,今天去研究了下,注册了一个帐号,然后在上面搜索了一下C# game,然后发现有许多的游戏. 随意地选择了一个,感觉比较简单,于是就下载了下来 ...

随机推荐

  1. 只有mdf文件而没有ldf文件修复方法

    原文发布时间为:2010-09-16 -- 来源于本人的百度文章 [由搬家工具导入] 只有mdf文件而没有ldf文件修复log文件或者重新生成一个log文件的方法 EXEC   sp_attach_s ...

  2. Linux 6.x 下Oracle 11g R2 安装配置

    Oracle 11g R2 数据库安装硬件配置要求: 最小内存 1 GB of RAM 虚拟内存容量,这个oracle也有要求,不用担心此时的swap分区不够oracle的要求 .虚拟内存swap如何 ...

  3. python脚本传递参数

    给python程序传递参数 运行python脚本时有时需要执行实传递参数 在linux下: [root@Test ~]# cat /opt/python.py #!/usr/local/bin/pyt ...

  4. 宝宝舌苔发白,消化不好 http://wenwen.soso.com/z/q103192661.htm

    你好,宝宝咳嗽,如果舌苔是白的,则是风寒咳嗽,说明孩子寒重,咳嗽的痰也较稀.白黏,并兼有鼻塞流涕,这时应吃一些温热.化痰止咳的食品.如果孩子的舌苔是黄.红,则是风热咳嗽,说明孩子内热较大,咳嗽的痰黄. ...

  5. linux创建和查看用户命令

    1.创建一个叫做hadoop的用户,用户的目录是/home/hadoop useradd -d /home/hadoop hadoop 2.输入密码 passwd hadoop 3.删除用户 user ...

  6. AC日记——[SCOI2007]蜥蜴 bzoj 1066

    1066 思路: 网络流最大流: 拆点,每个点拆成两个,流量为这个点的高度: 注意,文中说的距离是曼哈顿距离(劳资以为开根号wa了不知道多少次): 每两个距离不大于d的点连边,流量inf: 如果距离能 ...

  7. AC日记——[ZJOI2009]狼和羊的故事 bzoj 1412

    1412 思路: 最小割: 狼作为一个点集a,空领地作为点集b,羊作为点集c: s向a连边,c向t连边,a向b连边,b向b连边,b向c连边: 如何理解最小割? a,c之间割掉最少的路径(栅栏)使其没有 ...

  8. Goodbye 2017 B

    [题意]: 鲍勃编程一个机器人在2d迷宫中导航.迷宫有一些障碍.空单元格用'.'表示,其中障碍物用'#'表示.迷宫中有一个机器人.它的起始位置用字符“S”表示.这个位置没有任何障碍.迷宫中也有一个出口 ...

  9. TopCoder SRM 301 Div2 Problem 1000 CorrectingParenthesization(区间DP)

    题意  给定一个长度为偶数的字符串.这个字符串由三种括号组成. 现在要把这个字符串修改为一个符合括号完全匹配的字符串,改变一个括号的代价为$1$,求最小总代价. 区间DP.令$dp[i][j]$为把子 ...

  10. SpringBoot第十一篇:SpringBoot+MyBatis+Thymelaf实现CRUD

    作者:追梦1819 原文:https://www.cnblogs.com/yanfei1819/p/10936304.html 版权声明:本文为博主原创文章,转载请附上博文链接! 引言   总结前面几 ...