内容


HTML5 2D 游戏开发

设置舞台

实现游戏对象、暂停、冻结、解冻和键盘输入

Comments

系列内容:

此内容是该系列 # 部分中的第 # 部分: HTML5 2D 游戏开发

敬请期待该系列的后续内容。

此内容是该系列的一部分:HTML5 2D 游戏开发

敬请期待该系列的后续内容。

游戏开发的许多方面都和玩游戏没有关系。显示说明、暂停游戏、级别之间的过渡和滚动游戏分数,这些都是游戏开发人员必须在游戏本身以外实现的一些特性。

当游戏的灵感来临时,这些灵感中通常不包括显示高分数或级别之间的过渡的巧妙方式,开发人员会很自然地深入研究如何实现游戏机制,但对于游戏的基础架构却没有太多的想法。但在大多数项目中,如果想在开发后添加功能,所需的工作量比从一开始就添加功能要大得多。

在本系列的 上一期文章 中,我讨论了图形和动画,这些是 Snail Bait 游戏的基础内容。在本文中,我将临时转向去实现该游戏的一些基础架构。首先,我将 Snail Bait 的代码封装在一个 Game 对象中。最初实现该游戏时,我就是从这一步开始的,但在上一期 文章中,我不想在对象中实现它们而混淆了对图形和动画的讨论,所以我将对 Game 对象的介绍推迟到了现在。

我还将告诉您如何暂停和冻结 Snail Bait,以及随后如何利用动画倒计时解冻并重启游戏。在文章的结尾,我会回到游戏机制的主题,向您展示如何通过处理键盘事件来控制跑步小人的垂直位置。

在本文中,您将学习以下内容:

  • 将游戏函数封装在一个对象中。
  • 暂停和恢复游戏。
  • 当窗口失去焦点时自动暂停游戏。
  • 当窗口重新获得焦点时,利用动画的倒计时继续游戏。
  • 暂时显示给用户的消息(被称为 toast)。
  • 处理键盘输入。

在本文中,您将学习如何定义和实例化 JavaScript 对象,如何使用 CSS3 过渡,以及如何结合使用 setTimeout() 和这些过渡来实现分步动画。

游戏对象

在本系列文章中,到现在为止,我已经实现了所有 Snail Bait 函数,并将它们的几个变量定义为全局变量。当然,我们以后不会再这样做。如果您尚未了解全局变量的可恶之处,请参阅 参考资料,获得来自 Douglas Crockford 和 Nicholas Zakas 等 JavaScript 名人的支持论据。

从现在开始,我不再使用全局变量,而是将所有 Snail Bait 函数和变量封装在一个对象中。该对象由两部分组成,如清单 1 和清单 2 所示。(本文的完整样例代码请参见 下载。)

清单 1 是本游戏的构造函数,它定义了对象的属性:

清单 1. 本游戏的构造函数(部分清单)
var SnailBait = function (canvasId) {
   this.canvas  = document.getElementById(canvasId);
   this.context = this.canvas.getContext('2d');

   // HTML elements 

   this.toast = document.getElementById('toast'),
   this.fpsElement = document.getElementById('fps');

   // Constants

   this.LEFT = 1;
   this.RIGHT = 2;
   ...

   // Many more attributes are defined in the rest of this function
};

清单 2 是本游戏的原型,它定义了对象的方法:

清单 2. 是本游戏的原型(部分清单)
SnailBait.prototype = {
   // The  and  methods were
   // discussed in the  in this series.


   draw function (now) {
      this.setPlatformVelocity(); 
      this.setOffsets();
   
      this.drawBackground();
   
      this.drawRunner();
      this.drawPlatforms();
   },
   
   drawRunner: function () {
      this.context.drawImage(this.runnerImage,
         this.STARTING_RUNNER_LEFT,
         this.calculatePlatformTop(this.runnerTrack) - this.RUNNER_HEIGHT);
   },
   ...

   // Many more methods are defined in the rest of this object
};

为了在整个系列中添加一些新特性,我需要添加和删除一些方法,以及修改一些方法来实现。表 1 列出了 Snail Bait 的方法,因为它们会在本文结尾处出现:

