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();