Поздравить всех хабравчан с неофициальным днем компьютерной графики! В этот день я хочу рассказать вам о том как я сделал онлайновскую версию старой игры.
Возможно многим знакома игра DX-Ball, Я играл в нее еще в дошкольном возрасте, а уже в школе коротал за ней уроки информатики. Поэтому мне было интересно сделать ее на HTML5.
Немного о работе
Я не буду приводить здоровые куски кода, и объяснять как что работает, я расскажу о там, как я разбирал эту игру, а о самой игре скажу лишь, то, что она сделана только на canvas'е. Я хотел сделать ее максимально похожей на оригинальную, и оставить как можно больше файлов без изменения, единственное что изменилось, так это список рекорд — он стал «бесконечным».
В начале задумывалось перерисовывать только те элементы котором изменяются, но из за большого количества багов, которые возникали в связи с этим, я остановился на варианте полной перерисовки кадра. И несмотря на то что игра может растягивается на весь экран, ее разрешение всегда остается 640x480px, впрочем как и в оригинале.
Разберем состав игры:
фалы *.pcx — это фоны игры такие как начальная заставка с описанием бонусов, и финальный «High Score».
*.mds — MIDI файлы музыки, сконвертировать их в *.mp3 мне помог savex.
В файле Default.bds хранится информация о расположении кирпичей на всех 50 уровнях
На одном уровне 202 кирпичей, которые записываются побайтно в этот файл.
От этого и получается 202×50 = 20 КБ
*.sbk — файлы растровых шрифтов, и остальной графики (кирпичи, ракетки и др.)
Информации о том как устроен этот файл я негде не нашел, поэтому пришлось разбираться самому, и вот что я выяснил:
Файлы этого типа содержат в себе что то вроде этого:
Структура этого файла немного сложнее чем Default.bds, в нем по очереди описываются символы,
Первые 4 байта файла содержат в себе информацию о том сколько символов (изображений) содержит в себе файл, но так как это значение не превышает 0xFF я обращаю внимание только на первый (нулевой) байт
Как показано на скриншоте файл Sysfont.sbk содержит 94 символа, Ширина и высота символа указанны в 4'ом и 8'ом байтах. 12'ый байт — это номер символа из таблицы ASCII, 0x41 = A, если он равен 0x00, то это не символ, а какая то картинка. Далее идут еще несколько байт, в нашем случае 130 (13×10), начиная с 17'ого байта — это bitmap data символа (на рисунке обозначено зеленым). Затем все это повторяется еще 93 раза (начиная с ширины).
Изображения (символы) отрисовываются снизу вверх, слева направо 1 байт = 1 пиксель, значение байта — это номер цвета.
Как я понял, в игре используется 2 цветовых режима, первый используется в самой игре, а второй только в заставке.
вот 2 схемы, каждый цвет имеет 2 номера, верхний — это номер цвета в Dec, а нижний в Hex:
Вот пример первых 3-х символов файла Sysfont.sbk с использованием первой схемы:
Ах да, ноль в обоих таблицах это прозрачный цвет, а цвета в первой схеме начиная с 224 по 231 — динамические, то есть смещаются на один цвет каждый кадр, что бы образовать вот такую анимацию
Но тут возникает проблема, некоторые символы, такие как qypgj должны находится ниже остальных а апострофы и другие знаки должны быть выше, но так, как все символы имеют разный размер, они выравниваются по низу.
Для решения этой проблемы существует специальные 4 байта, идущие после обозначения символа, но я буду использовать только один (на первом скриншоте он подчеркнут синем), так как смещение небольшое.
У символов gj этот байт равен 0xFD = 253. а у ^' этот байт равен 0x08 = 8. Это как раз и есть смещение символа относительно остальным, но почему же вместо отрицательного смещения такие большие числа? Дело в том, что таков вид записи отрицательных чисел в одном байте 128 отрицательных и 128 положительных чисел. Если наш байт меньше 128, то мы его не трогаем, а если больше, мы просто вычитаем из него 256.
И получаем отличный результат:
Перед запуском игры все изображения (символы) отрисовываются в том же канвасе, что и игра, я сохраняю их как картинки,
то есть: char[...].img.src = canvas.toDataURL("image/png");
Так как putImageData() гараздо медленнее чем drawImage() и это не единственный минус.
putImageData, не накладывает изображение сверху, а заменяет его полностью.
В процессе разбора игры я обнаружил два секретных бонуса, которые не используются в игре :-)
Вся анимация в игре оптимизирована с помощью requestAnimationFrame, но разные устройства выдают разные показатели FPS. Для того чтобы мяч летал с одинаковой скоростью не в зависимости от fps я умножил скорость мяча на коэффициент delta который рассчитывался по следующей формуле: delta = 1000/fps/60;
, мой ноутбук успевал отрисовывать около 55 кадров в секунду, но периодически зависал, и проглатывал более 30 кадров за раз, из за этого зависания коэффициент delta рассчитывался не верно и мяч приобретал не контролируемую скорость, чтобы от от этого избавится я решил усреднять fps за 4 секунды, поэтому он так редко обновляется.
В предыдущей игре Doodle Jump я дал пользователям возможность вставить ее iframe на любой сайт, в результате чего свыше 7000 пользователей ежедневно играли в эту игру на сторонних сайтах совершенно разной тематики, от личных блогов до звукозаписывающих студий.
Поэтому и в DX-ball я не отнимаю такой возможности, более того игра гибко настраивается для вставки на сайт.
Вроде все, если на ваш взгляд я что нибудь не до рассказал, задавайте вопросы, я обязательно отвечу.
Ссылка на игру: DX-Ball.ru
P.S. Игра еще сыровата, в ней возможны баги и зависания, так что буду благодарен, если вы напишите о недочетах в комментариях.
Автор: BorodinKO