Данный материал написан на основе записей докладов Nicolas Canasse:
Полный код приведенных здесь примеров можно найти здесь.
Что такое Heaps?
Heaps - это:
h2d
- 2D APIh3d
- 3D APIhxd
- общие классы для работы с событиями, звуками, ресурсами и т.д.hxsl
- классы, относящиеся к языку шейдеров hxslh2d.Tile
- область или текстура целиком,h2d.Sprite
- спрайт, базовый класс для отображаемых объектов. Сам спрайт непосредственно ничего не может отрисовать, но выступает в качестве контейнера для других объектов.h2d.Bitmap
- объект, отображающий на экране текстуру или заданную область из текстуры.h2d.Text
- соответственно объект, отображающий на экране текст.h3d.scene.Object
- аналогично спрайту он может выступать как контейнер для других объектов в 3d-сцене, но сам не хранит информацию о геометрии объектов на сцене;h3d.Camera
- виртуальная камера, через которую можно наблюдать за сценой;h3d.scene.Mesh
- это объект, у которого есть геометрия (например, куб или сфера), а также материал, описывающий как эта геометрия должна отрисовываться, какие шейдеры и текстуры должны для этого использоваться.visible
, scaleX
, scaleY
, name
и т.д.).hxd.Res
- подсистема управления ресурсами, основанная на макросах. Представляет собой виртуальную файловую систему с возможностью загрузки данных из разных источников (с диска, из сети, из данных, встроенных в само приложение).В настоящее время профилирование приложений на HashLink осуществляется путем профилирования C кода, который получается на выходе (например, с помощью MS Visual Studio).
Для работы с сетью Nicolas рекомендует использовать его же библиотеку hxbit, пример работы с которой доступен в примерах, распространяемых вместей с Heaps - Network.hx.
Установка и настройка среды для Heaps
В настоящий момент в документации говорится, что для работы с Heaps необходима версия Haxe не ниже 3.4.2. Однако, если вы хотите собирать проект под HashLink, то могут возникнуть несовместимости между версиями Haxe и HashLink. Поэтому сразу оговорюсь, что при написании этого материала я использовал следующие версии Haxe, HashLink и библиотек:
Скачав и распаковав HashLink, нужно добавить путь к его папке в системную переменную Path (иначе не получится запускать hl-файлы в VS Code).
Heaps можно установить либо с haxelib:
либо использовать git-версию библиотеки:
Вместе с Heaps распространяется набор примеров (см. папку samples). Крайне рекомендуется ознакомиться с ними для лучшего понимания библиотеки.
В данном материале попробуем использовать VS Code в качестве редактора.
Для работы с Haxe в VS Code следует использовать набор расширений “Haxe extensions pack”:
Запускаем VS Code, открываем в нем папку для проекта (Файл->Открыть папку).
Добавляем в проект 2 файла:
1. Game.hx - основной (и единственный в нашем случае) класс проекта, наследуемый от hxd.App
(базового класса для приложений на Heaps):
2. game.hxml - файл с настройками для компиляции проекта.
В файле game.hxml задаем следующие параметры:
Ctrl+Shift+B - запуск задачи - выбираем “haxe: game.hxml” - компиляция проекта.
После этого в списке файлов должен появиться файл game.hl.
Если этого не происходит, то это скорее всего означает что какой-то процесс завис и нужно перезапустить VS Code, проще всего это сделать вызвав команду “Reload window” - для этого вызываем панель команд (Ctrl+Shift+P) и вызываем эту команду.
Также если у вас перестает работать автокомплит, или переходы по коду, то скорее всего потребуется просто перезапустить Language Server - вспомогательный процесс, обращающийся к компилятору Haxe для выполнения данных задач. Перезапуск Language Server также осуществляется через панель команд (Ctrl+Shift+P) командой “Haxe: Restart Language Server”.
Попробуем теперь автоматизировать запуск нашего hl-файла, для этого создадим соответствующую задачу в проекте. Жмем F5 и в качестве среды выбираем HashLink:
В сгенерированном коде необходимо исправить имя hxml-файла (по-умолчанию оно задано как “build.hxml”) на наш “game.hxml”
Теперь нужно настроить задачу сборки по умолчанию (Задачи->Настроить задачу сборки по умолчанию…), выбрав game.hxml в качестве задачи:
В сгенерированной задаче необходимо дать ей имя “Build”, добавив поле “label” с соответствующим значением (так как задача с таким именем ищется и запускается перед запуском приложения - задана в launch.json полем “preLaunchTask”):
Настроив все задачи, можем наконец запустить приложение, нажав F5 - откроется пустое окно с черным фоном.
Кроме того, что в VS Code есть поддержка синтаксиса Haxe, работа с Heaps и HashLink в VS Code удобна еще и тем, что в пакет расширений входит отладчик HashLink.
Простой пример работы с отладчиком:
Давайте переопределим в приложении метод init()
, который вызывается при запуске приложения. Добавим в него какую-нибудь строчку кода и поставим напротив нее точку останова (breakpoint):
Пересоберем и запустим приложение (F5) и увидим, что выполнение программы остановится, а в панели отладки отображаются стек вызовов функций и текущие значения переменных:
Обзор основных возможностей Heaps
Давайте теперь добавим визуальные объекты на сцену. Добавим 2d изображение - объект Bitmap
, конструктор которого принимает 2 параметра: tile
и parent
.
tile
описывает некоторую область текстуры (из одной текстуры можно создать множество тайлов). В этом примере создадим тайл программно - из текстуры с заливкой красным цветом.
parent
- родительский объект на сцене. Добавим наш объект на 2d-сцену, которая автоматически создается движком - s2d
(является свойством основного класса - h2d.App
, кроме нее в приложении создается и 3d-сцена - s3d
):
В этом примере мы использовали метод tile.center()
, чтобы поместить якорную точку тайла в его центр. Такого же результата можно добиться, используя свойства dx
и dy
тайла.
У объектов на 2d-сцене есть множество свойств, аналогичных свойствам DisplayObject во Flash, например, цветовые трансформации могут настраиваться следующим образом:
Добавим динамики - пусть наш объект изменяет свое положение со временем. Для этого в h2d.App
есть метод update(dt:Float)
, который вызывается каждый кадр. Давайте переопределим его:
Теперь, если мы скомпилируем пример, то увидим красный вращающийся квадрат.
Рассмотрим теперь как в Heaps обрабатываются события пользовательского ввода.
В Heaps, в отличие от Flash, объекты на сцене сами не могут обрабатывать события мыши. Для этого предназначены объекты типа h2d.Interactive
, которые добавляются как дочерние объекты на сцену:
Теперь при наведении мыши на красный квадрат он будет становиться полупрозрачным, а при отведении - непрозрачным.
Для того, чтобы переопределить поведение Interactive
объекта (области, в которой срабатывают события мыши), вы можете переопределить у него метод onCheck()
.
Также в Heaps вместо прямоугольной области проверки можно задать ему область в форме эллипса, для этого у Interactive
объекта есть булево свойство isEllipse
.
Попробуем использовать изображение в качестве текстуры.
Добавим в проект папку “res” (по умолчанию папка с таким именем используется в качестве хранилища ресурсов, но при необходимости имя папки можно переопределить в проекте, задав значение флага компиляции resourcesPath
), и поместим в нее наше изображение:
Теперь просто поменяем строчку
на
Благодаря тому, что система управления ресурсами основана на макросах, имена файлов с ресурсами не нужна задавать строками - они становятся полями класса hxd.Res
, что позволяет избежать опечаток в названиях файлов (сообщения о таких ошибках отображаются на этапе компиляции приложения).
Кроме того, на основании расширений файлов система ресурсов самостоятельно определяет тип ресурса (например, jpg и png обрабатываются как изображения).
Таблица соответствия расширений файлов типам ресурсов определена в классе hxd.res.Config
и может быть изменена, т.к. является публичной переменной:
В крайнем случае ресурсы можно загружать и по имени файла, но при этом теряется типизация ресурсов и нужно самостоятельно приводить получившийся ресурс к его типу:
Также можно создать ресурс из массива байтов и привести его к требуемому типу:
Но для того, чтобы текстура действительно загрузилась, необходимо проинициализировать систему ресурсов. Делается это перед вызовом конструктора приложения:
В данном примере мы вызываем метод Res.initLocal()
, что означает, что ресурсы будут читаться из локальной дисковой системы как отдельные файлы. Кроме этого метода доступны методы:
initEmbed()
- все ресурсы будут встроены в файл приложения;
initPak()
- ресурсы будут упакованы в pak-файлы (архивы ресурсов, которые можно сгруппировать по какому-либо признаку, например, ресурсы определенного игрового уровня).
Кроме того, вы можете самостоятельно определить, как будут загружаться ресурсы (например, по сети).
В Heaps реализован механизм live reload для ресурсов, который активируется строчкой (перед инициализацией системы ресурсов):
А для обработки изменения ресурса нужно назначить callback, который будет вызываться при этом:
Здесь свойство entry
у hxd.Res.haxeLogo
- это объект виртуальной файловой системы, абстракции с которой работает система ресурсов. У entry
есть свой интерфейс, с помощью которго можно определить, является ли entry папкой или файлом, прочитать его байты и т.д.
Также следует сказать о том, что в Heaps неиспользуемые ресурсы автоматически удаляются из памяти, но есть и ручные методы их уничтожения, например, у тайлов есть метод dispose()
, который удаляет из памяти используемую им текстуру.
Для кнопок и других “тянущихся” элементов (с использованием 9 slice) в Heaps присутствует класс h2d.ScaleGrid
.
Для разметки элементов (layout) может использоваться компонент h2d.Flow
.
Отрисовка текста может осуществляться либо с помощью класса h2d.Text
, либо с помощью h2d.HtmlText
, поддерживающего разметку с помощью html тэгов.
Текстовое поле с возможностью ввода реализовано в класса h2d.TextInput
.
В данных классах в качестве шрифтов используются bitmap-шрифты, которые можно подготовить например в программе bmfont (в сети можно найти множество ее аналогов с большим набором возможностей). Также если в качестве целевой платформы выступают flash, js или lime (официально не поддерживается), то bitmap-шрифты можно генерировать программно из векторных с помощью класса hxd.res.FontBuilder
:
Простейший пример использования класса h2d.Text
:
h2d.HtmlText
наследуется от h2d.Text
и работа с ним ничем не отличается:
h2d.HtmlText
поддерживает тэги font
, img
(то есть в текст можно встраивать изображения), br
, у тэга font
поддерживаются атрибуты color
, opacity
, face
.
h2d.InputText
добавляет возможность обработки ввода текста с клавиатуры, а также возможность получить информацию о выделенном фрагменте текста (с помощью свойств cursorIndex
и selectionRange
):
Для батчинга тайлов в Heaps есть 2 класса:
h2d.TileGroup
- для статических групп, элементы которых не изменяют своего относительного положения;
h2d.SpriteBatch
- для динамических групп.
Пример использования h2d.TileGroup
:
Так как класс TileGroup
оптимизирован для отрисовки статических элементов, то трансформации всей группы практически ничего не стоят в плане производительности, то есть можно изменять положение, масштаб, угол поворота группы без снижения скорости работы приложения независимо от того, сколько в ней элементов.
Рассмотрим создание простых 2d-анимаций с помощью класса h2d.Anim
:
Здесь мы разбиваем текстуру на 10 горизонтальных кадров, задаем скорость воспроизведения анимации и добавляем анимированный спрайт на сцену.
Конечно же, у анимированного спрайта можно вручную изменять скорость анимации, задавать текущий кадр, поставить анимацию на паузу и т.д. (в общем делать все, что ожидается от анимации):
Для отрисовки геометрических примитивов в Heaps используются объекты типа h2d.Graphics
(также есть h3d.scene.Graphics
, работающий с 3D-сценой), которые похожи на flash.display.Graphics
во Flash, но являются при этом элементом сцены, а не свойством спрайтов:
При этом в отличие от OpenFL все примитивы отрисовываются полностью средствами GPU, не потребляя дополнительной памяти, а также сохраняются в буфере (то есть не пересчитываются каждый кадр).
Обработка нажатий кнопок клавиатуры отличается от Flash:
Для организации работы с разными задачами в Heaps добавлен класс hxd.WaitEvent
, который выполняет передаваемую в него функцию до тех пор, пока она не вернет true
. Также в WaitEvent
есть несколько предопределенных функций, например, для отложенного вызова метода:
Перейдем теперь к знакомству с 3d в Heaps, рассмотрим для этого пример Base3D, который распространяется вместе с библиотекой.
Чтобы сгенерировать проект для VS Code запустим файл gen.hxml (он создаст проекты для всех примеров) и откроем его в VS Code (samples/build/base3D/).
В созданном проекте уже будут настроены задачи сборки под flash, html5 (выбирается по умолчанию) и HashLink.
Для отладки html5-версии вам понадобится установить расширение “Debugger for Chrome”. Но здесь давайте продолжим собирать приложение под HashLink, для этого перейдите на панель отладки и выберите в качестве конфигурации “HashLink”.
В данном примере показано как осуществляется работа с 3D-примитивами, простыми материалами, освещением и камерой:
В примере создается один примитив - куб с размерами 1х1х1, который затем используется двумя разными 3D-объектами (Mesh
):
Можно провести аналогию между парами “примитив-меш” в Heaps и “BitmapData-Bitmap” во Flash: также как несколько объектов типа Bitmap может отображать одну и ту же BitmapData, имея при этом разные свойства (положение, масштаб, поворот, цветовую трансформацию и т.д.), несколько объектов типа Mesh могут отображать одну и ту же геометрию, заданную примитивом.
Затем осуществляем подготовку куба для того, чтобы его можно было текстурировать и корректно работать с освещением:
Создаем материал, который будет использовать текстуру:
Создаем 3D-объекты, отображаемые на сцене s3d
. При этом второму объекту мы задаем пустой материал (без текстуры, что означает, что объект будет закрашен заданным цветом):
Добавляем на 3D-сцену источник направленного света:
Также задаем интенсивность окружающего освещения на сцене, чтобы грани объектов, не освещенные добавленным источником света, все же были видны на экране:
Отключаем у материалов объектов расчеты теней:
А в методе update()
, вызываемом каждый кадр, мы обновляем положение камеры, которая облетает добавленные на сцену объекты, а также вращаем один из кубов вокруг произвольной оси:
Работа с событиями мыши в 3d аналогична - для этого создаются объекты h3d.scene.Interactive
, которым задаются фигуры для определения пересечений с координатами мыши (с помощью raycasting’а). Добавим в качестве примера цветному кубу реакцию на события мыши:
В примерах Heaps есть также еще один пример, иллюстрирующий обработку событий мыши в 3D-сценах - Interactive.
В этом же примере есть код, показывающий загрузку анимированных моделей. Как видно из примера ниже, модели загружаются с помощью специального объекта типа ModelCache
. Делается это для того, чтобы парсинг данных модели происходил только один раз (то же самое касается и загрузки анимаций из файлов модели):
В настоящее время Heaps поддерживает только модели в формате fbx, при этом стоит отметить, что при компиляции проекта fbx-файлы конвертируются во внутренний формат hmd, более оптимизированный по размеру и скорости загрузки, в результате fbx-файлы подменяются hmd-файлами. Такие преобразования происходят не каждый раз, а только в случае изменения или добавления файла модели.
Как уже говорилось, hxsl-шейдеры - это Haxe-код, шейдеры наследуются от базового класса hxsl.Shader
. Код шейдера проверяется на этапе компиляции приложения, а не на этапе компиляции самого шейдера во время исполнения программы. Кроме того, на этапе компиляции проверяются и обращения к параметрам шейдера из кода.
Пример простейшего шейдера:
А так этот шейдер можно назначить объекту на 2d-сцене:
Но самое интересное в этой системе шейдеров это то, что одному объекту можно добавить сразу несколько шейдеров, а в результате из них будет собран один шейдер, комбинирующий в себе их действие.
Например, определим еще один простой шейдер:
И теперь назначим оба шейдера объекту на сцене:
Таким образом, отпадает необходимость написания универсальных шейдеров, т.к. в данной системе можно написать несколько небольших шейдеров, каждый из которых реализует какой-либо один эффект, а затем скомпоновать их в один, “смешивая” их так, как будет угодно, и получить в итоге требуемый результат.
Еще один пример шейдера, но уже для 3D-объекта:
А так данный шейдер задается объекту на сцене:
Как видно, материалы объектов в Heaps могут отрисовываться в несколько проходов (в примере мы используем только основной), что дает возможность создавать эффекты любой сложности.
Инструменты для Heaps
HIDE - IDE для Heaps, написана на Haxe, собирается как приложение на Electron. По словам Nicolas Cannasse выбор Electron для создания редактора крайне удачен, так как позволяет относительно быстро создавать любые интерфейсы, пользуясь при этом html, css и существующими библиотеками на js.
К сожалению у меня не получилось собрать редактор из исходников (видимо сказывается тяга его создателя к использованию самых последних версий библиотек и компилятора :), так что о его работе расскажу на основе записей докладов.
В HIDE встроен просмотрщик всех ресурсов, с которыми может работать Heaps: текстовые файлы, звук, 3D-модели и сцены, системы частиц, изображения, скрипты.
Просмотрщик моделей и сцен используется в основном дизайнерами для проверки правильности их настроек, предварительного просмотра анимаций.
Nicolas начал работу по интеграции CastleDB в HIDE:
CastleDB - это модель данных для игр, которые хранятся в json и могут использовать в проектах на Haxe с помощью библиотеки castle, доступной на haxelib. При этом библиотека castle не просто парсит json-файлы, с помощью макросов она генерирует enum’ы и typedef’ы для структур данных, которые хранятся в базе и могут ссылаться друг на друга. Таким образом, сокращается число возможных ошибок, так как вы работаете уже с типизированными данными и не сможете обратиться к несуществующему полю у какой-либо структуры.
Использование json в качестве формата хранения данных позволяет также иметь версионность файлов (json-файлы хорошо ложатся в любую систему управления версиями), а также позволяет дизайнерам одновременно работать с данными игры.
Также в клиенте CasteDB имеется встроенный редактор тайловых карт, при этом на объекты на карте можно навешивать сущности, описываемые в базе данных.
Чтобы загрузить json-файл с данными из CastleDB достаточно добавить в проект класс Data.hx
со следующим кодом:
И загрузить данные (после инициализации системы ресурсов):
После чего можно работать с данными как с коллекциями объектов. Например, если в импортированной базе есть таблица item
с описаниями объектов, то итерация по ним осуществляется следующим образом:
Отдельно клиент CastleDB можно скачать с его официального сайта (там же можно найти довольно подробную документацию по нему), однако Nicolas планирует прекратить его поддержку и полностью сосредоточиться на HIDE.
Сейчас Nicolas занимается тем, что работает над 3D-редактором сцен в HIDE:
Редактор систем частиц в HIDE:
Анимация свойств частиц осуществляется на GPU. Системы частиц для 2D и 3D в Heaps реализованы отдельно.
Загрузка эффекта, созданного в редакторе осуществляется в 2 строчки:
Тот же пример, но с реализацией Live reload:
Просмотрщики каждого типа ресурса реализованы в виде отдельных классов (Image, Model, Sound, Script, Particle2D, Particle3D), исходный код которых можно посмотреть в пакете hide.view.
В этом материале мы рассмотрели только базовые элементы, но в Heaps заложено гораздо больше (например, для отрисовки больших 3d-сцен есть специальный класс h3d.scene.World
, развивающий сцену на отдельные элементы). Поэтому крайне рекомендую покопаться в примерах, распространяемых вместе с движком.
К сожалению Heaps страдает тем, что слабо документирован, и ваша помощь в исправлении этой проблемы совсем не помешает ;)
Кроме примеров могу порекомендовать следующие источники:
Надеюсь, что данный материал оказался хоть немного полезным :)