Всем привет! В результате недавних трудов у меня появились исходники движка для квестов компании Trickster Games. Выкладывать я их не буду, потому что не знаю насколько он лицензионно чистый. Я не юрист, в таких вопросах не разбираюсь. Поэтому расскажу своими словами — как устроен движок для квестов Trickster Games.
Поколений движков было два — первый использовался для игр Танита. Морское Приключение, Приключения Барона Мюнхгаузена на Луне и использовал lua в качестве скриптового языка.
Начиная с игры «Петрович и все, все, все» движок переходит на использование Python 2.xx вместо lua. Я пришёл в Trickster Games, когда начали делать Петрович, так что про предыдущие движки и игры практически ничего не знаю.
Итак, приступим!
Описание редактора игрового движка Trickster Games
Небольшое отступление: движок казался гораздо проще. Однако, он содержит в себе множество разных python классов, буду заново разбираться по ходу написания статьи.
Итак, у нас есть редактор (это первый уровень игры «Петрович и все все все»):
Это тот же самый движок, но запущенный в специальном режиме.
Слева у нас окно дерева объектов, снизу свойства объекта, а справа самый большой экран — экран предпросмотра уровня. Фон заполняется жёлтым цветом, чтобы на сцене сразу были видны пустые места. В режиме игры, фон заполняется чёрным цветом, так что это не бросается в глаза в обычном режиме.
Почти все объекты наследуются от ItemBase и рисуются относительно координат родителя. При перемещении родительского элемента на сцене, его дети перемещаются вместе с ним. В дереве объектов элементы можно перетаскивать с помощью drag-n-drop или добавлять новые правой кнопкой мыши. У каждого элемента есть замочек и глазик — блокировать перемещение/выделение при клике на сцену и показать/спрятать элемент при отрисовке сцены. Отрисовка элементов идёт по дереву от корневого узла вниз.
Уровень (Location)
Собственно уровень игры, рутовый элемент нашей сцены, содержит параметры width, height — ширина локации, высота локации. Вот её панель свойств:
Может содержать в качестве детей только слои.
Слой (Layer)
Слой локации, имеет параметр parallax — это отставание при прокрутке. С помощью него создаётся эффект глубины при скроллинге локации по горизонтали или вертикали. Вот её панель свойств:
Может содержать в качестве детей статические изображения, регионы, пути, анимированные объекты и точки.
Статическое изображение (LayerImage)
Фоновое изображение, ничего не делает, просто рисуется с соответствующим свойствами (параллакс) слоя. Имеет довольно скудный набор свойств:
Анимированный объект (AnimatedObject)
Можно сказать, что это наш главный элемент, базовый кирпичик нашего движка. У него есть позиция, набор состояний, к нему привязываются звуки, регионы, а также скрипт.
Он может наследоваться от особенных классов, например, Playable Character — это наш главный герой, который умеет ходить по локации, или Takeable Item — это вещь, которую можно забрать себе в инвентарь. Он может загружаться согласно определённым условиям из скрипта, если поставить флажок «Conditional object loading». Пример условной загрузки элемента «Item_parfume» (условия описываются в родительском элементе):
class FloorLayer: def should_load_Item_parfume(self): return not world.parfume_taken |
Также у него есть pivot — начало его координат и позиция на локации абсолютная и относительная. К нему могут привязываться дочерние AnimatedObject, Region и StateSequence. Содержит список состояний. Представляет собой конечный автомат, который меняет своё состояние в зависимости от внешних условий. Может загружать дочерние элементы типа AnimatedObject по некоторым условиям.
Состояние объекта (AnimatedSequence)
Анимация и состояние AnimatedObject. Анимация неотделима от состояния объекта. Пока AnimatedObject находится в определённом состоянии, проигрывается анимация этого состояния из одного или нескольких кадров. К любому кадру можно привязать автоматическое проигрывание звука.
У состояния есть свойства on_enter, on_exit, link, on_update — для привязки пользовательских функций. on_enter выполнятся при переходе в это состояние, on_exit — при выходе из него, link — выполняется каждый кадр, и если возвращает строку, то состояние меняется на указанное, on_update вызывается каждым кадр с неким dt (количество миллисекунд между кадрами).
Регион (Region)
Некий замкнутый набор точек. Определяет область для хождения главного героя на сцене (на скриншоте редактора это R_Floor) и области для клика мышкой. Имеет свойство
«.click» или «.click_with_
Путь (Path)
Путь состоящий из нескольких точек. Можно привязать элемент к этому пути, и объект будет плавно по нему перемещаться.
Точка (Point)
Точка на уровне. В основном используется, чтобы указывать, куда подходит главный герой для выполнения действий. Настраиваемых свойств в редакторе не имеет, только позицию.
Описание игрового движка Trickster Games
Движок написан на C++, для рендера используется DirectX 9.0, для связки Python 2.xx и C++ используется Boost.Python. Для сборки и компиляции исходников движка применяется (БУДЬТЕ ПРОКЛЯТЫ ЕГО СОЗДАТЕЛИ!) Boost Jam и Visual Studio 2007.
Базовый элемент в игровом движке — это GameObject, который имеет матрицу трансформации себя относительно родителя, список детей типа GameObject, и используется для создания дерева сцены. Простыми словами, для отрисовки дерева сцены вызывается GameObject::update(dt), который последовательно вызывает обновления всех детей, начиная от корня дерева вниз по иерархии. От него торчат уши в Python. Python-часть движка инициализируется следующим кодом:
std::string setup( "sys.path = [r'Data\\Scripts1.pak', r'Data\\Scripts2.pak', " "r'Data\\Scripts3.pak', r'Data\\Scripts3.pak\\CommonClasses', " "r'Data\\Scripts4.pak', r'Data\\Scripts4.pak\\World', '.', 'Lib/std']\n"); run_string(setup); |
Затем вызываем метод on_init
из файла Lib/engine.py
:
boost::python::call<void>(bp::object(py["Lib"].attr("engine").attr("on_init")).ptr(), long(application::Application::window())); py_on_frame = py["Lib"].attr("engine").attr("on_frame"); |
И после делаем on_frame
из того же файла каждый кадр:
boost::python::call<void>(py_on_frame.ptr(), dt / 1000.0f, just_redraw, cursor_position, mouse_button_state); |
Всё, никакой магии.
Форматы файлов
При подготовке релиза происходит преобразование .py файлов в .pyc, PNG в DDS, и всё это запаковывается в .zip файл.
Анимация
Анимация делалась в Adobe Flash и экспортировалась в набор PNG файлов, которые при запаковке релиза превращаются в DDS — это родной формат для DirectX SDK.
Звуки
Звуки — это .wav файлы
Видео
Для видео использовался формат OGV (Theora Vorbis).
Вроде всё, что мог, описал. Спрашивайте, если что-то осталось непонятным.