Разработка 2D-игр на HTML5: Манипулирование временем, часть 1

Реализация прыжков с линейным движением

В предлагаемом цикле статей знаток HTML5 Дэвид Гири шаг за шагом демонстрирует процесс создания 2D-видеоигры на HTML5. В первой из двух статей показано, как реализовать манипулятор прыжков для спрайта бегуна.

Дэвид Джири, президент, Clarity Training, Inc.

David GearyАвтор, лектор и консультант Дэвид Джири является президентом компании Clarity Training, Inc., Он обучает разработчиков создавать Web-приложения с использованием JSF и Google Web Toolkit (GWT). Он участвовал в экспертных группах JSTL 1.0 и JSF 1.0/2.0, был соавтором сертификационного экзамена Web Developer Certification Exam компании Sun, а также принимал участие в проектах с открытым кодом, в том числе Apache Struts и Apache Shale. Книга Дэвида Graphic Java Swing стала одной из самых продаваемых книг о Java, а Core JSF (в соавторстве с Кэем Хорстманом) - одна из самых продаваемых книг о JSF. Дэвид регулярно выступает на конференциях и встречах пользовательских групп. Он является регулярным участником конференций NFJS с 2003 года, проводил курсы в университете Java и дважды удостаивался звания JavaOne rock star.



16.05.2013

В прошлой статье из этого цикла я говорил о том, как инкапсулировать действия спрайтов — такие как бег, падение, ходьба или взрыв — в подключаемые объекты, называемые манипуляторами. Во время выполнения можно легко снабдить любой спрайт любым набором манипуляторов. В число многих других преимуществ подобная гибкость способствует выявлению таких аспектов игры, которые в противном случае оставались бы в тени.

В этой статье я продолжу обсуждение поведения спрайтов с парой нюансов. Во-первых, это первая из двух последовательных статей цикла, посвященных одному и тому же манипулятору спрайта: прыжкам бегуна. К концу статьи «Манипулирование временем, часть 2» Snail Bait обретет, наконец, ту естественную последовательность прыжка, которая изображена на рисунке 1.

Рисунок 1. Естественная последовательность прыжка
Прыжок бегуна Snail Bait

Отсрочка обнаружения столкновений

Я откладываю описание процесса обнаружения столкновений Snail Bait, чтобы сосредоточиться на движении бегуна во время прыжка. С обнаружением столкновений бегун приземляется на платформу, тем самым обрывая прыжок; без обнаружения столкновений прыжки продолжаются до их завершения. Чтобы получить весь эффект прыжка, загрузите код для этой статьи и опробуйте его самостоятельно.

Во-вторых, прыжок, в отличие от тех манипуляторов, о которых я говорил в предыдущей статье, не повторяется бесконечно. По этой простой причине игра Snail Bait в ходе прыжка должна следить за временем. Это порождает потребность в чем-то вроде секундомера, поэтому мы создадим JavaScript-секундомер и будем использовать его, чтобы засекать время подъема и спуска бегуна во время прыжка.

Траектории бегуна и поверхность платформы

Как показано на рисунке 2, платформы Snail Bait перемещаются по горизонтали на трех уровнях.

Рисунок 2. Уровни движения платформ
Уровни движения платформ

Расстояние между уровнями составляет 100 пикселей. Это оставляет бегуну, рост которого 60 пикселей, достаточно места для маневра.

Листинг 1 демонстрирует, как Snail Bait устанавливает рост бегуна и уровень платформы. В нем также приведен удобный метод calculatePlatformTop(), который с учетом уровня (1, 2 или 3) возвращает положение поверхности соответствующей платформы.

Листинг 1. Расчет положения поверхности платформы в зависимости от уровня
var SnailBait = function () {
   // Высота элементов анимации бегуна:

   this.RUNNER_CELLS_HEIGHT = 60, // пикселей

   // Поверхность платформ:

   this.TRACK_1_BASELINE = 323, // пикселей
   this.TRACK_2_BASELINE = 223,
   this.TRACK_3_BASELINE = 123,
   ...
};
...