表 1. Snail Bait 在此开发阶段的方法(按调用顺序列出)
方法描述
initializeImages()初始化游戏的图像。背景图像的 onload 事件处理器调用 start()
start()通过调用 requestAnimationFrame() 启动游戏,在可以绘制第一个动画帧的时候,它调用了 animate() 方法。
splashToast() [1]向玩家显示一个临时消息。
animate() [2]如果游戏没有暂停,此方法将会绘制下一个动画帧,并调用 requestNextAnimationFrame() 来安排 animate() 的另一次调用。如果游戏暂停,那么 animate() 会等待 200 毫秒,然后调用 requestNextAnimationFrame()
calculateFps()根据自最后一个动画帧起所经过的时间,计算帧速率。
draw()绘制一个动画帧。
setTranslationOffsets()为背景和平台设置过渡偏移。
setBackgroundTranslationOffset()根据当前时间设置背景过渡偏移。
setPlatformTranslationOffset()根据当前时间设置平台过渡偏移。
setPlatformVelocity()将平台速度设置为背景速度的倍数,以产生轻微的视差效果。
drawBackground()平移 Canvas 坐标系统,绘制背景两次,并将坐标系平移回它的原始位置。
drawRunner() [3]使用 drawImage() 绘制跑步小人。
drawPlatforms() [3]使用 2D 上下文的 strokeRect()fillRect() 绘制矩形平台。
calculatePlatformTop()针对给定轨道计算平台顶部的 Y 坐标(平台在三个水平轨道之一上移动)。
turnLeft()将背景和平台向右滚动。
turnRight()将背景和平台向左滚动。
togglePaused() [1]切换游戏的暂停状态。

[1] 在本文中介绍
[2] 由浏览器调用
[3] 在本系列的下一期文章中将被替换

在本系列前两期文章中,我介绍过在 表 1 中列出的大部分方法,它们在当时只是函数。在本文中,我会讨论两个新的方法:togglePaused()splashToast(),还会修改其他方法,比如 animate()

清单 1清单 2 中的 JavaScript 定义了一个函数和一个原型,但没有实例化一个 SnailBait 对象。我会在下一节中完成此操作。

启动游戏

清单 3 显示了启动游戏的 JavaScript。该清单的开头定义了三个 SnailBait 方法的实现:animate()start()initializeImages()

清单 3. 启动
SnailBait.prototype = {
   ...

   // The 'this' variable in the animate() method is
   // the window object, so the method uses snailBait instead

   animate: function (now) { 
      snailBait.fps = snailBait.calculateFps(now); 
      snailBait.draw(now);

      requestNextAnimationFrame(snailBait.animate);
   },

   start: function () {
      this.turnRight();                     // Sets everything in motion
      this.splashToast('Good Luck!', 2000); // "Good Luck" is displayed for 2 seconds

      requestNextAnimationFrame(this.animate);
   },

   initializeImages: function () {
      this.background.src = 'images/background_level_one_dark_red.png';
      this.runnerImage.src = 'images/runner.png';
   
      this.background.onload = function (e) {

         // ...the 'this' variable is the window object,
         // so this function uses snailBait instead.
     
         snailBait.start();
      };
   },
}; // End of SnailBait.prototype


// Launch game

var snailBait = new SnailBait(); // Note: By convention, the object
                                     // reference starts with lowercase, but
                                     // the function name starts with uppercase

snailBait.initializeImages();

清单 3 中的 JavaScript 实例化一个 SnailBait 对象,并调用了它的 initializeImages() 方法,该方法实现背景图像的 onload 事件处理器。当图像加载时,该事件处理器调用了 start() 方法。

start() 方法调用 turnRight(),它设置移动中的背景和平台。还调用了 splashToast(),以显示 Good Luck! 两秒。最后,start() 调用了 requestNextAnimationFrame() polyfill(我们曾在本系列第二期文章中讨论过它,请参阅那篇文章的 A requestAnimationFrame() polyfill 部分),它最终调用该游戏的 animate() 方法。

animate() 方法将绘制当前帧,然后调用 requestNextAnimationFrame()(指定它本身作为回调函数)来保持动画。

这就是游戏开始的方式。接下来,我将告诉您在开始游戏之后如何暂停它。

暂停游戏

HTML5 游戏(尤其是视频游戏)必须能够暂停。在清单 4 中,我已经修改了 Snail Bait 的游戏循环,以便暂停和取消暂停游戏:

清单 4. 暂停和取消暂停
var SnailBait = function (canvasId) {
   ...
   this.paused = false,
   this.PAUSED_CHECK_INTERVAL = 200; // milliseconds
   ...
};

