Выбрать главу

  var motion = new Vector(this.speed.x * step, 0);

  var newPos = this.pos.plus(motion);

  var obstacle = level.obstacleAt(newPos, this.size);

  if (obstacle)

    level.playerTouched(obstacle);

  else

    this.pos = newPos;

};

Перемещение подсчитывается на основе состояния клавиш «направо» и «налево». Когда перемещение приводит к встрече с препятствием, вызывается метод уровня playerTouched, который обрабатывает гибель в лаве и сбор монеток. В ином случае объект обновляет свою позицию.

Движение по вертикали работает сходным образом, но симулирует прыжки и гравитацию.

var gravity = 30;

var jumpSpeed = 17;

Player.prototype.moveY = function(step, level, keys) {

  this.speed.y += step * gravity;

  var motion = new Vector(0, this.speed.y * step);

  var newPos = this.pos.plus(motion);

  var obstacle = level.obstacleAt(newPos, this.size);

  if (obstacle) {

    level.playerTouched(obstacle);

    if (keys.up && this.speed.y > 0)

      this.speed.y = -jumpSpeed;

    else

      this.speed.y = 0;

  } else {

    this.pos = newPos;

  }

};

В начале метода игрок ускоряется по вертикали, чтобы обеспечить гравитацию. Гравитация, скорость прыжка и все остальные константы в игре были подобраны методом проб и ошибок. Я проверял разные значения, пока меня не удовлетворил результат.

Затем мы снова проверяем препятствия. Если мы его встретили, возможны два варианта. Когда нажата клавиша «вверх», и мы двигаемся вниз (то есть, мы встретились с чем-то, что находится под нами), скорости присваивается довольно большое отрицательное значение. В результате игрок прыгает. В ином случае, мы просто во что-то врезались и скорость обнуляется.

Сам метод act следующий:

Player.prototype.act = function(step, level, keys) {

  this.moveX(step, level, keys);

  this.moveY(step, level, keys);

  var otherActor = level.actorAt(this);

  if (otherActor)

    level.playerTouched(otherActor.type, otherActor);

  // Losing animation

  if (level.status == "lost") {

    this.pos.y += step;

    this.size.y -= step;

  }

};

После движения метод проверяет других актёров, с которыми игрок сталкивается, и опять вызывает playerTouched, если таковой нашёлся. В этот раз он передаёт вторым аргументом объект actor, так как если другим актёром была монетка, метод playerTouched должен знать, какую именно монетку мы собрали.

В финале, когда игрок погибает (дотронувшись до лавы), мы делаем небольшую анимацию, из-за которой персонаж сжимается (или тонет), уменьшая высоту объекта player.

Вот метод, обрабатывающий столкновения между игроком и другими объектами:

Level.prototype.playerTouched = function(type, actor) {

  if (type == "lava" && this.status == null) {

    this.status = "lost";

    this.finishDelay = 1;

  } else if (type == "coin") {

    this.actors = this.actors.filter(function(other) {

      return other != actor;

    });

    if (!this.actors.some(function(actor) {

      return actor.type == "coin";

    })) {

      this.status = "won";

      this.finishDelay = 1;

    }

  }

};

Когда мы тронули лаву, статус игры устанавливается в “lost”. Когда собрана монетка, она удаляется из массива актёров, а если это была последняя – статус игры меняется на “won”. Всё это даёт нам уровень, пригодный для анимации. Не хватает только кода, её обрабатывающего.

Отслеживание клавиш

Для такой игры нам не нужны клавиши, эффект которых работает однократно после keypress. Нам нужен эффект, продолжающийся всё время, пока клавиша нажата (движущаяся фигурка)

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

Следующая функция, когда ей дают объект с кодами клавиш в виде имён свойств и названиями клавиш в виде значений, возвращает другой объект, который отслеживает текущее состояние кнопок. Он регистрирует обработчики событий для событий "keydown" и "keyup", и когда код клавиши события совпадает с отслеживаемым кодом, обновляет объект.

var arrowCodes = {37: "left", 38: "up", 39: "right"};

function trackKeys(codes) {

  var pressed = Object.create(null);

  function handler(event) {

    if (codes.hasOwnProperty(event.keyCode)) {

      var down = event.type == "keydown";

      pressed[codes[event.keyCode]] = down;

      event.preventDefault();