首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

HTML5 2D 游戏开发 实现重力和添加声音(2)

HTML5 2D 游戏开发 实现重力和添加声音(2)

合并重力重力是一种特殊情形重力会产生非线性运动,我在本系列的中已讨论过。在这篇文章中,我使用时间变换器实现了非线性的跳跃,通过淡出和淡入的渐变功能模拟了重力效果。如果修改这些变换器,则可生成一个无限的非线性运动范围,这就意味着重力通常是线性运动的一种特殊情形。

在地面附近,重力会以 9.81m/s 的加速度让物体加速下落,也就是说物体每下落一秒,它的速度就会增加近 10 m/s(或 32 ft/s)。对于游戏开发人员而言,考虑重力的结果是,不但要基于 sprite 的速度计算其位置,还必须计算它们下落时的速度。
计算重力影响下的速度的数学原理很简单:将重力加速度乘以 sprite 的已下落时间,再将该值与 sprite 开始下落时的初始速度相加。就像等式一样,令人困惑的部分常常不是数学计算,而是单位,因为上一个等式的结果的单位是 m/s。为了让这个数字有意义,Snail Bait 将其转换为像素每秒,使用以下算法来计算 sprite 位置:
  • 游戏开始时:
    • 以像素为单位定义游戏的宽度。
    • 以米为单位任意定义游戏的宽度。
    • 将以像素为单位的宽度除以以米为单位的宽度,以获得像素 / 米的比率。
  • 随后,对于每个动画帧:
    • 使用重力加速度 (9.81 m/s/s) 计算以 m/s 为单位的速度。
    • 将以 m/s 为单位的速度乘以 中计算出的像素 / 米比率,得到像素 / 秒的比率。
    • 根据以像素 / 秒为单位的速度计算位置。
现在 , 您可将前面的算法转换为代码。第一步是定义重力和游戏的像素 / 米比率,如清单 3 所示:
清单 3. 与重力和下落相关的常量
1
2
3
4
5
6
7
var SnailBait = { // SnailBait constructor function
  ...
  
  this.GRAVITY_FORCE = 9.81,
  this.PIXELS_PER_METER = this.canvas.width / 10; // 10 meters, randomly selected width
  ...
}




当跑步小人跑离平台的边缘或从下方与平台相撞时,她以垂直速度 0 开始下落。但是,当跑步小人跳跃结束未落到平台上时,她就开始以跳跃下降过程的结束时的垂直速度下落。清单 4 展示了跑步小人的跳跃行为如何使用中定义的 GRAVITY_FORCE 和 PIXELS_PER_METER 来计算这一初始速度:
清单 4. 跳跃结束时的下落
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
this.jumpBehavior= {
  ...

  finishDescent: function (sprite) {
     sprite.stopJumping();

     if (snailBait.isOverPlatform(sprite) !== -1) {
        sprite.top = sprite.verticalLaunchPosition;
     }
     else {
        // The argument passed to the sprite's fall() method
        // is the sprite's initial velocity when it begins to fall
   
        sprite.fall(snailBait.GRAVITY_FORCE*
                    (sprite.descendAnimationTimer.getElapsedTime()/1000) *
                    snailBait.PIXELS_PER_METER);
     }
  },
};




跑步小人在下降结束时的垂直速度是重力加速度乘以下降所用时间,再乘以游戏的像素 / 米比率:
(9.81 m/s/s) * (所用的下降秒数)* (800 像素 / 10 m)
计算结果以像素 / 秒为单位,并表示跑步小人在跳跃的下降阶段结束时的垂直速度。(请参阅本系列的 ,查看跳跃行为实现的其余部分。)
Snail Bait 使用 GRAVITY_FORCE 和 PIXELS_PER_METER 计算的跑步小人速度的另一位置是在跑步小人的下落行为中,如清单 5 所示:
清单 5. 设置 sprite 速度并计算跑步小人在当前帧中的垂直下落距离
1
2
3
4
5
6
7
8
9
10
11
12
13
14
this.fallBehavior= {
  ...
  setSpriteVelocity: function (sprite) {
     sprite.velocityY = sprite.initialVelocityY +

                        snailBait.GRAVITY_FORCE*
                        (sprite.fallAnimationTimer.getElapsedTime()/1000) *
                        snailBait.PIXELS_PER_METER;
  },

  calculateVerticalDrop: function (sprite, fps) {
     return sprite.velocityY / fps;
  },
};