SnailBait.prototype = {
   animate: function (now) { 
      if (snailBait.paused) {

         // Check again in snailBait.PAUSED_CHECK_INTERVAL milliseconds

         setTimeout( function () {

            requestNextAnimationFrame(snailBait.animate);

         }, snailBait.PAUSED_CHECK_INTERVAL);
      }
      else {

         // The game loop from 

         snailBait.fps = snailBait.calculateFps(now); 
         snailBait.draw(now);
         requestNextAnimationFrame(snailBait.animate);
      }
   },

   togglePaused: function () {
      this.paused = !this.paused;
   },
};

togglePaused() 方法简单地切换该游戏的 paused 变量。当该变量为 true (意味着游戏已暂停)时,animate() 方法不会执行游戏循环。

每秒检查 60 次(假设帧速率为 60fps),查看是否应该恢复某个暂停的帧,这是没必要的,且效率低下,因此,清单 4 中的 animate() 方法只会等待 200ms,然后调用 requestNextAnimationFrame() polyfill,在到时间绘制下一个动画帧时,它就会安排对 animate() 的另一次调用。

当窗口失去焦点时自动暂停

W3C 的 Timing control for script-based animations(基于脚本的动画时序控制) 规范对于使用 requestAnimationFrame() 实现的动画具有下列规定:

如果页面目前不可见,可以大幅裁剪该页面上的动画,那么就不会经常更新这些页面,因此只会占用极少的 CPU 资源。

术语大幅节流意味着,浏览器以极低的帧速率(通常介于 1 到 10 fps 之间)调用您的动画回调,如图 1 所示,帧速率在窗口重新获得焦点后立即达到 6fps:

图 1. 在失去焦点和重新获得焦点后的 Snail Bait
重新获得焦点后的 Snail Bait 的屏幕截图
重新获得焦点后的 Snail Bait 的屏幕截图

大幅节流的帧速率可能对碰撞检测算法造成破坏,该算法通常基于帧速率确定是否发生了(或将发生)碰撞。您可以在游戏的窗口失去焦点时暂停游戏,并在窗口重新获得焦点时重新启动它,避免因大幅节流的帧速率而造成碰撞检测的崩溃。在清单 5 中,您可以看到如何做到这一点:

清单 5. 自动暂停
window.onblur = function () { // window looses focus
   if (!snailBait.paused) {
      snailBait.togglePaused();
   }
};

window.onfocus = function () { // window regains focus
   if (snailBait.paused) {
      snailBait.togglePaused();
   }
};

当窗口失去焦点时,您不仅要暂停游戏,还应该在暂停游戏时冻结 它。

冻结游戏

暂停游戏所涉及的不仅仅是简单地中止动画。游戏应该恢复到离开它们时的确切位置清单 4 的出现满足了这一要求。毕竟,在游戏暂停后,什么也不会发生,就好像游戏已经恢复为和它暂停之前一样。但实际情况并非如此,因为所有动画(包括 Snail Bait 的动画)的主要参数是时间。

如我在第二期文章中所讨论的(请参阅这篇文章的 requestAnimationFrame() 部分),requestAnimationFrame() 将时间传递给指定的回调函数。在使用 Snail Bait 的情况下,该回调函数是 animate() 方法,它随后会将时间传递给 draw() 方法。

当游戏暂停时,即使动画没有运行,时间依然有增无减地继续前进。而且,由于 Snail Bait 的 draw() 方法根据从 animate() 接收的时间绘制下一个动画帧,所以 清单 4 中实现的 togglePaused() 会导致在恢复暂停游戏时,游戏的时间寸步难行。

清单 6 显示了 Snail Bait 如何避免暂停的游戏在恢复时出现时间的变化:

清单 6. 冻结游戏
var SnailBait = function (canvasId) {
   ...
   this.paused = false,
   this.pauseStartTime = 0,
   this.totalTimePaused = 0,
   this.lastAnimationFrameTime = 0,
   ...
};

SnailBait.prototype = {
   ...
   calculateFps: function (now) {
      var fps = 1000 / (now - this.lastAnimationFrameTime);
      this.lastAnimationFrameTime = now;
   
      if (now - this.lastFpsUpdateTime > 1000) {
         this.lastFpsUpdateTime = now;
         this.fpsElement.innerHTML = fps.toFixed(0) + 'fps';

      }

      return fps; 
   },

   togglePaused: function () {
      var now = +new Date();

      this.paused = !this.paused;
   
      if (this.paused) {
         this.pauseStartTime = now;
      }
      else {
         this.lastAnimationFrameTime += (now - this.pauseStartTime);
      }
   },
};

