Основы
So, as we have already said, logic (or program code) is what breathes life into the content and makes it perform the task at hand.Итак, как мы уже говорили, логика (или программный код) – это то, что вдыхает в контент жизнь и заставляет его выполнять поставленную задачу.
To implement the project logic in UNIGINE, you can use C# or C++ (if you need the best performance and effective integration with the existing source code). In most cases C# is the optimal option, as it is more widespread and provides the best ratio of speed and ease of use. If you want, you can combine programming languages (for example, you can use C++ for performance consuming operations as a plugin and then access it from C# code).Для реализации логики проекта в UNIGINE можно использовать C# или же C++ (если нужна максимально возможная производительность и эффективная интеграция с существующей базой исходного кода). Оптимальным вариантом в большинстве случаев является C#, поскольку он наиболее распространен и обеспечивает наилучшее соотношение скорости и простоты использования. При желании языки программирования можно комбинировать (например реализовать критичный по производительности функционал на C++ в виде плагина, и потом обращаться к нему из кода на C#).
Every UNIGINE-based application has its life cycle, that consists of certain stages, some of them are performed once, others are repeated each frame. In short, these stages are as follows:У UNIGINE-приложений собственный жизненный цикл, который состоит из определенных этапов. Какие-то из этих этапов выполняются однократно, другие отрабатываются каждый кадр. Схематически эти этапы можно представить следующим образом:
UNIGINE has three main logic components, each of them has a set of functions (named Init(), Update(), Render(), etc.) that contain actions to be performed at corresponding stages of the Engine's working cycle. These components are:В UNIGINE есть три основных логических компонента. В каждом из них есть набор функций (которые называются Init(), Update(), Render() и т.д.), содержащих действия, выполняемые на соответствующих этапах рабочего цикла движка. Вот эти компоненты:
-
System Logic is the code that is run during the whole application life cycle (its scope exists even when switching between worlds).Системная логика (System Logic) включает в себя код, который выполняется в течение всего жизненного цикла приложения (эта компонента работает и при переключении между мирами).
- For applications that use C++ AppSystemLogic.cpp is created, and for C# applications — AppSystemLogic.cs. This file is stored in the source/ folder of your project. It has implemented methods to put your logic code inside.Для приложений на C++ создается файл AppSystemLogic.cpp, а для приложений на C# — AppSystemLogic.cs. Этот файл хранится в папке source/ вашего проекта. В файле уже реализованы соответствующие методы, куда можно писать свой код.
-
World Logic is the logic of the virtual world. The logic takes effect only when the world is loaded.Логика мира (World Logic) — это логика виртуального мира. Эта логика используется, только когда загружен данный мир.
- For applications that use C++ AppWorldLogic.cpp is created, and for C# applications — AppWorldLogic.cs. This file is stored in the source/ folder of your project and stays loaded during the whole engine runtime. It has implemented methods to put your logic code inside.Для приложений на C++ создается файл AppWorldLogic.cpp, а для приложений на C# — AppWorldLogic.cs. Этот файл хранится в папке source/ вашего проекта и остается загруженным в течение всего рабочего цикла движка. В файле так же уже реализованы соответствующие методы для добавления кода.
- Editor Logic. This component is to be used in case you need to implement your own Editor. It has more implemented methods providing you with clear understanding of the current Engine events (a node has been created, a property has been deleted, a material has been changed, etc.).Логика редактора (Editor Logic). Эту компоненту следует использовать в случае, если вам нужен собственный редактор. В нем реализовано больше методов, дающих вам четкое представление о текущих событиях движка (создание ноды, удаление свойства, изменение материала и т.д.).
The files described above are useful for writing code referring to the application itself (for example, if you need to perform some actions before starting the main loop or when switching between worlds) or to a world (for example, selecting the loading screen depending on which world is being loaded), but the most common approach is to bind logic to objects. For this purpose, UNIGINE has the Component System, which allows you to implement the application logic using a set of building blocks — components, and assign these components to nodes, thus extending their basic functionality (for example, an ordinary static mesh can be made interactive — reacting to a mouse click in a certain way). By combining these small and simple blocks, you can create a very complex logical system.В описанных выше файлах удобно писать код с привязкой к самому приложению (например, если нужно выполнить какие-либо действия до запуска основного рабочего цикла или при переключении между мирами) или к миру (например, изменение экрана загрузки в зависимости от того, какой мир загружается в данный момент), но наиболее распространенный подход – привязывание логики к объектам. Для этого в UNIGINE есть Компонентная система (Component System), которая позволяет реализовать логику приложения при помощи набора строительных блоков – компонентов и привязывать эти компоненты к нодам, расширяя тем самым их базовый функционал (например, обычный статический меш можно сделать интерактивным – реагирующим как-то на щелчок мыши). Комбинируя эти маленькие и простые блоки, вы можете создать очень сложную логическую систему.
A logic component combines a node and a C # class that contains a logical implementation (actions to be performed), and also defines a set of additional parameters (required to perform these actions).Логический компонент объединяет ноду и класс C #, содержащий логическую реализацию (действия, которые должны быть выполнены), а также определяет набор дополнительных параметров (нужных для выполнения этих действий).
LogicЛогика#
The component logic is implemented using a set of methods that are called by the corresponding World Logic functions:Логика компонентов реализована с помощью набора методов, которые вызываются соответствующими функциями логики мира (World Logic):
- Init() — all necessary resources are created and initialized.Init() — создаются и инициализируются все необходимые ресурсы.
-
UpdateAsyncThread() — used for specifying all logic functions you want to be called every frame independently of the rendering thread.UpdateAsyncThread() — указываются все логические функции, которые вы хотите вызывать в каждом кадре независимо от потока рендеринга.
This function does not have protection locks, so it is not recommended to modify other components inside this function, unless you are absolutely sure, that these components won't be modified or removed elsewhere.Этот метод не имеет защитных блокировок, поэтому не рекомендуется изменять другие компоненты внутри этого метода, если вы не уверены, что эти компоненты не будут изменены или удалены где-либо еще. -
UpdateSyncThread() — used for specifying any parallel logic functions to be executed before the Update(). This method is used to perform resource-consuming calculations such as pathfinding, procedural texture generation, etc.UpdateSyncThread() — указываются все параллельные функции логики, которые вы хотите выполнить перед Update(). Этот метод можно использовать для выполнения некоторых ресурсоемких вычислений, таких как поиск пути, генерация процедурных текстур и т.д.
This method should be used to call only those API methods that apply to the current node: the node itself, its materials and properties.Этот метод следует использовать для вызова только тех методов API, которые относятся к текущей ноде: самой ноде, ее материалам и свойствам. - Update() — used for specifying all logic-related functions you want to be called every frame.Update() — указываются все логические функции, которые должны вызываться в каждом кадре.
- PostUpdate() — used to adjust the behavior according to the updated states of the nodes in the same frame.PostUpdate() — корректируется поведение в соответствии с обновленными состояниями нод в том же кадре.
- UpdatePhysics() — physics simulation is performed: continuous operations (moving the car forward depending on the engine speed, simulating constant wind, performing immediate reactions to a collision, etc.).UpdatePhysics() — выполняется симуляция физики: выполнение непрерывных операций (перемещение машины вперед в зависимости от оборотов двигателя, имитация постоянного ветра, выполнение немедленных реакций на столкновение и т.д.).
-
Swap()— operating with the results of the UpdateAsyncThread() method — all other methods (threads) have already been performed and are idle. After this function, only two actions occur:Swap() — работа с результатами метода UpdateSyncThread() — все остальные методы (потоки) уже выполнены и ждут. После этой функции происходит только два действия:
- All objects that are queued for deletion are deleted.все объекты, поставленные в очередь на удаление, удаляются;
- Profiler is updated.выполняется обновление Профилировщика.
- Shutdown() — cleanup is performed when the component is shutdown.Shutdown() — выполняется очистка при выключении компонента.
The most frequently used functions are Init() and Update().Наиболее часто используются функции Init() и Update().
The logic of a certain component is applied only when the component and the corresponding node are enabled. Thus, you can enable/disable the logic of each specific component at runtime, if necessary.Логика определенного компонента применяется только тогда, когда компонент и соответствующая нода включены. Таким образом, при необходимости можно включать/отключать логику каждого конкретного компонента во время выполнения.
If some method of a component should always be executed regardless of this condition, the InvokeDisabled option should be included in the definition:Если какой-то метод компонента должен выполняться всегда, независимо от выполнения этого условия, то в определении нужно включить опцию InvokeDisabled.
[Method(InvokeDisabled = true)]
void Init() {
// инициализация выполняется даже если компонент отключен
}
void Update() {
// выполняется только когда компонент включен
}
You can assign as many components to a node as you need. The sequence in which their logic is executed is determined by the order value specified for the corresponding methods. In the example below, the Component1.Init() method will be executed first, followed by the Component2.Init() method:Одной ноде можно назначить сколько угодно компонентов. Последовательность, в которой выполняется их логика, определяется значением order, указанным для соответствующих методов. В примере ниже сначала выполнится метод Component1.Init() затем – Component2.Init():
// Component1
[Method(Order=2)]
void Init() {
// ...
}
// Component2
[Method(Order=3)]
void Init() {
//...
}
If the order values are the same or not specified, the execution sequence of methods with the same name will be determined by the order of the components in the Properties window.Если значения order совпадают или не указаны, последовательность выполнения одноименных методов будет определяться порядком компонентов в окне Properties.
Components can interact with other components and nodes.Компоненты могут взаимодействовать с другими компонентами и нодами.
As an example, you can use components to implement the logic of chasing the player by enemies in your game: regardless of their size, shape, or speed, they will all check the player's position and try to find the path to get to him the as soon as possible. The code will be mostly the same, only the parameters (speed, meshes and probably sounds) may differ, so you can add all these parameters to the component (to easily change them at any time) and write code in the methods of the corresponding class (e.g. add enemies to the world in the Init() method, and implement chasing the player in the Update() method).В качестве примера, можно использовать компоненты для реализации логики преследования игрока врагами в вашей игре: независимо от их размера, формы и скорости, все они будут проверять позицию игрока и пытаться найти путь, по которому можно быстрее всего до него добраться. Код будет по большей части одинаковым, могут различаться лишь параметры (скорость, меш и, возможно, звуки), поэтому можно добавить все эти параметры в компонент (чтобы можно было изменять их в любое время) и написать код в методах соответствующего класса (например, добавление врагов в мире в методе Init(), а само преследование игрока реализовать в методе Update()).
Then you just need to assign a component to all enemy objects and customize the parameters (define meshes, sounds, etc.). The component system will do the rest: execute your code at the corresponding stages of the Engine's main loop for all enemy objects, using their specific parameters. If you later want to change the code (such as to improve the chase algorithm), you'll need to do that in one place only — the component class.Затем нужно просто назначить компонент всем объектам врагов и настроить параметры (определить меши, звуки и т.д.). Система компонентов сделает все остальное: выполнит ваш код на соответствующих этапах основного цикла Движка для всех объектов врагов, используя их конкретные параметры. Если вы впоследствии захотите изменить код (скажем, улучшить алгоритм преследования), то это достаточно будет сделать в одном единственном месте — классе компонента.
So, let's sum up – using components provides more flexibility in logic implementation, allowing you to:Итак, подытожим – использование компонентов обеспечивает большую гибкость в реализации логики, позволяя:
- Define which parts of code (implemented in component methods) should be executed and which should not.Определять, какие части кода (реализованные в методах компонентов) должны выполняться, а какие – нет.
- Control the execution order of these code parts.Контролировать порядок выполнения этих частей кода.
- Reuse parts of code as many times as you need for any number of objects without any changes. If you need to change your code, you'll have to modify only one source (similar to working with NodeReference, if we are talking about content).Многократно переиспользовать части кода, написанные один раз, для любого количества объектов без каких-либо изменений. Если нужно внести изменения в свой код, вы изменяете единственный источник (аналогично работе с NodeReference, если мы говорим о контенте).
- Combine specific pieces of code that will be executed for specific objects. You can build a very complex system out of many small and simple blocks (similar to using NodeReference to create a large and complex structure out of many simple elements).Объединять определенные части кода, которые будут выполняться для определенных объектов. Можно построить очень сложную систему из множества маленьких и простых блоков (аналогично использованию NodeReference для создания большой и сложной структуры из множества простых элементов).
Installing the Required SoftwareУстановка необходимого ПО#
To work with C# components we recommend using MS Visual Studio Code environment, by default it is installed together with SDK, or you can download it from the official site (https://code.visualstudio.com/download) and install. And in order to make writing C# code as convenient as possible — with syntax highlighting, tooltips, and displaying the engine API documentation, install the C# extension from OmniSharp for Visual Studio Code.Для работы с C# компонентами мы рекомендуем использовать среду MS Visual Studio Code, по умолчанию она устанавливается вместе с SDK, либо можно скачать ее с официального сайта (https://code.visualstudio.com/download) и установить. А для того, чтобы писать C# вам было максимально удобно, с подсветкой синтаксиса, всплывающими подсказками и отображением документации по API движка, дополнительно установите расширение C# для Visual Studio Code от OmniSharp.
A logic component integrates a node, a C++ class, containing logic implementation (actions to be performed), and a property, defining a set of additional parameters to be used. The list of parameters as well as their types are the same for both the component and the corresponding property. Component is assigned to a node via a property.Now, once everything is installed, you can create a project and start writing code!Теперь, после того, как все установлено, можно создавать проект и начинать писать код!