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

  在上一篇《Chrome自带恐龙小游戏的源码研究(二)》中实现了云朵的绘制和移动,这一篇主要研究如何让游戏实现昼夜交替。

  昼夜交替的效果主要是通过样式来完成,但改变样式的时机则由脚本控制。 首先对游戏容器使用transition创建一个贝塞尔渐变:

1 .game-body{
2     transition:filter 1.5s cubic-bezier(0.65, 0.05, 0.36, 1),
3                background-color 1.5s cubic-bezier(0.65, 0.05, 0.36, 1);
4      
5     /*告诉浏览器即将对某元素执行什么动画,这样浏览器可以提前进行准备来优化动画效果,使动画更为流畅*/
6     will-change: filter,background-color;
7 }

渐变作用于两个属性:filter及background-color。

1 .inverted{
2     filter: invert(100%); /*反色*/
3     background-color: #000; /*改变背景为黑色(入夜效果)*/
4 }

当符合条件时,游戏容器将添加inverted样式,以达到昼夜交替效果。

接下来看一下代码实现:

 1 NightMode.config = {
 2     FADE_SPEED: 0.035,    //淡入淡出速度
 3     HEIGHT: 40,    //月亮高度
 4     MOON_SPEED: 0.25,    //月亮移动速度
 5     NUM_STARS: 2,    //星星数量
 6     STAR_SIZE: 9,    //星星宽度
 7     STAR_SPEED: 0.3,//星星速度
 8     STAR_MAX_Y: 70,    //星星在画布上出现的位置
 9     WIDTH: 20    //半个月度宽度
10 };
11 //月亮在不同时期有不同的位置
12 NightMode.phases = [140,120,100,60,40,20,0];
13 
14 //时间记录
15 NightMode.invertTimer = 0;
16 //是否可以进行昼夜交替
17 NightMode.inverted = false;
18 //用于控制样式切换
19 NightMode.invertTrigger = false;
20 //黑夜持续时间
21 NightMode.INVERT_FADE_DURATION = 5000;
  1 function NightMode(canvas,spritePos,containerWidth) {
  2     this.spritePos = spritePos;
  3     this.canvas = canvas;
  4     this.ctx = canvas.getContext("2d");
  5     this.containerWidth = containerWidth;
  6     this.xPos = containerWidth - 50;    //月亮的x坐标
  7     this.yPos = 30;    //月亮的y坐标
  8     this.currentPhase = 0;
  9     this.opacity = 0;
 10     this.stars = [];    //用于存储星星
 11     this.drawStars = false; //是否绘制星星
 12     this.placeStars();    //放置星星
 13 }
 14 
 15 NightMode.prototype = {
 16     update:function(activated) {
 17         //若夜晚模式处于激活状态且opacity为0时
 18         //对月亮周期进行更新
 19         if(activated && this.opacity == 0) {
 20             this.currentPhase++;
 21             if(this.currentPhase >= NightMode.phases.length) {
 22                 this.currentPhase = 0;
 23             }
 24         }
 25 
 26         //淡入
 27         if(activated && (this.opacity < 1 || this.opacity == 0)) {
 28             this.opacity += NightMode.config.FADE_SPEED;
 29         } else if(this.opacity > 0) {//淡出
 30             this.opacity -= NightMode.config.FADE_SPEED;
 31         }
 32 
 33         //当opacity大于0时移动月亮位置
 34         if(this.opacity > 0) {
 35             this.xPos = this.updateXPos(this.xPos, NightMode.config.MOON_SPEED);
 36 
 37             //移动星星
 38             if(this.drawStars) {
 39                 for (var i = 0; i < NightMode.config.NUM_STARS; i++) {
 40                     this.stars[i].x = this.updateXPos(this.stars[i].x,NightMode.config.STAR_SPEED);
 41                 }
 42             }
 43             this.draw();
 44         } else {
 45             this.opacity = 0;
 46             this.placeStars();
 47         }
 48         this.drawStars = true;
 49     },
 50     updateXPos: function(currentPos, speed) {
 51         if (currentPos < -NightMode.config.WIDTH) {
 52             currentPos = this.containerWidth;
 53         } else {
 54             currentPos -= speed;
 55         }
 56         return currentPos;
 57     },
 58     draw:function() {
 59         //周期为3时画满月
 60         var moonSourceWidth = this.currentPhase == 3 ? NightMode.config.WIDTH * 2 :
 61             NightMode.config.WIDTH;
 62         var moonSourceHeight = NightMode.config.HEIGHT;
 63         //从雪碧图上获取月亮正确的形状
 64         var moonSourceX = this.spritePos.x + NightMode.phases[this.currentPhase];
 65         var moonOutputWidth = moonSourceWidth;
 66         var starSize = NightMode.config.STAR_SIZE;
 67         var starSourceX = spriteDefinition.STAR.x;
 68 
 69         this.ctx.save();
 70         //画布透明度也随之变化
 71         this.ctx.globalAlpha = this.opacity;
 72 
 73         if (this.drawStars) {
 74             for (var i = 0; i < NightMode.config.NUM_STARS; i++) {
 75                 this.ctx.drawImage(imgSprite,
 76                     starSourceX, this.stars[i].sourceY,
 77                     starSize, starSize,
 78                     Math.round(this.stars[i].x), this.stars[i].y,
 79                     NightMode.config.STAR_SIZE, NightMode.config.STAR_SIZE);
 80             }
 81         }
 82 
 83         this.ctx.drawImage(imgSprite,
 84             moonSourceX, this.spritePos.y,
 85             moonSourceWidth, moonSourceHeight,
 86             Math.round(this.xPos), this.yPos,
 87             moonOutputWidth, NightMode.config.HEIGHT);
 88 
 89         this.ctx.globalAlpha = 1;
 90         this.ctx.restore();
 91     },
 92     placeStars:function() {
 93         //将画布分为若干组
 94         var segmentSize = Math.round(this.containerWidth /NightMode.config.NUM_STARS);
 95         for (var i = 0; i < NightMode.config.NUM_STARS; i++) {
 96             this.stars[i] = {};
 97             //每组星星位置随机
 98             this.stars[i].x = getRandomNum(segmentSize * i, segmentSize * (i + 1));
 99             this.stars[i].y = getRandomNum(0, NightMode.config.STAR_MAX_Y);
100             this.stars[i].sourceY = spriteDefinition.STAR.y + NightMode.config.STAR_SIZE * i;
101         }
102     },
103     invert:function(deltaTime) {
104         this.update(NightMode.inverted);
105 
106         //黑夜持续时间5秒
107         if(NightMode.invertTimer > NightMode.INVERT_FADE_DURATION) {
108             NightMode.invertTimer = 0;
109             NightMode.invertTrigger = false;
110             NightMode.inverted = document.body.classList.toggle('inverted',NightMode.invertTrigger);
111         } else if(NightMode.invertTimer) {
112             NightMode.invertTimer += deltaTime;
113         } else {
114             //每500帧触发黑夜,这里只是为了模拟效果,完整游戏中是每700米触发一次黑夜
115             NightMode.invertTrigger = !(gameFrame % 500);
116             if(NightMode.invertTrigger && NightMode.invertTimer === 0) {
117                 NightMode.invertTimer += deltaTime;
118                 NightMode.inverted = document.body.classList.toggle('inverted',NightMode.invertTrigger);
119             }
120         }
121     },
122     reset: function() {
123         this.currentPhase = 0;
124         this.opacity = 0;
125         this.update(false);
126     }
127 };

最后添加测试代码:

 1      window.onload = function () {
 2             var h = new HorizonLine(c,spriteDefinition.HORIZON);
 3             var cloud = new Cloud(c,spriteDefinition.CLOUD,DEFAULT_WIDTH);
 4             var night = new NightMode(c,spriteDefinition.MOON,DEFAULT_WIDTH);
 5             var startTime = 0;
 6             var deltaTime;
 7             (function draw(time) {
 8                 gameFrame++;
 9                 ctx.clearRect(0,0,600,150);
10                 time = time || 0;
11                 deltaTime = time - startTime;
12                 h.update(deltaTime,3);
13                 cloud.updateClouds(0.2);
14                 night.invert(deltaTime);
15                 startTime = time;
16                 window.requestAnimationFrame(draw,c);
17             })();
18         };

下面是运行效果(每500帧切换一次):

 

posted @ 2016-11-22 10:46  逐影  阅读(...)  评论(...编辑  收藏