Что нужно знать при конвертации Flash-игры в HTML5?
Опубликовано: 2022-03-10С ростом использования HTML5 многие компании начинают переделывать свои самые популярные игры, чтобы избавиться от устаревшего Flash и привести свои продукты в соответствие с последними отраслевыми стандартами. Это изменение особенно заметно в индустрии азартных игр/казино и развлечений и происходит уже несколько лет, поэтому приличный выбор игр уже был преобразован.
К сожалению, при просмотре интернета довольно часто можно наткнуться на примеры, казалось бы, поспешных работ, в результате чего получается халявное качество конечного продукта. Вот почему разработчикам игр рекомендуется посвятить часть своего времени ознакомлению с предметом преобразования Flash в HTML5 и изучению ошибок, которых следует избегать, прежде чем приступить к работе.
Среди причин выбора JavaScript вместо Flash, помимо очевидных технических проблем, также тот факт, что изменение дизайна вашей игры с SWF на JavaScript может улучшить взаимодействие с пользователем, что, в свою очередь, придаст ей современный вид. Но как это сделать? Вам нужен специальный конвертер игр JavaScript, чтобы избавиться от этой устаревшей технологии? Что ж, преобразование Flash в HTML5 может быть проще простого — вот как об этом позаботиться.
Рекомендуемая литература : Принципы игрового дизайна HTML5 .
Как улучшить игровой опыт HTML5
Конвертация игры на другую платформу — отличная возможность улучшить ее, исправить недочеты и увеличить аудиторию. Ниже приведены несколько вещей, которые можно легко сделать и заслуживают внимания:
Поддержка мобильных устройств
Конвертация с Flash на JavaScript позволяет охватить более широкую аудиторию (пользователей мобильных устройств); поддержка сенсорного управления обычно также должна быть реализована в игре. К счастью, устройства Android и iOS теперь также поддерживают WebGL, поэтому обычно можно легко добиться скорости рендеринга 30 или 60 кадров в секунду. Во многих случаях 60 FPS не вызовет никаких проблем, которые со временем будут только улучшаться, поскольку мобильные устройства становятся все более и более производительными.- Повышение производительности
Когда дело доходит до сравнения ActionScript и JavaScript, последний быстрее первого. Кроме того, конвертация игры — хороший повод пересмотреть алгоритмы, используемые в игровом коде. При разработке игр на JavaScript вы можете оптимизировать их или полностью удалить неиспользуемый код, оставленный первоначальными разработчиками. - Исправление ошибок и внесение улучшений в игровой процесс
Привлечение новых разработчиков к изучению исходного кода игры может помочь исправить известные ошибки или обнаружить новые и очень редкие. Это сделает игру менее раздражающей для игроков, что заставит их проводить больше времени на вашем сайте и побудит попробовать другие ваши игры. - Добавление веб-аналитики
Помимо отслеживания трафика веб-аналитика также может использоваться для сбора информации о том, как игроки ведут себя в игре и где они застревают во время игры. - Добавление локализации
Это увеличит аудиторию и важно для детей из других стран, играющих в вашу игру. Или, может быть, ваша игра не на английском языке, и вы хотите поддерживать этот язык?
Почему пропуск HTML и CSS для игрового пользовательского интерфейса улучшит производительность игры
Когда дело доходит до разработки игр на JavaScript, может возникнуть соблазн использовать HTML и CSS для внутриигровых кнопок, виджетов и других элементов графического интерфейса. Мой совет: будьте осторожны здесь. Это нелогично, но на самом деле использование элементов DOM менее эффективно в сложных играх, и это приобретает большее значение на мобильных устройствах. Если вы хотите добиться постоянных 60 кадров в секунду на всех платформах, может потребоваться отказ от HTML и CSS.
Неинтерактивные элементы графического интерфейса, такие как шкалы здоровья, шкалы боеприпасов или счетчики очков, можно легко реализовать в Phaser с помощью обычных изображений (класс Phaser.Image
), используя свойство .crop
для обрезки и класс Phaser.Text
для простого текстовые метки.
Такие интерактивные элементы, как кнопки и флажки, можно реализовать с помощью встроенного класса Phaser.Button
. Другие, более сложные элементы могут состоять из различных простых типов, таких как группы, изображения, кнопки и текстовые метки.
Примечание. Каждый раз, когда вы создаете экземпляр объекта Phaser.Text или PIXI.Text, создается новая текстура для рендеринга текста. Эта дополнительная текстура нарушает группировку вершин, поэтому следите за тем, чтобы их не было слишком много .
Как убедиться, что пользовательские шрифты загружены
Если вы хотите отображать текст с помощью пользовательского векторного шрифта (например, TTF или OTF), вам необходимо убедиться, что шрифт уже загружен браузером, прежде чем отображать любой текст. Phaser v2 не предоставляет решения для этой цели, но можно использовать другую библиотеку: Web Font Loader.
Предполагая, что у вас есть файл шрифта и вы включили загрузчик веб-шрифтов на свою страницу, ниже приведен простой пример того, как загрузить шрифт:
Создайте простой файл CSS, который будет загружен загрузчиком веб-шрифтов (вам не нужно включать его в свой HTML):
@font-face { // This name you will use in JS font-family: 'Gunplay'; // URL to the font file, can be relative or absolute src: url('../fonts/gunplay.ttf') format('truetype'); font-weight: 400; }
Теперь определите глобальную переменную с именем WebFontConfig
. Обычно бывает достаточно чего-то простого:
var WebFontConfig = { 'classes': false, 'timeout': 0, 'active': function() { // The font has successfully loaded... }, 'custom': { 'families': ['Gunplay'], // URL to the previously mentioned CSS 'urls': ['styles/fonts.css'] } };
В конце не забудьте поместить свой код в «активный» обратный вызов, показанный выше. Вот и все!
Как упростить пользователям сохранение игры
Чтобы постоянно хранить локальные данные в ActionScript, вы должны использовать класс SharedObject. В JavaScript простой заменой является localStorage API, который позволяет сохранять строки для последующего поиска, выдерживая перезагрузку страницы.
Сохранить данные очень просто:
var progress = 15; localStorage.setItem('myGame.progress', progress);
Обратите внимание, что в приведенном выше примере переменная progress
, которая является числом, будет преобразована в строку.
Загрузка тоже проста, но помните, что извлеченные значения будут строками или null
, если они не существуют.
var progress = parseInt(localStorage.getItem('myGame.progress')) || 0;
Здесь мы гарантируем, что возвращаемое значение является числом. Если он не существует, то переменной progress
будет присвоено значение 0.
Вы также можете хранить и извлекать более сложные структуры, например, JSON:
var stats = {'goals': 13, 'wins': 7, 'losses': 3, 'draws': 1}; localStorage.setItem('myGame.stats', JSON.stringify(stats)); … var stats = JSON.parse(localStorage.getItem('myGame.stats')) || {};
В некоторых случаях объект localStorage будет недоступен. Например, при использовании протокола file://
или при загрузке страницы в приватном окне. Вы можете использовать оператор try и catch, чтобы гарантировать, что ваш код будет продолжать работать и использовать значения по умолчанию, как показано в примере ниже:
try { var progress = localStorage.getItem('myGame.progress'); } catch (exception) { // localStorage not available, use default values }
Еще одна вещь, которую следует помнить, это то, что сохраненные данные сохраняются для каждого домена, а не для каждого URL-адреса. Так что если есть риск, что много игр размещено на одном домене, то при сохранении лучше использовать префикс (пространство имён). В приведенном выше примере 'myGame.'
это такой префикс, и вы обычно хотите заменить его на название игры.
Примечание . Если ваша игра встроена в iframe, localStorage не будет сохраняться в iOS. В этом случае вам нужно будет вместо этого хранить данные в родительском iframe .
Как использовать замену стандартного фрагментного шейдера
Когда Phaser и PixiJS визуализируют ваши спрайты, они используют простой внутренний фрагментный шейдер. У него не так много функций, потому что он рассчитан на скорость. Однако вы можете заменить этот шейдер для своих целей. Например, вы можете использовать его для проверки перерисовки или поддержки дополнительных функций для рендеринга.
Ниже приведен пример того, как предоставить собственный фрагментный шейдер по умолчанию для Phaser v2:
function preload() { this.load.shader('filename.frag', 'shaders/filename.frag'); } function create() { var renderer = this.renderer; var batch = renderer.spriteBatch; batch.defaultShader = new PIXI.AbstractFilter(this.cache.getShader('filename.frag')); batch.setContext(renderer.gl); }
Примечание. Важно помнить, что шейдер по умолчанию используется для ВСЕХ спрайтов, а также при рендеринге в текстуру. Также имейте в виду, что использование сложных шейдеров для всех внутриигровых спрайтов сильно снизит производительность рендеринга .
Как изменить метод тонирования с помощью шейдера по умолчанию
Пользовательский шейдер по умолчанию можно использовать для замены метода тонирования по умолчанию в Phaser и PixiJS.
Тонирование в Phaser и PixiJS работает путем умножения пикселей текстуры на заданный цвет. Умножение всегда затемняет цвета, что, очевидно, не является проблемой; это просто отличается от тонировки Flash. Для одной из наших игр нам нужно было реализовать тонировку, аналогичную Flash, и мы решили, что можно использовать собственный шейдер по умолчанию. Ниже приведен пример такого фрагментного шейдера:
// Specific tint variant, similar to the Flash tinting that adds // to the color and does not multiply. A negative of a color // must be supplied for this shader to work properly, ie set // sprite.tint to 0 to turn whole sprite to white. precision lowp float; varying vec2 vTextureCoord; varying vec4 vColor; uniform sampler2D uSampler; void main(void) { vec4 f = texture2D(uSampler, vTextureCoord); float a = clamp(vColor.a, 0.00001, 1.0); gl_FragColor.rgb = f.rgb * vColor.a + clamp(1.0 - vColor.rgb/a, 0.0, 1.0) * vColor.a * fa; gl_FragColor.a = fa * vColor.a; }
Этот шейдер осветляет пиксели, добавляя базовый цвет к оттеночному. Чтобы это работало, вам нужно предоставить негатив нужного цвета. Следовательно, чтобы получить белый цвет, нужно установить:
sprite.tint = 0x000000; // This colors the sprite to white Sprite.tint = 0x00ffff; // This gives red
Результат в нашей игре выглядит так (обратите внимание, как танки мигают белым при попадании):
Как проверить перерисовку, чтобы обнаружить проблемы со скоростью заполнения
Замена шейдера по умолчанию также может быть использована для облегчения отладки. Ниже я объяснил, как с помощью такого шейдера можно обнаружить перерисовку.
Перерисовка происходит, когда многие или все пиксели на экране визуализируются несколько раз. Например, многие объекты занимают одно и то же место и отображаются один поверх другого. Сколько пикселей GPU может отображать в секунду, называется скоростью заполнения. Современные настольные графические процессоры имеют чрезмерную скорость заполнения для обычных 2D-задач, но мобильные намного медленнее.
Существует простой способ узнать, сколько раз записывается каждый пиксель на экране, заменив глобальный фрагментный шейдер по умолчанию в PixiJS и Phaser на этот:
void main(void) { gl_FragColor.rgb += 1.0 / 7.0; }
Этот шейдер осветляет обрабатываемые пиксели. Число 7,0 указывает, сколько операций записи необходимо, чтобы сделать пиксель белым; вы можете настроить этот номер по своему вкусу. Другими словами, более светлые пиксели на экране записывались несколько раз, а белые — не менее 7 раз.
Этот шейдер также помогает находить как «невидимые» объекты, которые по какой-то причине все еще рендерятся, так и спрайты, вокруг которых есть лишние прозрачные области, которые нужно зачистить (GPU по-прежнему должен обрабатывать прозрачные пиксели в ваших текстурах).
На картинке слева показано, как игрок видит игру, а на картинке справа показан эффект применения шейдера перерисовки к той же сцене.
Почему физические движки — ваши друзья
Физический движок — это промежуточное программное обеспечение, которое отвечает за моделирование физических тел (обычно динамики твердых тел) и их столкновений. Физические движки моделируют 2D или 3D пространства, но не оба одновременно. Типичный физический движок обеспечит:
- движение объекта путем задания скоростей, ускорений, суставов и двигателей;
- обнаружение коллизий между различными типами фигур;
- вычисление реакции на столкновение, т. е. того, как два объекта должны реагировать при столкновении.
В Merixstudio мы большие поклонники физического движка Box2D и использовали его несколько раз. Для этой цели хорошо подходит плагин Phaser. Box2D также используется в игровом движке Unity и GameMaker Studio 2.
Хотя физический движок ускорит вашу разработку, вам придется заплатить цену: снижение производительности во время выполнения. Обнаружение коллизий и вычисление ответов — это задача с интенсивным использованием ЦП. Вы можете быть ограничены несколькими десятками динамических объектов в сцене на мобильных телефонах или столкнуться с ухудшением производительности, а также снижением частоты кадров ниже 60 кадров в секунду.
Левая часть изображения представляет собой сцену из игры, а правая часть показывает ту же сцену с наложением отладки физики Phaser, отображаемым сверху.
Как экспортировать звуки из файла .fla
Если у вас есть звуковые эффекты Flash-игры внутри файла .fla , то их экспорт из графического интерфейса невозможен (по крайней мере, не в Adobe Animate CC 2017) из-за отсутствия пункта меню, предназначенного для этой цели. Но есть и другое решение — специальный скрипт, который делает именно это:
function normalizeFilename(name) { // Converts a camelCase name to snake_case name return name.replace(/([AZ])/g, '_$1').replace(/^_/, '').toLowerCase(); } function displayPath(path) { // Makes the file path more readable return unescape(path).replace('file:///', '').replace('|', ':'); } fl.outputPanel.clear(); if (fl.getDocumentDOM().library.getSelectedItems().length > 0) // Get only selected items var library = fl.getDocumentDOM().library.getSelectedItems(); else // Get all items var library = fl.getDocumentDOM().library.items; // Ask user for the export destination directory var root = fl.browseForFolderURL('Select a folder.'); var errors = 0; for (var i = 0; i < library.length; i++) { var item = library[i]; if (item.itemType !== 'sound') continue; var path = root + '/'; if (item.originalCompressionType === 'RAW') path += normalizeFilename(item.name.split('.')[0]) + '.wav'; else path += normalizeFilename(item.name); var success = item.exportToFile(path); if (!success) errors += 1; fl.trace(displayPath(path) + ': ' + (success ? 'OK' : 'Error')); } fl.trace(errors + ' error(s)');
Как использовать скрипт для экспорта звуковых файлов:
- Сохраните приведенный выше код как файл .jsfl на своем компьютере;
- Откройте файл .fla с помощью Adobe Animate;
- В верхнем меню выберите «Команды» → «Выполнить команду» и в открывшемся диалоге выберите скрипт;
- Теперь появляется еще один файл диалога для выбора каталога назначения экспорта.
Готово! Теперь у вас должны быть файлы WAV в указанном каталоге. Осталось преобразовать их, например, в MP3, OGG или AAC.
Как использовать MP3 в преобразованиях Flash в HTML5
Старый добрый формат MP3 вернулся, так как срок действия некоторых патентов истек, и теперь каждый браузер может декодировать и воспроизводить MP3. Это немного упрощает разработку, поскольку, наконец, нет необходимости подготавливать два отдельных аудиоформата. Раньше вам были нужны, например, файлы OGG и AAC, а теперь достаточно MP3.
Тем не менее, есть две важные вещи, которые вам нужно помнить о MP3:
- MP3 необходимо декодировать после загрузки, что может занять много времени, особенно на мобильных устройствах. Если вы видите паузу после загрузки всех ваших ресурсов, это, вероятно, означает, что MP3 декодируется;
- Воспроизведение зацикленных MP3 без перерывов немного проблематично. Решение — использовать mp3loop, о котором вы можете прочитать в статье, опубликованной Compu Phase.
Итак, почему вы должны конвертировать Flash в JavaScript?
Как видите, преобразование Flash в JavaScript не невозможно, если вы знаете, что делать. Обладая знаниями и навыками, вы можете перестать бороться с Flash и наслаждаться гладкими, увлекательными играми, созданными на JavaScript. Не пытайтесь починить Flash — избавьтесь от него, пока все не были вынуждены это делать!
Хотите узнать больше?
В этой статье я сосредоточился в основном на Phaser v2. Тем не менее, теперь доступна более новая версия Phaser, и я настоятельно рекомендую вам ознакомиться с ней, так как она представила множество новых интересных функций, таких как несколько камер, сцен, тайловых карт или физический движок Matter.js.
Если вы достаточно смелы и хотите создавать действительно замечательные вещи в браузерах, то WebGL — это то, что нужно изучать с нуля. Это более низкий уровень абстракции, чем различные фреймворки или инструменты для создания игр, но он позволяет добиться большей производительности и качества, даже если вы работаете над 2D-играми или демоверсиями. Среди множества веб-сайтов, которые могут оказаться полезными при изучении основ WebGL, можно выделить WebGL Fundamentals (использует интерактивные демонстрации). В дополнение к этому, чтобы узнать больше о степени внедрения функций WebGL, проверьте WebGL Stats.
Всегда помните, что знаний много не бывает, особенно когда речь идет о разработке игр!