Выразительный JavaScript (Хавербеке) - страница 166

(>"keydown") или >false (>"keyup").

Запуск игры

Функция >requestAnimationFrame, которую мы видели в главе 13, предоставляет хороший способ анимировать игру. Но интерфейс её примитивен – его использование заставляет нас отслеживать момент времени, в который она была вызвана в прошлый раз, и вызывать >requestAnimationFrame каждый раз после каждого кадра.

Давайте определим вспомогательную функцию, оборачивающую эти скучные операции в удобный интерфейс, и позволяющую нам просто вызвать >runAnimation, задавая ей функцию, которая принимает разницу во времени и рисует один кадр. Когда функция >frame возвращает >false, анимация останавливается.

>function runAnimation(frameFunc) {

>  var lastTime = null;

>  function frame(time) {

>    var stop = false;

>    if (lastTime != null) {

>      var timeStep = Math.min(time - lastTime, 100) / 1000;

>      stop = frameFunc(timeStep) === false;

>    }

>    lastTime = time;

>    if (!stop)

>      requestAnimationFrame(frame);

>  }

>  requestAnimationFrame(frame);

>}

Я назначил максимальное время для кадра в 100 миллисекунд (1/10 секунды). Когда закладка или окно браузера спрятано, вызовы >requestAnimationFrame прекратятся, пока закладка или окно не станут снова активны. В этом случае, разница между >lastTime и текущим временем будет равна тому времени, в течение которого страница была спрятана. Продвигать игру на всё это время было бы глупо и затратно (вспомните разделение времени в методе >animate).

Эта функция также преобразовывает временные отрезки в секунды, которыми проще оперировать, чем миллисекундами.

Функция >runLevel принимает объект >Level, конструктор для >display, и, необязательным параметром – функцию. Она выводит уровень в >document.body и позволяет пользователю играть на нём. Когда уровень закончен (победа или поражение), >runLevel очищает экран, останавливает анимацию, а если задана функция >andThen, вызывает её со статусом уровня.

>var arrows = trackKeys(arrowCodes);


>function runLevel(level, Display, andThen) {

>  var display = new Display(document.body, level);

>  runAnimation(function(step) {

>    level.animate(step, arrows);

>    display.drawFrame(step);

>    if (level.isFinished()) {

>      display.clear();

>      if (andThen)

>        andThen(level.status);

>      return false;

>    }

>  });

>}

Игра – это последовательность уровней. Когда игрок погибает, уровень начинается заново. Когда уровень закончен, мы переходим на следующий. Это можно выразить следующей функцией, принимающей массив планов уровней (массив строк) и конструктор >display.

>function runGame(plans, Display) {

>  function startLevel(n) {