О чем этот пример
Игры часто используют нестандартные форматы данных — конфиги, сейвы или, как в нашем случае, трекерную музыку. Phaser позволяет легко загружать бинарные файлы и работать с ними на низком уровне. В этой статье мы разберем, как загрузить бинарный файл, извлечь из него текстовую информацию и отобразить её на экране. Этот подход полезен для создания инструментов для моддинга, просмотра метаданных пользовательских файлов или импорта данных из специфичных форматов.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.binary('mod', 'assets/audio/protracker/elysium.mod', Uint8Array);
}
create ()
{
const buffer = this.cache.binary.get('mod');
// var buffer = new Uint8Array(this.cache.binary.get('mod'));
// getString scans the binary file between the two values given,
// returning the characters it finds there as a string
const signature = this.getString(buffer, 1080, 1084);
const text = this.add.text(32, 32, `Signature: ${signature}`, { fill: '#ffffff' });
text.setShadow(2, 2, 'rgba(0,0,0,0.5)', 0);
const title = this.getString(buffer, 0, 20);
const text2 = this.add.text(32, 64, `Title: ${title}`, { fill: '#ffffff' });
text2.setShadow(2, 2, 'rgba(0,0,0,0.5)', 0);
// Get the sample data
const sampleText = [];
for (let i = 0; i < 31; i++)
{
const st = 20 + i * 30;
sampleText.push(this.getString(buffer, st, st + 22));
}
const text3 = this.add.text(400, 32, sampleText, { fill: '#ffffff' });
text3.setShadow(2, 2, 'rgba(0,0,0,0.5)', 0);
}
getString (buffer, start, end)
{
let output = '';
for (let i = start; i < end; i++)
{
output += String.fromCharCode(buffer[i]);
}
return output;
}
}
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
backgroundColor: '#0072bc',
scene: Example
};
const game = new Phaser.Game(config);
Загрузка бинарных данных
Phaser предоставляет удобный загрузчик для работы с бинарными данными. В методе preload мы настраиваем базовый URL и указываем загрузчику, какой файл нам нужен и в каком формате его ожидать.
Ключевой метод — this.load.binary(). Он принимает три аргумента:
- Ключ (key), по которому данные будут доступны в кеше.
- Путь к файлу.
- Тип данных, в который будет преобразован загруженный файл. В нашем случае — Uint8Array.
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.binary('mod', 'assets/audio/protracker/elysium.mod', Uint8Array);
После успешной загрузки файл будет доступен в бинарном кеше с ключом 'mod'.
Доступ к данным и извлечение строк
После загрузки сцены в методе create мы можем получить наши данные из кеша. Phaser хранит бинарные данные в this.cache.binary.
const buffer = this.cache.binary.get('mod');
Теперь в переменной buffer находится наш Uint8Array. Это массив беззнаковых 8-битных целых чисел (байтов). Чтобы прочитать из него текстовую информацию, нужна вспомогательная функция, которая преобразует коды символов в строку.
В примере используется метод getString. Он проходит по массиву от начального (start) до конечного (end) индекса, преобразуя каждый байт в символ с помощью String.fromCharCode() и собирая их в одну строку.
getString(buffer, start, end) {
let output = '';
for (let i = start; i < end; i++) {
output += String.fromCharCode(buffer[i]);
}
return output;
}
Парсинг структуры файла
Формат файла .mod (ProTracker) имеет четкую структуру. Зная смещения (оффсеты) в файле, мы можем извлечь нужную информацию.
1. **Сигнатура файла** находится по смещению 1080 байт и имеет длину 4 символа. Это специальная метка, идентифицирующая формат. 2. **Название трека** занимает первые 20 байт файла. 3. **Список сэмплов** начинается с 20-го байта. Описание каждого сэмпла занимает 30 байт, а его название хранится в первых 22 байтах этого блока.
Код ниже демонстрирует, как, зная эти смещения, извлечь все данные за несколько строк.
// Извлечение сигнатуры
const signature = this.getString(buffer, 1080, 1084);
// Извлечение названия трека
const title = this.getString(buffer, 0, 20);
// Извлечение списка названий сэмплов (31 шт.)
const sampleText = [];
for (let i = 0; i < 31; i++) {
const st = 20 + i * 30; // Начало блока i-го сэмпла
sampleText.push(this.getString(buffer, st, st + 22)); // Его название
}
Отображение результата
Извлеченные данные — это обычные строки JavaScript. Phaser с помощью объекта this.add.text() позволяет легко вывести их на экран.
Метод add.text() принимает координаты X, Y, сам текст и объект стиля. Для улучшения читаемости к тексту можно добавить тень с помощью метода .setShadow().
// Создание текстового объекта для сигнатуры
const text = this.add.text(32, 32, `Signature: ${signature}`, { fill: '#ffffff' });
text.setShadow(2, 2, 'rgba(0,0,0,0.5)', 0);
// Создание текстового объекта для названия трека
const text2 = this.add.text(32, 64, `Title: ${title}`, { fill: '#ffffff' });
text2.setShadow(2, 2, 'rgba(0,0,0,0.5)', 0);
// Создание текстового объекта для списка сэмплов.
// Массив sampleText будет автоматически преобразован в строку, разделенную запятыми.
const text3 = this.add.text(400, 32, sampleText, { fill: '#ffffff' });
text3.setShadow(2, 2, 'rgba(0,0,0,0.5)', 0);
Что попробовать дальше
Загрузка и парсинг бинарных данных в Phaser — мощный инструмент для работы с пользовательскими форматами файлов. Вы можете адаптировать этот подход для загрузки конфигураций, сохранений игр, кастомных карт или ресурсов. Для экспериментов попробуйте: загрузить другой бинарный файл (например, .wav-заголовок) и прочитать его параметры (частота дискретизации, битность); создать простой инструмент для просмотра HEX-дампа файлов прямо в игре; или реализовать загрузку уровня игры из бинарного файла собственного формата.
