在上一篇《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. 分裂游戏(bzoj 1188)

    Description 聪聪和睿睿最近迷上了一款叫做分裂的游戏. 该游戏的规则试: 共有 n 个瓶子, 标号为 0,1,2.....n-1, 第 i 个瓶子中装有 p[i]颗巧克力豆,两个人轮流取豆子 ...

  2. Page_Load与Page_PreRender的执行顺序

    原文发布时间为:2009-10-25 -- 来源于本人的百度文章 [由搬家工具导入] Page_PreRender 服务器控件将要呈现给其包含的 控件时发生。简单的理解为page中的控件渲染调用此事件 ...

  3. 先将Excel导入到gridview再添加进数据库【Excel批量添加】

    原文发布时间为:2008-10-27 -- 来源于本人的百度文章 [由搬家工具导入] 前台: <%@ Page Language="C#" AutoEventWireup=& ...

  4. PowerDesigner 中将Comment(注释)及Name(名称)内容互相COPY的VBS代码

    在用PowerDesigner时.常常在NAME或Comment中写中文在Code中写英文.Name只会显示给我们看,Code会使用在代码中.但Comment中的文字会保存到数据库TABLE的Desc ...

  5. Atcoder CODE FESTIVAL 2017 qual C D - Yet Another Palindrome Partitioning 回文串划分

    题目链接 题意 给定一个字符串(长度\(\leq 2e5\)),将其划分成尽量少的段,使得每段内重新排列后可以成为一个回文串. 题解 分析 每段内重新排列后是一个回文串\(\rightarrow\)该 ...

  6. Day 23 异常处理

    异常处理 一.异常基本形式 # if # def test: # pass # class Foo # pass def test(): ''' 异常的基本类型和相关 :return: ''' try ...

  7. Codeforces Round #455 (Div. 2) A. Generate Login【贪心】

    A. Generate Login time limit per test 2 seconds memory limit per test 256 megabytes input standard i ...

  8. BZOJ——3412: [Usaco2009 Dec]Music Notes乐谱

    http://www.lydsy.com/JudgeOnline/problem.php?id=3412 Time Limit: 3 Sec  Memory Limit: 128 MBSubmit:  ...

  9. 轻量i3wm配置使用笔记 -- 主题切换器(j4-make-config)

    快速切换主题 j4-make-config介绍: j4-make-config脚本可以方便地在几组"主题"之间切换,还可以根据当前工作的环境,轻松地从几个不同的配置部分组合一个完整 ...

  10. MySQL OCP

    http://www.royalwzy.com/ http://www.aixchina.net/home/space.php?uid=898169