Pull to refresh

«Гоночки» на SVG

Reading time 4 min
Views 8K
image

Вводная часть


Аркадная js игра. Прототипом послужили так называемые «Гоночки в клетку»(в каком-то детском журнале видел). Смысл в том, что на тетрадном листе рисуется трасса и игроки ходят по клеткам. За один ход можно увеличить скорость на один или уменьшить. Если «врезаешься» в стену, то продолжаешь от этого места с единичной скоростью.

image

Код игры

Игра сделана с помощью raphael.js и playground.js, с отрисовкой графики в SVG. На моем средненьком ноутбуке (CPU 4 x 2.1 GHz) под chromium игра выдает 50 fps для маленьких трасс и 40fps для больших.

На одной трассе участвуют до 5 райсеров(или гоночек). Райсеры противников, повторяют действия предыдущих игроков, которые имеют самые лучшие результаты на этой трассе. Что бы трассер райсера записался в базу данных, достаточно прийти к финишу не последним. В меню можно выбрать цвет райсера и название, под которым, может записаться этот результат(для отображения результатов при финишировании, а для самого лучшего — в главном меню).

Физика реализована на двумерном массиве. Райсер ездит (летает?) по ходам. Каждый ход игрок может изменить скорость (на 3 единицы больше или на 10 единиц меньше) и так же, направление (влево или вправо). Исходя из этих данных и текущего положения, игра смотрит где закончится текущий ход и проверят массив на наличие стен или финиша. В зависимости от того, что в этой конечной точке, дальше следует реализация соответствующих алгоритмов. Создание трассеров тоже реализовано на этом массиве(в бд хранятся индексы массивов, а не реальные размеры смещений).

Ширины трасс варьируются от 500 единиц до 2000. Фактического ограничения на размеры нет, но чем больше, тем меньший fps выдает браузер.

Для графики используется SVG c 11 дочерними элементами. 5 (path) — для отрисовки райсеров, 4 (path) — для отрисовки трассы, 1 (circle) — для анимации столкновений со стенами и 1 (path) — для огней выхлопов.

image

База данных представлена одним json-файлом с трассерами. Ее обслуживают три php скрипта. Карты хранятся в переменной js. Можно было бы и для карт создать json, но они статичны и у них маленький размер.

В аркаде присутствуют звуки столкновений, звуки райсеров (как того, что под управлением игрока, так и чужих, при приближении к ним), а так же есть звук свиста стен. Мне этот эффект очень нравился в серии NFS, и я тут его постарался повторить. Все звуки скачены с сайта opengameart.org. На фоне играет Let the Games Begin(Section 31 — Tech), либо alien swamp(к сожеленью, автор неизвестен).

Отрисовка трассы


image

Сначала я читаю массив объектов.

{i: size_i, j: size_j, size: indent_in_cell}

и получаю цепочку отрезков, каждый элемент которого, соединяется с последующим, а конечный с первым. И таким образом я получаю замкнутый набор отрезков.

Из этого набора отрезков, с помощью функций тригонометрии, по indent_in_cell я получаю набор параллелепипедов (должны были быть прямоугольники, но не получились)

image

Далее я, с помощью всех тех же функций тригонометрии, определяю находится ли элемент массива (который я создал по параметру radius, и изначально он заполняется единицами) в комом-либо из прямоугольников. Если да, то этому элементу массива присваивается значение 0.

В самом конце я отрисовываю все прямоугольники одним элементом «path».

Для финиша я указываю смещение по ширине i. На этом смещении алгоритм ищет первый отрезок нулевых элементов и берет из этого отрезка конечную и начальную точку, а затем с помощью элемента «path» он рисуется.

Значения для «коробок» берутся из массива элементов типа

{i: size_i, j: size_j, width: width_in_cels}

i и j указываются для левого верхнего угла, а width это общий размер для квадрата. Затем на основе этих параметров изменяется главный массив. Элементам, которые попадают в «коробки», присваиваются значения 3. Далее по массиву я обрисовываю все коробки. Они рисуются, конечно же, все одним элементом «path».

Создание и чтение трассера.


Трассер представляет собой массив объектов типа:

{
  s: racer.speed,
  r: racer.root,
  i: racer.coord.i,
  j: racer.coord.j,
  wait: 0,
}

где s — скорость райсера, root — направление, i, j — координаты, в которые надо поставить райсер в текущем ходе, wait — это время ожидания. В последний элемент массива я добавляю информацию трассера:

    time: this.timer,
    human_time: racer.human_time,
    start_i: racer.start.i,
    start_j: racer.start.j,   
    road: settings.road,
    name: racer.name,
    color: racer.fill,

где, time — это общие количество фреймов, за которое этот райсер пришел к финишу, human_time — это время в формате hh:ss, start_i, start_j — координаты для стартовой позиции, road — индекс трассы, на которой получен этот трассер, name — название трассера, color — цвет райсера.

На каждом ходе игрока происходит запись объекта в массив с текущими параметрами. Координаты, для начала хода, направление и скорость. Если скорость равна нулю, то происходит инкремент параметра wait. Раз в фрейм к этому параметру прибавляется 1 и так до тех пор, пока скорость не станет
больше нуля.

При старте райсера создается объект хода и начинается инкрементирование wait (так как скорость сначала равна нулю) Далее, при каждом ходе, когда счетчик фреймов равняется нулю, происходит или запись объекта хода в массив, или инкреминтирование wait.

При чтении этого массива сначала смотрится значение wait. Если этот параметр больше нуля, то отключается рендер этого райсера и начинается инкриминтироваться собственный счетчик. Когда собственный счетчик будет равен значению wait, то дальше смотрится следующий объект хода. Объекты трассера, где wait не равен нулю, обрабатываются теми же функциями, что и движения райсера под управлением игрока. То есть выставляются из объекта скорость и направление и так же, так называемая цель хода (конечная точка для этого хода), и запускается стандартный набор функций для расчета параметров рендера и отрисовки хода райсера.

Заключение


В итоге у меня получилась довольно шустрая аркада с элементами сетевого взаимодействия игроков. Кроссбраузерность не проверял… точнее сказать firefox выдавал ужасный fps, при изменении масштаба, и я не знаю, с чем это связано. Если у кого не работает, то сhromium или, на крайний случай, chrom.

Сначала у меня задумывалось что-то неторопливое и головоломное, то есть надо было считать клетки и находить точки, из которых можно было бы не врезаться в стену на следующем ходу. И были клетки. Но после того, как я понял, что при 50 fps можно с большой скоростью и плавностью обрисовывать райсеры, то я все переделал и вместо логической игры по ходам, получилась аркада.

Игра примерно вышла на 3000 строк кода и 4 мегабайта.

UP 1: Кто-нить знает почему chromium под linux то работает нормально (50fps), то выдает только лишь 20. И firefox почему так не любит viewBox и в особенности масштабирование? Весь рендер работает на window.requestAnimationFrame()
Tags:
Hubs:
+11
Comments 27
Comments Comments 27

Articles