清单 6 中,我修改了 Snail Bait 的 togglePaused()calculateFps() 方法,以计算游戏被暂停(如果有的话)的时间量。

为了计算前一个动画帧的帧速率,我从当前时间减去我绘制最后一帧的时间,并用 1000 被除以该值,这使得我获得的帧速率是以秒为单位,而不是以毫秒为单位。(请参阅第二期文章的 以 fps 计算动画速率 部分,了解关于计算帧速率的更多信息。)

当游戏恢复时,我将游戏暂停的时间量加到最后一个动画帧的时间。这个加法有效地清除了暂停,游戏恢复到在暂停开始时它的确切位置。

当窗口获得焦点时解冻游戏

当游戏恢复时,玩家会喜欢平稳地过渡到操作,为他们提供一些时间来重新获得控制。在这段时间里,在恢复游戏之前提供有关剩余时间量的反馈,这会是一个好主意。Snail Bait 通过在 toast 中显示倒计时来实现该反馈,所以我从 toast 的概述开始这个讨论。

Toast

toast 是游戏暂时向玩家显示的某些消息,像图 2 中的 Good Luck! toast:

图 2. toast
Snail Bait 中的 Good Luck! toast 的屏幕截图
Snail Bait 中的 Good Luck! toast 的屏幕截图

像 Snail Bait 本身,Snail Bait toast 是使用 HTML、CSS 和 JavaScript 的组合来实现的,如接下来的三个清单所示。

清单 7 显示一个 toast 的 HTML 代码:

清单 7. toast:HTML
<!DOCTYPE html>
<html>
   <head>
      ...
   </head>

   <body>
      <div id='wrapper'>
         <!-- Toast...................................................-->

         <div id='toast'></div>
         ...
   
      </div>
      ...
  </body>
</html>

实现 Snail Bait 的 Good Luck! toast 的 CSS 如清单 8 所示:

清单 8. toast:CSS
#toast {
   position: absolute;
   ...

   -webkit-transition: opacity 0.5s;
   -moz-transition: opacity 0.5s;
   -o-transition: opacity 0.5s;
   transition: opacity 0.5s;

   opacity: 0;
   z-index: 1;
   display: none;
}

清单 9 显示 Good Luck! toast 的 JavaScript:

清单 9. toast:JavaScript
var SnailBait =  function () {
   ...
   this.toast = document.getElementById('toast'),
   this.DEFAULT_TOAST_TIME = 3000, // 3 seconds
   ...
};

SnailBait.prototype = {
   ...
   start: function () {
      ...
      snailBait.splashToast('Good Luck!');
   },

   splashToast: function (text, howLong) {
      howLong = howLong || this.DEFAULT_TOAST_TIME;

      toast.style.display = 'block';
      toast.innerHTML = text;

      setTimeout( function (e) {
         toast.style.opacity = 1.0; // After toast is displayed
      }, 50);

      setTimeout( function (e) {
         toast.style.opacity = 0; // Starts CSS3 transition

         setTimeout( function (e) {
            toast.style.display = 'none'; // Just before CSS3 animation concludes
         }, 480);
      }, howLong);
   },
   ...
}

正如前面这三个清单中的实现,toast 只是 DIV,您可以在 清单 7 中看到。事情在 清单 8 中变得更为有趣,其中列出了 DIV 的 CSS。DIV 的位置是 absolute,这意味着它可以显示在其他 DIV 的上面或下面,而不是前面或后面。toastDIVz-index 值也是 1,这意味着它始终显示在游戏画布的上面,其 z-index 的默认值为 0。最后,toast 元素的 CSS 定义一个绑定到 opacity 属性的 0.5 秒的过渡,当更改该属性时,CSS 用 0.5 秒时间将 DIV 从之前的不透明度平滑过渡到新的值。

清单 9splashToast() 方法中,事情变得更加有趣,toast 会在一段指定的时间内显示。当 Snail Bait 调用 splashToast() 时,默认的显示时间为 3 秒,toast 淡入 0.5 秒,短暂地显示 2.5 秒,然后淡出 0.5 秒。下面是它的工作原理:

