• 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帧切换一次):

     

  • 相关阅读:
    虚拟机网络模型详解,看这篇就够了(图文并茂)
    快速理解 VirtualBox 的四种网络连接方式
    Linux下桥接模式详解一
    CentOS7 下VNC Server远程桌面配置详解
    BAN如何下载?
    SNS交换机(OEM博科FC交换机)怎样设置密码策略
    SNS交换机(OEM博科FC交换机)的端口状态及排查流程说明
    SNS2124(OEM博科FC交换机)忘记密码,密码初始化
    博科SAN交换机zone配置(华为SNS系列交换机为例OEM博科)
    手淫让我付出了沉重的代价
  • 原文地址:https://www.cnblogs.com/undefined000/p/trex_3.html
Copyright © 2020-2023  润新知