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

.background    { background: rgb(52, 166, 251);

                 table-layout: fixed;

                 border-spacing: 0;              }

.background td { padding: 0;                     }

.lava          { background: rgb(255, 100, 100); }

.wall          { background: white;              }

Некоторые из настроек (table-layout, border-spacing и padding) используются для подавления нежелательного поведения по умолчанию. Не нужно, чтобы вид таблицы зависел от содержимого ячеек, и не нужны пробелы между ячейками или отступы внутри них.

Правило background задаёт цвет фона. CSS разрешает задавать цвета словами (white) и в формате rgb(R, G, B), где красная, зелёная и синяя компоненты разделены на три числа от 0 до 255. То есть, в записи rgb(52, 166, 251) красный компонент равен 52, зелёный 166 и синий 251. Поскольку синий компонент самый большой, результирующий цвет будет синеватым. Вы можете видеть, что самый большой компонент в правиле .lava – красный.

Каждый актёр рисуется созданием элемента DOM и заданием позиции и размера, основываясь на свойства актёра. Значения надо умножать на масштаб scale, чтобы переходить от единиц игры к пикселям.

DOMDisplay.prototype.drawActors = function() {

  var wrap = elt("div");

  this.level.actors.forEach(function(actor) {

    var rect = wrap.appendChild(elt("div",

                                    "actor " + actor.type));

    rect.style.width = actor.size.x * scale + "px";

    rect.style.height = actor.size.y * scale + "px";

    rect.style.left = actor.pos.x * scale + "px";

    rect.style.top = actor.pos.y * scale + "px";

  });

  return wrap;

};

Чтобы задать элементу больше одного класса, мы разделяем их имена пробелами. В коде CSS класс actor задаёт позицию absolute. Имя типа используется в дополнительном классе для задания цвета. Нам не надо заново определять класс lava, потому что мы повторно используем класс для лавы из решётки, который мы определили ранее.

.actor  { position: absolute;            }

.coin   { background: rgb(241, 229, 89); }

.player { background: rgb(64, 64, 64);   }

При обновлении экрана метод drawFrame удаляет старое изображение актёра, если оно было, и затем перерисовывает его на новой позиции. Напрашивается использование элементов DOM в качестве актёров, но для этого нам потребовалось бы передавать слишком много дополнительной информации между кодом дисплея и кодом симуляции. Надо было бы связать актёров с элементами DOM, и код рисования должен был бы удалять элементы при исчезновении актёров. Так как обычно в игре актёров совсем немного, их перерисовка отнимает немного ресурсов.

DOMDisplay.prototype.drawFrame = function() {

  if (this.actorLayer)

    this.wrap.removeChild(this.actorLayer);

  this.actorLayer = this.wrap.appendChild(this.drawActors());

  this.wrap.className = "game " + (this.level.status || "");

  this.scrollPlayerIntoView();

};

Добавив в обёртку wrapper текущий статус уровня в виде класса, мы можем стилизовать персонажа по-разному в зависимости от того, выиграна игра или проиграна. Мы добавим правило CSS, которое работает, только когда у игрока есть потомок с заданным классом.

.lost .player {

  background: rgb(160, 64, 64);

}

.won .player {

  box-shadow: -4px -7px 8px white, 4px -7px 8px white;

}

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

Нельзя предполагать, что уровни всегда вмещаются в окно просмотра. Поэтому нам нужен scrollPlayerIntoView – он нужен для гарантии того, что если уровень не влезает в окно, он будет прокручен, чтобы игрок всегда был близко к центру. Следующий CSS задаёт обёртке максимальный размер, и гарантирует, что всё вылезающее за него не видно. Также мы задаём элементу позицию relative, чтобы актёры внутри него располагались относительно его левого верхнего угла.

.game {

  overflow: hidden;

  max-width: 600px;

  max-height: 450px;

  position: relative;

}

В методе scrollPlayerIntoView мы находим положение игрока и обновляем позицию прокрутки обёртывающего элемента. Мы меняем позицию, работая со свойствами scrollLeft и scrollTop, когда игрок подходит близко к краю.

DOMDisplay.prototype.scrollPlayerIntoView = function() {

  var width = this.wrap.clientWidth;