splashToast() 方法首先将 toastDIVdisplay 属性设置为 block,这通常会使得 DIV 变得可见。但是,因为它的 opacity 属性的初始值为 0,所以 toastDIV 仍保持不可见。然后 splashToast()toastDIV 的内部 HTML 设置为您传递给方法的文本,但不透明度设置保持不变,所以设置文本也不会使得 DIV 可见。

为了使得 toastDIV 可见,我将它的不透明度设置为 1.0。该设置触发因我在 清单 8 中指定的过渡而产生的 CSS3 动画,但是,仅当不透明度设置稍后(在本例中是 50 毫秒)产生看起来很奇怪的 setTimeout() 的结果时,才会修改不透明度设置,在该函数中它是封闭的。这是因为:

只能对有中间状态的元素属性指定 CSS3 过渡。例如,如果您将不透明度从 0.2 修改为 0.3(随机选择的两个数字),中间不透明度为 0.21、0.22 等。

过渡需要中间状态,这是有一定道理的;如果没有中间状态,就没有一个明确的方法来指定过渡的动画。例如,这就是为什么您不能为 display 属性指定一个过渡的原因,它没有中间状态。不仅如此,如果您修改了 display 属性,那么 CSS3 将不再接受您为其他任何属性指定的任何过渡。这也是有一定道理的,因为您在让 CSS3 执行互相冲突的两件事:例如,通过修改 display 属性让元素立即可见,但又使用 opacity 属性的过渡使其慢慢地淡入视图。CSS3 不能两件事同时做,所以它选择了修改 display 属性。

如果 splashToast() 同时设置 toastDIVdisplayopacity 属性,CSS3 会忽略不透明度的过渡,因此,在设置 display 属性之后,该方法会将不透明度设置为 1.0,更确切地说,在大约 50ms 之后会执行该操作。

最后,当所需的显示时间过去后,splashToast() 会将 toastDIVopacity 属性重新设置为 0,这会再次触发一个 0.5 秒的 CSS3 动画。在 CSS3 动画开始两秒钟后之后,splashToast() 方法会将 display 属性重新设置为 0

解冻 Snail Bait

当 Snail Bait 在暂停后恢复播放时,它通过三秒钟的倒计时让玩家有时间做好准备,如图 3 所示:

图 3. 解冻过程中的倒计时
Snail Bait 解冻过程中的倒计时屏幕截图
Snail Bait 解冻过程中的倒计时屏幕截图

清单 10 显示了倒计时的 JavaScript:

清单 10. 倒计时:JavaScript
var SnailBait = function (canvasId) {
   ...
   this.toast = document.getElementById('toast'),
};


window.onblur = function (e) {  // Pause if unpaused
   if (!snailBait.paused) {
      snailBait.togglePaused();
   }
};

window.onfocus = function (e) {  // unpause if paused
   var originalFont = snailBait.toast.style.fontSize;

   if (snailBait.paused) {
      snailBait.toast.style.font = '128px fantasy';

      snailBait.splashToast('3', 500); // Display 3 for one half second

      setTimeout(function (e) {
         snailBait.splashToast('2', 500); // Display 2 for one half second

         setTimeout(function (e) {
            snailBait.splashToast('1', 500); // Display 1 for one half second

            setTimeout(function (e) {
               snailBait.togglePaused();

               setTimeout(function (e) { // Wait for '1' to disappear
                  snailBait.toast.style.fontSize = originalFont;
               }, 2000);
            }, 1000);
         }, 1000);
      }, 1000);
   }
};

当 Snail Bait 窗口重新获得焦点时,它会使用 splashToast() 方法启动倒计时。每个数字淡入 0.5 秒,然后淡出 0.5 秒。一旦倒计时为零,onfocus 处理器就会重新启动游戏。

然而,如果玩家在倒计时过程中激活了另一个窗口或选项卡,清单 10 中的代码就会无法正常工作,因为无论窗口是否获得焦点,游戏都会在倒计时结束时重新开始。这很容易解决,利用一个 windowHasFocus 标实即可,如清单 11 所示:

清单 11. 在倒计时期间失去焦点的处理
var SnailBait = function (canvasId) {
   ...
   this.windowHasFocus = true,
   ...
};
...

