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

У игры фиксированный фон в виде решётки, где движущиеся элементы накладываются на фон. Каждая ячейка решётки либо пустая, либо является стеной, либо лавой. Движущиеся элементы – игрок, монеты и некоторые кусочки лавы. В отличие от симуляции жизни из главы 7, позиции этих элементов не привязаны к решётке. Их координаты могут быть дробными, обеспечивая плавное движение.

Технология

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

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

Мы представляем фон как таблицу, поскольку это – неизменная решётка из квадратов. Свободно двигающиеся элементы можно накладывать сверху, используя абсолютное позиционирование.

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

В следующей главе мы изучим другую технологию браузера, тег <canvas>, который предоставляет более традиционный способ для рисования, и работает с формами и пикселями вместо элементов DOM.

Уровни

В главе 7 мы использовали массивы строк для описания двумерной решётки. Мы можем сделать то же и здесь. Это позволит нам разрабатывать уровни без того, чтобы сначала писать редактор уровней.

Простой уровень может выглядеть так:

var simpleLevelPlan = [

  "                      ",

  "                      ",

  "  x              = x  ",

  "  x         o o    x  ",

  "  x @      xxxxx   x  ",

  "  xxxxx            x  ",

  "      x!!!!!!!!!!!!x  ",

  "      xxxxxxxxxxxxxx  ",

  "                      "

];

Фиксированная решётка и движущиеся элементы включены. Символы x обозначают стены, пробелы – пустое место, а восклицательные знаки – фиксированная лава.

@ отмечает место, где игрок начинает. o – монетки, знак равенства = означает блок движущейся лавы, который двигается по горизонтали туда и сюда. Заметьте, что решётка на этих позициях будет содержать пустое пространство, и для отслеживания позиции этих подвижных элементов используется ещё одна структура данных.

Мы будем поддерживать ещё два вида лавы: вертикальная черта | — для кусочков, двигающихся по вертикали, и v для капающей лавы. Она будет двигаться вниз, но не отскакивать обратно, а просто перепрыгивать на начальную позицию по достижению пола.

Игра состоит из нескольких уровней, которые надо закончить. Уровень закончен, когда собраны все монетки. Если игрок касается лавы, текущий уровень возвращается к исходному состоянию, и игрок начинает заново.

Чтение уровня

Следующий конструктор создаёт объект уровня. Аргументом должен быть массив строк, задающих уровень.

function Level(plan) {

  this.width = plan[0].length;

  this.height = plan.length;

  this.grid = [];

  this.actors = [];

  for (var y = 0; y < this.height; y++) {

    var line = plan[y], gridLine = [];

    for (var x = 0; x < this.width; x++) {

      var ch = line[x], fieldType = null;

      var Actor = actorChars[ch];

      if (Actor)

        this.actors.push(new Actor(new Vector(x, y), ch));

      else if (ch == "x")

        fieldType = "wall";

      else if (ch == "!")

        fieldType = "lava";

      gridLine.push(fieldType);

    }

    this.grid.push(gridLine);

  }

  this.player = this.actors.filter(function(actor) {

    return actor.type == "player";

  })[0];

  this.status = this.finishDelay = null;

}

Для краткости код не проверяет входящие данные. Он предполагает, что план уровня допустимый, что там есть стартовая позиция игрока и другие необходимые вещи.

Уровень сохраняет свои ширину и высоту и ещё два массива – один для решётки, и один для движущихся частей. Решётку представляет массив массивов, где каждый вложенный массив представляет горизонтальную линию, а каждый квадрат содержит либо null для пустых квадратов, либо строку, отражающую тип квадрата – “wall” или “lava”.