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

  }

};

Метод вычисляет занимаемые телом ячейки решётки, применяя Math.floor и Math.ceil на координатах тела. Помните, что размеры ячеек – 1×1 единиц. Округляя границы тела вверх и вниз, мы получаем промежуток из ячеек фона, которых касается тело.

Поиск столкновений на решётке

Если тело высовывается из уровня, мы всегда возвращаем “wall” для двух сторон и верха и “lava” для низа. Это обеспечит гибель игрока при выходе за пределы уровня. Когда тело внутри решётки, мы в цикле проходим блок квадратов решётки, найденный округлением координат, и возвращаем содержимое первого непустого квадратика.

Столкновения игрока с другими актёрами (монеты, движущаяся лава) обрабатываются после сдвига игрока. Когда движение приводит его к другому актёру, срабатывает соответствующий эффект (сбор монет или гибель).

Этот метод сканирует массив актёров, в поисках того, который накладывается на заданный аргумент:

Level.prototype.actorAt = function(actor) {

  for (var i = 0; i < this.actors.length; i++) {

    var other = this.actors[i];

    if (other != actor &&

        actor.pos.x + actor.size.x > other.pos.x &&

        actor.pos.x < other.pos.x + other.size.x &&

        actor.pos.y + actor.size.y > other.pos.y &&

        actor.pos.y < other.pos.y + other.size.y)

      return other;

  }

};

Актёры и действия

Метод animate типа Level даёт возможность всем актёрам уровня сдвинуться. Аргумент step задаёт временной промежуток. Объект keys содержит информацию про стрелки клавиатуры, нажатые игроком.

var maxStep = 0.05;

Level.prototype.animate = function(step, keys) {

  if (this.status != null)

    this.finishDelay -= step;

  while (step > 0) {

    var thisStep = Math.min(step, maxStep);

    this.actors.forEach(function(actor) {

      actor.act(thisStep, this, keys);

    }, this);

    step -= thisStep;

  }

};

Когда у свойства уровня status есть значение, отличное от null (а это бывает, когда игрок выиграл или проиграл), мы уменьшить до нуля счётчик finishDelay, считающий время между моментом, когда произошёл выигрыш или проигрыш и моментом, когда надо заканчивать показ уровня.

Цикл while делит временной интервал на удобные мелкие куски. Он следит, чтобы промежутки были не больше maxStep. К примеру, шаг в 0,12 секунды будет нарезан на два шага по 0,05 и остаток в 0,02

У объектов актёров есть метод act, который принимает временной шаг, объект level и объект keys. Вот он для типа Lava, который игнорирует объект key:

Lava.prototype.act = function(step, level) {

  var newPos = this.pos.plus(this.speed.times(step));

  if (!level.obstacleAt(newPos, this.size))

    this.pos = newPos;

  else if (this.repeatPos)

    this.pos = this.repeatPos;

  else

    this.speed = this.speed.times(-1);

};

Он считает новую позицию, добавляя результат умножения временного промежутка и текущей скорости к старой позиции. Если новую позицию не занимает препятствие, происходит перемещение. Если препятствие существует, поведение зависит от типа блока лавы. У капающей лавы есть свойство repeatPos, и она при встрече с препятствием отражается в обратную сторону. Прыгающая лава просто инвертирует скорость (умножает на -1), чтобы продолжить движение в обратном направлении.

Монеты используют метод act, чтобы дрожать. Столкновения они игнорируют, поскольку они просто подрагивают внутри своего квадрата, а столкновения с игроком будут обрабатываться методом act игрока.

var wobbleSpeed = 8, wobbleDist = 0.07;

Coin.prototype.act = function(step) {

  this.wobble += step * wobbleSpeed;

  var wobblePos = Math.sin(this.wobble) * wobbleDist;

  this.pos = this.basePos.plus(new Vector(0, wobblePos));

};

Свойство wobble обновляется, чтобы следить за временем, и потом используется как аргумент Math.sin для создания волны, которая используется для подсчёта новой позиции.

Остаётся игрок. Движение игрока обрабатывается по разным осям отдельно, потому что встреча с полом не должна мешать горизонтальному перемещению, а встреча со стеной – падению или прыжку. Этот метод работает с горизонтальным перемещением.

var playerXSpeed = 7;

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

  this.speed.x = 0;

  if (keys.left) this.speed.x -= playerXSpeed;

  if (keys.right) this.speed.x += playerXSpeed;