SnailBait.prototype = {
   ...
   calculatePlatformTop: function (track) {
      var top;

      if      (track === 1) { top = this.TRACK_1_BASELINE; }
      else if (track === 2) { top = this.TRACK_2_BASELINE; }
      else if (track === 3) { top = this.TRACK_3_BASELINE; }

      return top;
   ...
};

Для размещения почти всех спрайтов в Snail Bait используется метод calculatePlatformTop().


Первоначальная реализация прыжка

В прошлой статье были реализованы простейшие алгоритмы управления прыжками Snail Bait, приведенные в листинге 2.

Листинг 2. Управление прыжками с клавиатуры
window.onkeydown = function (e) {
   var key = e.keyCode;
   ...

   if (key === 74) { // 'j'
      if (snailBait.runner.track === 3) { // Верхний уровень; двигаться больше некуда
         return;
      }

      snailBait.runner.track++;
      snailBait.runner.top = snailBait.calculatePlatformTop(snailBait.runner.track) -
                              snailBait.RUNNER_CELLS_HEIGHT;
   }
};
...

Когда игрок нажимает клавишу j, Snail Bait немедленно ставит ноги бегуна на линию следующего уровня (при условии, что он еще не там), как показано на рисунке 3.

Рисунок 3. Упрощенная последовательность прыжка: просто в реализации, но неестественно
Упрощенная последовательность прыжка бегуна Snail Bait

Реализация прыжка, приведенная в листинге 2, имеет два серьезных недостатка. Во-первых, мгновенное перемещение бегуна с одного уровня на другой далеко от желаемого эффекта. Во-вторых, прыжки реализуются не на том уровне абстракции. Обработчик событий окна не должен непосредственно манипулировать атрибутами бегуна; за прыжки должен отвечать сам бегун.


Переложение ответственности за прыжки на бегуна

В листинге 3 показана исправленная реализация обработчика событий окна onkeydown. Она намного проще, чем реализация из листинга 2, так как ответственность за прыжки перекладывается с обработчика событий на бегуна.

Листинг 3. Обработчик нажатия клавиш окна, делегирование бегуну
window.onkeydown = function (e) {
   var key = e.keyCode;
   ...

   if (key === 74) { // 'j'
      runner.jump();
   }
};

В начале игры Snail Bait вызывает метод equipRunner(), как показано в листинге 4.

Листинг 4. Оснащение бегуна в начале игры
SnailBait.prototype = {
   ...
   start: function () {
      this.createSprites();
      this.initializeImages();
      this.equipRunner();
      this.splashToast('Good Luck!');
   },
};

Метод equipRunner(), показанный в листинге 5, добавляет к бегуну атрибуты и метод jump().

Листинг 5. Оснащение бегуна: метод jump() бегуна
SnailBait.prototype = {
  equipRunner: function () {
    // Эта функция устанавливает атрибуты бегуна:

    this.runner.jumping = false; // 'this' is snailBait
    this.runner.track = this.INITIAL_RUNNER_TRACK;

    ... // Другие атрибуты бегуна опущены для краткости
    // Эта функция также реализует метод jump() бегуна:
    this.runner.jump = function () {
      if ( ! this.jumping) { // «this» ― это бегун.
        this.jumping = true; // Начало прыжка
      }
    };
   },
 },

Представления и контроллеры

Манипулятор прыжков бегуна и соответствующий метод jump() напоминают пару представление/контроллер. То, как Snail Bait рисует бегуна, пока тот прыгает, реализовано в манипуляторе, тогда как метод jump() бегуна действует как простой контроллер, который управляет тем, прыгает ли бегун в данный момент или нет.

Атрибуты бегуна среди прочего указывают его текущий уровень, а также, находится ли он в состоянии прыжка.

Если бегун в данный момент не прыгает, то метод runner.jump() просто устанавливает значение true атрибута jumping бегуна. Snail Bait реализует акт прыжка в отдельном объекте манипулятора, как и для всех прочих манипуляторов бегуна, таких как бег и падение, — и вообще манипуляторов всех спрайтов. При создании бегуна Snail Bait добавляет объект бегуна в таблицу манипуляторов бегуна, как показано в листинге 6.

Листинг 6. Создание бегуна с его манипуляторами
var SnailBait = function () {
   ...
   this.jumpBehavior = {
      execute: function(sprite, time, fps) {

         // здесь реализуется прыжок

      },
      ...
   };
   ...

   this.runner = new Sprite('runner',          // тип
                            this.runnerArtist,  // начертатель
                            [ this.runBehavior, // манипуляторы
                              this.jumpBehavior,
                              this.fallBehavior
                            ]); 
   ...
};

Теперь, когда инфраструктура прыжков готова, можно сосредоточиться исключительно на манипуляторе прыжка.


Манипулятор прыжка

Листинг 7, в котором показана первоначальная реализация манипулятора прыжка бегуна, функционально эквивалентен коду из листинга 2. Если атрибут бегуна jumping— который устанавливается методом бегуна jump() (см. листинг 5) — имеет значение false, то манипулятор не делает ничего. Он не делает ничего и в том случае, если бегун находится на верхней платформе.

Листинг 7. Нереалистичная реализация прыжка
var SnailBait =  function () {
   ...

   this.jumpBehavior = {
      ...

      execute: function(sprite, time, fps) {
         if ( ! sprite.jumping || sprite.track === 3) {
            return;
         }

         sprite.track++;

         sprite.top = snailBait.calculatePlatformTop(sprite.track) -
                      snailBait.RUNNER_CELLS_HEIGHT;

         sprite.jumping = false;
      } 
   },
   ...
};

Бесконечный цикл

Напомним, что Snail Bait, по существу, представляет собой бесконечный цикл, который постоянно выполняет все манипуляторы каждого видимого спрайта. Метод бегуна jump() инициирует прыжок, просто установив значение true атрибута бегуна jumping. Когда Snail Bait выполнит манипулятор прыжка в следующий раз, этот параметр вызовет включение соответствующего поведения.

Если бегун находится в состоянии прыжка и не находится на верхней платформе, то манипулятор прыжка, реализованный в листинге 7, переносит его на следующий уровень и завершает прыжок, присвоив атрибуту jumping значение false.

Как и при реализации прыжка из листинга 2, при реализации из листинга 7 бегун мгновенно перемещается с одного уровня на другой. Для достижения реалистичного изображения прыжков это необходимо делать постепенно, за конечный период времени.


Синхронизированная анимация: секундомеры

До сих пор все движения в Snail Bait были постоянными; например, все спрайты игры, за исключением бегуна, двигаются непрерывно в горизонтальном направлении, а кнопки и улитки постоянно снуют на своих платформах из стороны в сторону. (О том, как реализовано это движение, читайте в разделе Прокрутка фона второй статьи этого цикла.) Монеты, сапфиры и рубины также могут медленно подниматься и опускаться ― без всякого перерыва.

Однако прыжки ― не постоянное поведение; у них есть начало и конец. Поэтому, чтобы реализовать прыжки, нужно постоянно контролировать, сколько времени прошло с момента начала прыжка. Нужен секундомер.

В листинге 8 показана реализация объекта JavaScript Stopwatch.

Листинг 8. Объект Stopwatch
 // Stopwatch..................................................................
//
// Секундомер можно включить и остановить, а также узнать, сколько времени прошло с 
//  момента включения. После остановки секундомера метод getElapsedTime() 
// возвращает время, прошедшее с момента включения до момента остановки.
Stopwatch = function ()  {
   this.startTime = 0;
   this.running = false;
   this.elapsed = undefined;
   this.paused = false;
   this.startPause = 0;
   this.totalPausedTime = 0;
};
// Значение времени можно получить во время работы секундомера или после его остановки.
Stopwatch.prototype = {
   start: function () {
      this.startTime = +new Date();
      this.running = true;
      this.totalPausedTime = 0;
      this.startPause = 0;
   },
   stop: function () {
      if (this.paused) {
         this.unpause();
      }
      this.elapsed = (+new Date()) - this.startTime -
                                     this.totalPausedTime;
      this.running = false;
   },
   pause: function () {
      this.startPause = +new Date(); 
      this.paused = true;
   },
   unpause: function () {
      if (!this.paused) {
         return;
      }
      this.totalPausedTime += (+new Date()) - this.startPause; 
      this.startPause = 0;
      this.paused = false;
   },
   getElapsedTime: function () {
      if (this.running) {
         return (+new Date()) - this.startTime - this.totalPausedTime;
      }
      else {
        return this.elapsed;
      }
   },
   isPaused: function() {
      return this.paused;
   },
   isRunning: function() {
      return this.running;
   },
   reset: function() {
     this.elapsed = 0;
     this.startTime = +new Date();
     this.running = false;
     this.totalPausedTime = 0;
     this.startPause = 0;
   }
};

Объект секундомера, показанный в листинге 8, можно запустить, остановить, приостановить, возобновить и сбросить. Также можно получить значение прошедшего времени и определить, работает ли секундомер или приостановлен.

В разделе Приостановка игры третьей статьи этого цикла мы говорили о том, как возобновить приостановленную игру точно с того же места, где она прервалась, учитывая время, на которое игра была приостановлена. Как и сама игра, приостановленный секундомер должен возобновлять работу точно с того же места, где он был остановлен, так что время приостановки должно фиксироваться.

Реализация секундомера, хотя она и проста, имеет большое значение, потому что позволяет реализовать поведение, длящееся ограниченное время — в данном случае, естественный прыжок.


Уточнение манипулятора прыжка

Теперь, когда у нас есть секундомер, его можно использовать для уточнения манипулятора прыжка. Во-первых, изменим метод equipRunner() из листинга 5, как показано в листинге 9.

Листинг 9. Измененный метод equipRunner()
SnailBait.prototype = {
   ...
   this.RUNNER_JUMP_HEIGHT = 120,    // пиксели
   this.RUNNER_JUMP_DURATION = 1000, // миллисекунды
   equipRunnerForJumping: function () {
      this.runner.JUMP_HEIGHT = this.RUNNER_JUMP_HEIGHT;
      this.runner.JUMP_DURATION = this.RUNNER_JUMP_DURATION;
      this.runner.jumping = false;
      this.runner.ascendStopwatch  = new Stopwatch(this.runner.JUMP_DURATION/2);
      this.runner.descendStopwatch = new Stopwatch(this.runner.JUMP_DURATION/2);
      this.runner.jump = function () {
         if (this.jumping) // 'this' - это бегун
            return;
         this.jumping = true;
         this.runAnimationRate = 0; // Бегун замирает во время прыжка
         this.verticalLaunchPosition = this.top;
         this.ascendStopwatch.start();
      };
   },
   equipRunner: function () {
      ...
      this.equipRunnerForJumping();
   },
   ...
};

Пересмотренная реализация метода equipRunner() вызывает новый метод: equipRunnerForJumping(). Как и предполагает его название, он оснащает бегуна всем необходимым для прыжков. Этот метод создает два секундомера: runner.ascendStopwatch для прыжка вверх и runner.descendStopwatch для прыжка вниз.

В начале прыжка метод jump() запускает секундомер прыжка вверх, как видно в листинге 9. Этот же метод устанавливает темп анимации бега — который определяет, насколько быстро бегун пройдет цикл анимации бега — пока не зависнет в воздухе. Метод run() фиксирует вертикальное положение бегуна, чтобы его можно было вернуть в это положение после завершения прыжка.

Все атрибуты бегуна, заданные в листинге 9, приведены в таблице 1.

Таблица 1. Атрибуты бегуна, связанные с прыжком
АтрибутОписание
JUMP_DURATIONКонстанта, соответствующая продолжительности прыжка в миллисекундах: 1000.
JUMP_HEIGHTКонстанта, соответствующая высоте прыжка в пикселях: 120. В верхней точке прыжка бегун находится на 20 пикселей выше следующего уровня.
ascendStopwatchСекундомер, отсчитывающий время движения вверх.
descendStopwatchСекундомер, отсчитывающий время движения вниз.
jumpApexВерхняя точка прыжка; манипулятор прыжка использует ее для определения того, как далеко должен продвинуться бегун за один кадр движения вниз.
jumpingФлаг, который на время прыжка принимает значение true.
verticalLaunchPositionПоложение бегуна (верхний левый угол спрайта бегуна) в момент начала прыжка. В конце полного прыжка бегун возвращается в это положение.

В листинге 10 приведен отредактированный манипулятор прыжка из листинга 7.

Листинг 10. Отредактированный манипулятор прыжка
var SnailBait =  function () {
   this.jumpBehavior = {
      ...

      execute: function(sprite, context, time, fps) {
         if ( ! sprite.jumping) {
            return;
         }

         if (this.isJumpOver(sprite)) {
            sprite.jumping = false;
            return;
         }

         if (this.isAscending(sprite)) {
            if ( ! this.isDoneAscending(sprite)) this.ascend(sprite);
            else                               this.finishAscent(sprite);
         }
         else if (this.isDescending(sprite)) {
            if ( ! this.isDoneDescending(sprite)) this.descend(sprite); 
            else                                this.finishDescent(sprite);
         }
      } 
   },
   ...

Манипулятор прыжка, приведённый в листинге 10, ― это высокоуровневая абстракция реализации, в которой детали прыжка оставлены другим методам, таким как ascend() и isDescending(). Теперь остается лишь заполнить детали с помощью секундомеров движения вверх и вниз для реализации следующих методов:

  • isJumpOver()
  • ascend()
  • isAscending()
  • isDoneAscending()
  • finishAscent()
  • descend()
  • isDescending()
  • isDoneDescending()
  • finishDescent()

Линейное движение

Приведенные выше методы создают линейное движение, что означает, что бегун поднимается и опускается с постоянной скоростью, как изображено на рисунке 4.

Рисунок 4. Монотонная линейная последовательность прыжка
Монотонная линейная последовательность прыжка бегуна

Линейное движение приводит к неестественному прыжку, потому что сила тяжести должна вызывать постоянное ускорение и замедление движения бегуна, когда он поднимается и опускается. В следующей статье мы изменим эти методы так, чтобы они приводили к нелинейному движению, как изображено на рисунке 1. Пока же будем придерживаться упрощенного варианта с линейным движением.

В листинге 11 показана реализация метода isJumpOver(), который одинаков независимо от того, линейно ли движение при прыжке. Прыжок окончен, если ни один секундомер не работает.

Листинг 11. Определение того, окончен ли прыжок
SnailBait.prototype = {
   this.jumpBehavior = {
      isJumpOver: function (sprite) {
         return !sprite.ascendStopwatch.isRunning() &&
                !sprite.descendStopwatch.isRunning();
      },
      ...
   },
   ...
};

Методы манипулятора прыжка, работающие при подъеме, показаны в листинге 12.

Листинг 12. Подъем
SnailBait.prototype = {
   ...

   this.jumpBehavior = {
      isAscending: function (sprite) {
         return sprite.ascendStopwatch.isRunning();
      },

      ascend: function (sprite) {
         var elapsed = sprite.ascendStopwatch.getElapsedTime(),
             deltaY  = elapsed / (sprite.JUMP_DURATION/2) * sprite.JUMP_HEIGHT;

         sprite.top = sprite.verticalLaunchPosition - deltaY; // Перемещение вверх
      },

      isDoneAscending: function (sprite) {
         return sprite.ascendStopwatch.getElapsedTime() > sprite.JUMP_DURATION/2;
      },

      finishAscent: function (sprite) {
         sprite.jumpApex = sprite.top;
         sprite.ascendStopwatch.stop();
         sprite.descendStopwatch.start();
      }
   },
   ...
};

Методы из листинга 12 сведены в таблицу 2.

Таблица 2. Методы подъема jumpBehavior
МетодОписание
isAscending()Возвращает значение true, если работает секундомер подъема бегуна.
ascend()Перемещает бегуна вверх с учетом времени, прошедшего с момента последнего кадра анимации, а также продолжительности и высоты прыжка.
isDoneAscending()Возвращает значение true, если время подъема бегуна превышает половину продолжительности прыжка.
finishAscent()

Завершает подъем, остановив секундомер подъема бегуна и запустив секундомер спуска.

jumpBehavior вызывает этот метод, когда бегун находится в наивысшей точке прыжка, так что finishAscent() сохраняет положение бегуна в его атрибуте jumpApex. Этот атрибут используется методом descend().

Напомним, что метод бегуна jump(), показанный в листинге 9, запускает секундомер подъема бегуна. Впоследствии этот работающий секундомер приводит к тому, что метод манипулятора прыжка isAscending() временно возвращает значение true. Пока бегун не закончит подъем — то есть до половины прыжка, — манипулятор прыжка бегуна повторяет вызов метода ascend(), как видно в листинге 10.

Подъем и спуск

Метод ascend() постепенно перемещает бегуна все выше. Он вычисляет количество пикселей подъема бегуна для каждого кадра анимации путем деления времени по секундомеру (в миллисекундах) на половину продолжительности прыжка (в миллисекундах) и умножения этого значения на высоту прыжка (в пикселях). Миллисекунды сокращаются, так что единицей измерения значения deltaY являются пиксели. Это значение соответствует количеству пикселей, на которое перемещается бегун в вертикальном направлении за текущий кадр анимации.

Когда бегун заканчивает подъем, метод finishAscent() манипулятора прыжка фиксирует положение спрайта в качестве вершины, останавливает секундомер подъема и запускает секундомер спуска.

Методы манипулятора прыжка, связанные со спуском, показаны в листинге 13.

Листинг 13. Спуск
SnailBait.prototype = {
   this.jumpBehavior = {
      isDescending: function (sprite) {
         return sprite.descendStopwatch.isRunning();
      },

      descend: function (sprite, verticalVelocity, fps) {
         var elapsed = sprite.descendStopwatch.getElapsedTime(),
             deltaY  = elapsed / (sprite.JUMP_DURATION/2) * sprite.JUMP_HEIGHT;

         sprite.top = sprite.jumpApex + deltaY; // Перемещение вниз
      },

      isDoneDescending: function (sprite) {
         return sprite.descendStopwatch.getElapsedTime() > sprite.JUMP_DURATION/2;
      },

      finishDescent: function (sprite) {
         sprite.top = sprite.verticalLaunchPosition;
         sprite.descendStopwatch.stop();
         sprite.jumping = false;
         sprite.runAnimationRate = snailBait.RUN_ANIMATION_RATE;
      }
   },
   ...
};

Методы из листинга 13 сведены в таблицу 3.

Таблица 3. Методы спуска jumpBehavior
isDescending()Возвращает значение true, если работает секундомер спуска бегуна.
descend()Перемещает бегуна вниз с учетом времени, прошедшего с момента последнего кадра анимации, а также продолжительности и высоты прыжка.
isDoneDescending()Возвращает значение true, если бегун упал ниже своего положения перед прыжком.
finishDescent()

Останавливает спуск и прыжок, остановив секундомер спуска бегуна и установив флаг jumping бегуна в состояние false.

После спуска бегун может оказаться не точно на той же высоте, как в начале прыжка, поэтому finishDescent() приводит бегуна в то же положение по вертикали, в каком он находился перед прыжком.

Наконец, finishDescent() приводит скорость анимации бегуна к ее нормальному значению, и бегун начинает бег.

Методы подъема из листинга 12 и методы спуска из листинга 13 во многом симметричны. Методы ascend() и descend() одинаково рассчитывают количество пикселей для перемещения бегуна в вертикальном направлении для текущего кадра. Только метод descend() прибавляет это значение к позиции вершины прыжка, а ascend() вычитает его из начальной позиции. (Напомним, что ось Y холста направлена сверху вниз).

По завершении спуска метод finishDescent() возвращает бегуна в то же вертикальное положение, в котором он находился перед прыжком, и вновь запускает анимацию бега.


В следующей статье

В следующей статье этой серии я покажу, как реализовать нелинейное движение для создания реалистичного прыжка, показанного на рисунке 1. Попутно я покажу, как деформировать само время, чтобы производить нелинейные эффекты для любых других производных времени, такие как изменение цвета. До скорой встречи.


Загрузка

ОписаниеИмяРазмер
Пример кодаj-html5-game6.zip1,2 MБ

Ресурсы

Научиться

Получить продукты и технологии

  • Replica Island: загрузите открытый исходный код этого популярного платформера для Android. Большинство графики Snail Bait взято из Replica Island (используется с разрешения).

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Технология Java, Web-архитектура
ArticleID=929906
ArticleTitle=Разработка 2D-игр на HTML5: Манипулирование временем, часть 1
publish-date=05162013