SnailBait.prototype = {
   ...

   splashToast: function (text, howLong) {
      howLong = howLong || this.DEFAULT_TOAST_TIME;

      toast.style.display = 'block';
      toast.innerHTML = text;

      setTimeout( function (e) {
         if (snailBait.windowHasFocus) {
            toast.style.opacity = 1.0; // After toast is displayed
         }
      }, 50);

      setTimeout( function (e) {
         if (snailBait.windowHasFocus) {
            toast.style.opacity = 0; // Starts CSS3 transition
         }

         setTimeout( function (e) { 
            if (snailBait.windowHasFocus) {
               toast.style.display = 'none'; 
            }
         }, 480);
      }, howLong);
   },
   ...
};
...

window.onblur = function (e) {  // pause if unpaused
   snailBait.windowHasFocus = false;
   
   if (!snailBait.paused) {
      snailBait.togglePaused();
   }
};

window.onfocus = function (e) {  // unpause if paused
   var originalFont = snailBait.toast.style.fontSize;

   snailBait.windowHasFocus = true;

   if (snailBait.paused) {
      snailBait.toast.style.font = '128px fantasy';

      snailBait.splashToast('3', 500); // Display 3 for one half second

      setTimeout(function (e) {
         snailBait.splashToast('2', 500); // Display 2 for one half second

         setTimeout(function (e) {
            snailBait.splashToast('1', 500); // Display 1 for one half second

            setTimeout(function (e) {
               if ( snailBait.windowHasFocus) {
                  snailBait.togglePaused();
               }

               setTimeout(function (e) { // Wait for '1' to disappear
                  snailBait.toast.style.fontSize = originalFont;
               }, 2000);
            }, 1000);
         }, 1000);
      }, 1000);
   }
};

键盘输入

在 Snail Bait 中,玩家可以使用键盘来控制跑步小人,所以我会对该游戏如何处理键盘输入做一个简短的讨论,并以此来结束这篇文章。dk 键可以让移动的跑步小人向左和向右移动,而 jf 则分别让跑步小人跳跃和下落。图 4 显示了跳上第三个平台的轨道之后的跑步小人:

图 4. 在轨道之间跳跃之后的奔跑
在轨道之间跳跃之后的 Snail Bait 跑步小人的屏幕截图
在轨道之间跳跃之后的 Snail Bait 跑步小人的屏幕截图

您只能将键盘事件侦听器添加到 focusable HTML 元素。canvas 元素是不能获得焦点的,所以 Snail Bait 将一个 onkeydown 事件处理器添加到 window 对象,如清单 12 所示:

清单 12. 对键盘输入的反应
var runnerTrack = 1,
    BACKGROUND_VELOCITY = 42;

function turnLeft() {
   bgVelocity = -BACKGROUND_VELOCITY;
}

function turnRight() {
   bgVelocity = BACKGROUND_VELOCITY;
}

window.onkeydown = function (e) {
   var key = e.keyCode;

   if (key === 80 || (paused && key !== 80)) {  // p
      togglePaused();
   }

   if (key === 68 || key === 37) { // d or left arrow
      turnLeft();
   }
   else if (key === 75 || key === 39) { // k or right arrow
      turnRight();
   }
   else if (key === 74) { // j
      if (runnerTrack === 3) {
         return;
      }
      runnerTrack++;
   }
   else if (key === 70) { // f
      if (runnerTrack === 1) {
         return;
      }
      runnerTrack--;
   }
};

重要的是要认识到,Snail Bait 的游戏循环在不断运行。在浏览器已经准备好画下一个动画帧时,浏览器就会调用 animate() 函数,而 animate() 反过来又不断地调用 draw()(在清单 2 中列出)。

由于游戏循环在不断运行,键盘事件处理器只需设置游戏的变量即可。例如,当您按下 k 将跑步小人向右移动,事件处理器会将 bgVelocity 设置为 BACKGROUND_VELOCITY = 42(像素/秒),当您按下 d 将跑步小人向左移动,事件处理器会将 bgVelocity 设置为 -42 像素/秒。一直到后来,当游戏绘制下一个动画帧时,那些设置才会生效。

结束语

在本系列的下一篇文章中,我将向您演示如何将 Snail Bait 的插入图形转换成被称为精灵 (sprite) 的动画对象。您将看到如何采用几种不同的方式绘制精灵,包括从一个精灵表 (spritesheet) 绘制它们,并且您将看到如何将它们纳入 Snail Bait 的现有代码中。下次再见!


下载资源


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Java technology, Web development
ArticleID=855645
ArticleTitle=HTML5 2D 游戏开发: 设置舞台
publish-date=01212013