Архитектура движка
There are different approaches to setting up game architecture, ranging from all-in-one solutions, where game logic is fused with an engine, to a heap of separate modules, each of which is responsible for one part of functionality. Unigine is somewhere in the middle of this scale, it incorporates everything one needs to implement a game or other 3D application, except for the application logic, networking, and AI. Unigine includes only typical game logic, common for applications of all types, and this is done intentionally. Существуют разные подходы к настройке игровой архитектуры, от комплексных решений, в которых игровая логика объединена с движком, до кучи отдельных модулей, каждый из которых отвечает за одну часть функциональности. Unigine находится где-то посередине этой шкалы, он включает в себя все, что нужно для реализации игры или другого 3D-приложения, за исключением логики приложения, сетей и ИИ. Unigine включает в себя только типичную игровую логику, общую для приложений всех типов, и это сделано намеренно.
This may not seem an advantage, because in order to create a game, one needs to do a great deal of programming and by themselves implement common functionality widely used in games of the required genre. However, lack of the genre-specific game logic is not so bad, as it makes Unigine a general-purpose library, easily re-usable in different projects. Moreover, developers are not bound to some specific genre, they can experiment with several genres at once, which is extremely difficult if a special-purpose engine is used. For example, it's difficult to make a mix of a shooter and a racing game out of an engine created solely for first-person shooters. Also, Unigine allows plugging of external modules, which can contain genre-specific functionality and can be re-used, too. This all makes Unigine a rather flexible base for a wide range of 3D applications. Это может показаться не преимуществом, потому что для создания игры нужно много программировать и самостоятельно реализовывать общие функции, широко используемые в играх требуемого жанра. Тем не менее, отсутствие логики игры, ориентированной на определенный жанр, не так уж плохо, поскольку делает Unigine универсальной библиотекой, которую легко использовать в различных проектах. Более того, разработчики не привязаны к какому-то конкретному жанру, они могут экспериментировать сразу с несколькими жанрами, что крайне сложно, если используется специализированный движок. Например, сложно совместить шутер и гоночную игру на движке, созданном исключительно для шутеров от первого лица. Кроме того, Unigine позволяет подключать внешние модули, которые могут содержать специфичные для жанра функциональные возможности, а также могут использоваться повторно. Все это делает Unigine довольно гибкой базой для широкого спектра 3D-приложений.
To understand the architecture of Unigine is very important both for developers and for content creators. The former will know what they are allowed to do, what they can achieve, and what to expect from Unigine. The latter will understand how their content is going to be processed. That is why this article provides a brief and high-level overview of the Unigine workflow. Понять архитектуру Unigine очень важно как для разработчиков, так и для создателей контента. Первые будут знать, что им разрешено делать, чего они могут достичь и чего ожидать от Unigine. Последние поймут, как будет обрабатываться их контент. Вот почему эта статья представляет собой краткий общий обзор рабочего процесса Unigine.
The diagram below demonstrates interrelations between internal components of Unigine and different external entities. Схема ниже демонстрирует взаимосвязь между внутренними компонентами Unigine и различными внешними объектами.
Everything starts when the custom application logic calls Unigine API functions, These calls can be done using either UnigineScript or C/C++ directly. The former is a more preferable way, as in this case the programmer doesn't have to think about multi-threading and memory management. Also, if the application is written in UnigineScript, it will run on each platform supported by Unigine. The API component loads modules that extend the Unigine functionality, if that is required by the application logic. Such modules may contain, for example, third-party middleware. Все начинается, когда настраиваемая логика приложения вызывает функции Unigine API. Эти вызовы могут выполняться напрямую с помощью UnigineScript или C / C ++. Первый вариант более предпочтителен, так как в этом случае программисту не нужно думать о многопоточности и управлении памятью. Кроме того, если приложение написано на UnigineScript, оно будет работать на каждой платформе, поддерживаемой Unigine. Компонент API загружает модули , которые расширяют функциональные возможности Unigine, если это требуется логикой приложения. Такие модули могут содержать, например, промежуточное ПО сторонних производителей.
API calls are passed to the interpreter, which initializes required resources: registers extensions, loads core data, configuration files, scripts, and user interface files. As all these resources are organized in a special data directory and, moreover, can be packed, they are loaded by means of the file system component. In addition, the file system component tracks the endianness of files. Note that if you add files to this directory after the initialization is completed, you need to reload the file system component. Вызовы API передаются в интерпретатор , который инициализирует необходимые ресурсы: регистрирует расширения, загружает основные данные, файлы конфигурации, сценарии и файлы пользовательского интерфейса. Поскольку все эти ресурсы организованы в специальный каталог данных и, кроме того, могут быть упакованы , они загружаются с помощью компонента файловой системы . Кроме того, компонент файловой системы отслеживает порядок байтов файлов. Обратите внимание: если вы добавляете файлы в этот каталог после завершения инициализации, вам необходимо перезагрузить компонент файловой системы.
After initialization, the world manager comes into play. It loads files, which are required to build the current scene, and determines the set of visible nodes, which later will be sent to the render component. The world manager also tells the sound component, when and how to play environmental sounds, which sources are placed somewhere in the world and have spatial properties. Of course, the world manager cooperates with the physics component, which performs physical calculations (collision detection, joints solving, fluid buoyancy, and so on) and, possibly, updates the node hierarchy. To put it shortly, the world manager does not draw objects, play sounds, or perform physical calculations on its own. Instead, it delegates these tasks to designated components. После инициализации в игру вступает менеджер мира . Он загружает файлы, необходимые для построения текущей сцены, и определяет набор видимых узлов, которые позже будут отправлены в компонент render . Менеджер мира также сообщает компоненту sound , когда и как воспроизводить звуки окружающей среды, какие источники расположены где-то в мире и имеют пространственные свойства. Конечно, менеджер мира взаимодействует с компонентом физики , который выполняет физические вычисления (обнаружение столкновений, решение суставов, плавучесть жидкости и т.д.) И, возможно, обновляет иерархию узлов. Короче говоря, менеджер мира не рисует объекты, не воспроизводит звуки и не выполняет физические вычисления самостоятельно. Вместо этого он делегирует эти задачи определенным компонентам.
The render and sound components need additional resources to complete their tasks: the render component needs textures, meshes, and animations, and the sound component needs sounds. So, they ask the resource manager to provide them what is required. It is the task of the resource manager to load required graphics and sounds, cache them, and unload if they are no longer required. The presence of the resource manager allows the game developer not to think about how and when the data should be loaded and unloaded. Компоненты рендеринга и звука нуждаются в дополнительных ресурсах для выполнения своих задач: компоненту рендеринга нужны текстуры, сетки и анимации, а звуковому компоненту нужны звуки. Поэтому они просят менеджера ресурсов предоставить им то, что требуется. Задача менеджера ресурсов - загрузить необходимую графику и звуки, кэшировать их и выгружать, если они больше не требуются. Наличие менеджера ресурсов позволяет разработчику игры не думать о том, как и когда данные следует загружать и выгружать.
If required by the application logic, the interpreter may also call the GUI component and make it draw the user interface via the render component. In Unigine, GUI objects can be not only stand-alone, but also a part of the displayed virtual world. In the latter case, they are managed by the world manager just like other nodes. Если этого требует логика приложения, интерпретатор может также вызвать компонент GUI и заставить его рисовать пользовательский интерфейс через компонент рендеринга. В Unigine объекты GUI могут быть не только автономными, но и частью отображаемого виртуального мира. В последнем случае они управляются мировым менеджером, как и другие узлы.
When the rendered image, possibly containing the GUI, is displayed, the user, of course, starts interacting with the world. The user can influence the world by means of various input devices. Input from these devices is sent to the GUI component and to the controls component. The GUI component processes the input, detects the clicked element, and executes the corresponding callback function. The controls component processes input that is not related to the GUI, for example the player's actions in the game. Note that the GUI component always gets the input data before the controls component and, therefore, has a higher priority. Когда отображается визуализированное изображение, возможно, содержащее графический интерфейс, пользователь, естественно, начинает взаимодействовать с миром. Пользователь может влиять на мир с помощью различных устройств ввода. Входные данные с этих устройств отправляются в компонент графического интерфейса пользователя и компонент элементы управления . Компонент GUI обрабатывает ввод, обнаруживает элемент, на который щелкнули, и выполняет соответствующую функцию обратного вызова. Компонент управления обрабатывает ввод, не связанный с графическим интерфейсом, например, действия игрока в игре. Обратите внимание, что компонент GUI всегда получает входные данные перед компонентом управления и, следовательно, имеет более высокий приоритет.
The processed input is passed to the interpreter, which detects if the user actions influence render or physics settings, etc. If yes, the interpreter asks the corresponding components to update the image or calculations. If the user input changes something in the world, the world manager updates the set of visible nodes and sends new requests to designated components. Обработанный ввод передается интерпретатору, который определяет, влияют ли действия пользователя на параметры рендеринга или физики и т. д. Если да, интерпретатор просит соответствующие компоненты обновить изображение или вычисления. Если ввод пользователя что-то меняет в мире, менеджер мира обновляет набор видимых узлов и отправляет новые запросы указанным компонентам.
These actions are repeated in a cycle, until the user quits the application. Эти действия повторяются в цикле, пока пользователь не выйдет из приложения.