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()
Поделиться с друзьями
-->

Комментарии (26)


  1. ilyaplot
    22.06.2017 15:24

    Ставлю под сомнение выбор цвета объекта. Синяя «машинка», а не гонщик. Хотя, тут неоднозначно.


  1. dimarikpro
    22.06.2017 16:13
    +1

    почему поворот только на 45 градусов, да еще и с таким лагом… — простите это п… ц
    20-30 лет назад под z80 было играбельнее (конечно без масштабирования и прочих плюшек, но на 3,5 мгц и 48 кб)
    на асме писали управление подобным спрайтом менее чем в 100 строк с вращением в 12 сторон и подобным управлением( "o", "p" space)


    В общем надо доработать!


    1. Arteben
      22.06.2017 17:40

      В оффлайне у меня было 50 fps chromium(linux) выдавал… Вообще браузер странно себя ведет. То работает нормально 50fps, то впадает в режим зомби и работает только на 20. С чем бы это может быть связано?


  1. vlreshet
    22.06.2017 16:33
    +1

    Всё сломалось
    image


    1. ilyaplot
      22.06.2017 17:03

      И мне показывает. А вообще не понятно, какое право хостер имеет показывать пользователю такие страницы? Может каждый посетитель стоит 1000 рублей? 500 бы показывали и все, а владельцу уведомление…


    1. Arteben
      22.06.2017 17:41

      Есть. Там же трассеры игроков сохраняются через php скрипты.


      1. vlreshet
        22.06.2017 17:47

        Тогда с этим надо что-то делать. Переносить на клиент, например


        1. Arteben
          22.06.2017 18:07

          Или хостинг нормальный заказать. Только денег нет на него. А без сервера там будет совсем скучно. Кстати, какой фпс был у игры?


          1. TheShock
            22.06.2017 22:08

            Выложите статику на GitHub pages, а со своего сервера берите только статистику, которую кешируйте время от времени. Не обязательно, если статистика будет самой актуальной. Пусть обновляется с задержкой в пару минут, это не страшно.


          1. elkrieg
            23.06.2017 16:39

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


    1. AlexMal
      22.06.2017 23:12
      +2

      Для всех, кто хочет попробовать, выложил на свой сервер). Только осторожно, он еще толком не отстроен.
      races.alexipro.ru


  1. Ogoun
    22.06.2017 16:34

    Судя по игре сдвиг рассчитывается неправильно, движение вдоль осей медленнее чем по диагонали.
    Т.е. скорее всего делается как-то так:
    x = x + speed * xoffset
    y = y + speed * yoffset

    Где значения xoffset и yoffset[-1;0;1].

    Если сделать матрицу сдвигов такой (направление, и в скобках сдвиги по x и y):

    Вверх (0; -1)
    Вверх-Вправо (sqrt(0.5); -sqrt(0.5))
    Вправо (1; 0)
    Вправо-Вниз (sqrt(0.5); sqrt(0.5))
    Вниз (0; 1)
    Влево-Вниз (-sqrt(0.5); sqrt(0.5))
    Влево (-1; 0)
    Влево-Вверх (-sqrt(0.5); -sqrt(0.5))

    и координаты хранить во float, то получим одинаковое значение длины сдвига в любом направлении.


    1. ruspartisan
      22.06.2017 16:42

      Это же изначально игра для бумаги, там никаких корней никто бы вычислять не стал, поэтому и такая условность.


    1. Arteben
      22.06.2017 18:13

      Там же действительно все по «клеткам», то есть по массиву. Диагональ будет всегда длиннее, чем по оси. Можно было сделать скорость движения одинаковой, но в прототипе «игра в клеточку» все было именно так)


  1. maxfromua
    22.06.2017 18:17

    На хостинге хабраэффект.


    1. Arteben
      22.06.2017 18:18

      Поднимай на своем сервере)


      1. AlexMal
        22.06.2017 23:18

        Поднял на своем). races.alexipro.ru.
        Для всех, кто хочет. Надеюсь выдержит нагрузки.


        1. ivan386
          23.06.2017 11:16

          Игра не для планшета? Сколько не тыкал кнопку "играть" безрезультатно.


  1. vintage
    22.06.2017 21:27
    +2

    Ну запилите хоть видос. Интересно же, на что там ушло 4 мегабайта :-)


  1. TheShock
    22.06.2017 22:09

    UP 1: Кто-нить знает почему chromium под linux то работает нормально (50fps), то выдает только лишь 20

    Насколько я помню, SVG плохо ускоряется видеокартой, особенно под Линукс. И это вообще не самая лучшая технология для игр в браузере. Возьмите какой-либо WebGL фреймворк, например Pixi и тогда точно будет стабильные 60 fps почти везде.


    1. Arteben
      23.06.2017 17:11

      Да я понял почему. Это из-за экономии ресурсов процессора. В одном случае это ускорение подключается и все нормально, а в другом — нет и только лишь 20 fps. То ли в хроме ошибка, то ли в самом линуксе. Ну ладно, когда питание от батареи, но когда от сети, верните ускорение. Так же нет. Только после включения\выключения и то, если повезет.
      Ну я на самом деле просто попробовал. Ну так, не однозначно. С одной стороны вроде нормально, но с другой… Если не использовать viewBox, для каких-нибудь логических, svg лучше подойдет. Он же так нормально работает, если не перемещать эти массивы прямоугольников по экрану.


  1. GoldKeeper
    23.06.2017 03:02

    а вы в курсе что у этого хостера запрещено для бесплатных:
    4.5.7. Использовать любой вид онлайн игр (разрешено при использовании услуг платного хостинга)?
    4.7.1. Использовать более 10% системных ресурсов на протяжении 60 секунд. Это включает в себя PHP, HTTP, крон задания и т.п.


  1. bro-dev
    23.06.2017 03:16
    +2

    На бумаге правила совсем другие которые тут не реализованы, а именно скорость поворота была такая что на скорости например 6 максимум кривизна с которой можно поворачивать будет тоже 6 или больше, это значит что на большой скорости не вписаться в крутые повороты на узких дорогах.


    1. TheShock
      23.06.2017 03:33

      Нарисуйте, как вы это представляете? Ну вот я захожу в очень широкий поворот со скоростью 6. Где и почему ставится каждая следующая точка?


      1. bro-dev
        23.06.2017 05:54
        +1

        В очередной ход, прокладываем из текущей точки, вектором движения предыдущего хода, это будет наша инерция, от конченой точки мы выбираем ближайшую одну из 9, это наше руление торможение и ускорение.
        http://joxi.ru/5mdvPzoiveZvyA
        На рисунке видно что при скорости 3 радиус поворота 9, в этом и фишка игры всего за 6 ходов можно разогнаться до 6 скорости, но на радиусе 18 таких поворотов просто не бывает на тетрадном листе бумаги так как карта маленькая ширина дороги обычно 4-8 клеток. Тише едешь дальше будешь. Само собой зная карту можно на своём ходе до финиша продумать оптимальный маршрут, поэтому если есть такие хитрецы делают второй лист бумаги с вырезом 6 на 6 клеток посередке и можно смотреть на область карты тока вокруг себя, такой туман войны.


        1. Arteben
          23.06.2017 16:51

          ооо таких подробностей в том журнале не было. Я как запомнил, так и сделал. А как называется эта игра?