下落行为的 setSpriteVelocity()方法依据跑步小人下落的时长来设置她的速度。该方法会小心地合并可能由 中的跳跃行为设定的跑步小人初始速度。
calculateVerticalDrop()方法使用基于时间的运动 —我已在本系列的第 2 篇文章的 一节中讨论过 —基于 setSpriteVelocity()所计算的速度和当前的帧率来计算跑步小人的垂直下落。
本系列的 中已详细讨论过,Snail Bait 会在每个动画帧中迭代它的所有 sprite。对于每个可见的 sprite,Snail Bait 迭代该 sprite 的行为,从而依次调用每个行为的 execute()方法。清单 6 显示了跑步小人的下落行为的 execute()方法:
清单 6. 下落行为的 execute()方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
this.fallBehavior= {
     execute: function (sprite, time, fps) { // sprite is the runner
        var deltaY;

        if (sprite.jumping) {
           return;
        }

        if (this.isOutOfPlay(sprite) || sprite.exploding) {
           if (sprite.falling) {
              sprite.stopFalling();
           }
           return;
        }
         
        if (!sprite.falling) {
           if (!sprite.exploding && !this.isPlatformUnderneath(sprite)) {
              sprite.fall();
           }
           return;

        }

        this.setSpriteVelocity(sprite);
        deltaY = this.calculateVerticalDrop(sprite, fps);
               
        if (!this.willFallBelowCurrentTrack(sprite, deltaY)) {
           sprite.top += deltaY;
        }
        else { // will fall below current track
           if (this.isPlatformUnderneath(sprite)) {
              this.fallOnPlatform(sprite);
              sprite.stopFalling();
           }
           else {
              sprite.top += deltaY;
              sprite.track--;
           }
        }
     }
  },




当跑步小人跳跃、掉下来摔死或爆炸时,下落行为的 execute()方法会启动或停止下落并返回。如果她在下落过程中摔死或爆炸了,该方法会调用她的 stopFalling()方法。如果跑步小人未下落,目前未爆炸,并且她的下方没有平台,那么该方法会调用她的 fall()方法。
满足这些前提条件后,下落行为的 execute()方法会计算跑步小人的当前速度和位置。如果这个新位置未将跑步小人放在其当前平台下方,那么该方法会将她移动到那里。否则,跑步小人会下落到其平台下方,所以该方法检查她下方是否有另一个平台。如果有,该方法将她放在这个平台上并停止下落。如果跑步小人下落到其平台下方,并且她下方没有任何平台,那么该方法会将她移动到新位置并递减她当前的轨道。
下落行为的 execute()方法使用 4 个便捷方法。清单 7 中的两个方法确定跑步小人是否摔死或降落到当前轨道下方:
清单 7. 确定跑步小人摔死还是将落到当前轨道下方
1
2
3
4
5
6
7
8
9
10
11
this.fallBehavior = {
  isOutOfPlay: function (sprite) {
     return sprite.top > snailBait.TRACK_1_BASELINE;
  },

  willFallBelowCurrentTrack: function (sprite, deltaY) {
     return sprite.top + sprite.height + deltaY >
            snailBait.calculatePlatformTop(sprite.track);
  },
  ...
};




清单 8 中的便捷方法确定跑步小人下方是否有一个平台,并让跑步小人落到一个平台上:
清单 8. 确定跑步小人下方是否有一个平台,并让跑步小人落到一个平台上
1
2
3
4
5
6
7
8
9
10
11
this.fallBehavior= {
  isPlatformUnderneath: function (sprite) {
     return snailBait.isOverPlatform(sprite) !== -1;
  },

  fallOnPlatform: function (sprite) {
     sprite.top = snailBait.calculatePlatformTop(sprite.track) - sprite.height;
     sprite.stopFalling();
  },
  ...
};




一定要认识到,下落行为的 execute()方法仅在跑步小人的 falling属性为 true时才垂直移动她。跑步小人的 stopFalling()方法将该属性设置为 false,如清单 9 所示,将跑步小人的垂直速度设置为 0并停止跑步小人的下落动画计时器:
清单 9. 下落行为的 stopFalling()方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
SnailBait.prototype = {
  ...

  equipRunnerForFalling: function () {
     ...

     this.runner.stopFalling= function () {
        this.falling = false;
        this.velocityY = 0;
        this.fallAnimationTimer.stop();
     }
  },
  ...
};

返回列表