C# Component System
Component System enables you to implement your application’s logic via a set of building blocks — components, and assign these blocks to nodes, giving them additional functionality. By combining these small and simple blocks you can create a very sophisticated logic system.
A logic component integrates a node and a C# class, containing logic implementation (actions to be performed), defining a set of additional parameters to be used.
Components give you more flexibility in implementing your logic, enabling you to:
- Control which parts of code (implemented as component methods) are to be executed, and which of them are not.
- Control execution order of these parts of code.
- Repeatedly use parts of code, written once, for as many objects as you need, with no modifications required. If you want to change your code, you modify a single source (similar to NodeReferences, if we talk about content).
- Combine certain parts of code to be executed for certain nodes. Build a very sophisticated system from numerous small and simple blocks (like you would use a NodeReference to build a large complex structure using many simple nodes).
Component System is available for all projects using C# (.NET Core) API.
Before starting coding, you should install required software.
Requirements#
Proper workflow for programming and building .NET Core-based projects implies a set of requirements:
- .NET Core 2.2 SDK
- an IDE or a text editor. Compatibility of different IDEs with the following .NET Core versions is checked:
IDE Supported .NET Core version MS Visual Studio Code 2.2.3 MS Visual Studio 2019 2.2.203 MS Visual Studio 2017 2.2.106 MS Visual Studio 2015 not supported Visual Studio Code is the recommended option.
If you work with MS Visual Studio Code, install the C# extension for Visual Studio Code powered by OmniSharp when first opening the component.
Creating a Component#
Components are created using the Editor. Open the Asset Browser and choose Create > Create C# Component.
Specify a name for the component in the prompt dialog, after which a C# script asset file with the corresponding name will be created in the current directory of the Asset Browser.
Double-click the new script asset to edit the component logic in the default IDE.
Structure of a Component#
Essentially components are C# classes inherited from the base Component class.
The work of the C# Component System is based on properties. When you create a new component, it is automatically registered in the ComponentSystem and an internal runtime property is created and associated with the component via a GUID. The following Component class attribute is required for proper work of the component.
[Component(PropertyGuid = "2ae011355ed7f110a202912faa000cc22276e539")]
C# components are listed in the Properties hierarchy, initially they are inherited from the base C# Components property.
Logic of components is implemented via a set of methods, that are called by the corresponding functions of the world script:
- Init() — create and initialize all necessary resources
- Update() — specify all logic functions you want to be called every frame
- UpdateAsyncThread() — specify all logic functions you want to be called every frame independently of the rendering framerate. This method does not have protection locks, so it is not recommended to modify other components inside this method, unless you are absolutely sure, that these components won't be modified or removed elsewhere.
- UpdateSyncThread() — specify all logic functions you want to be executed before the Update() (prior to updating nodes PlayerSpectator, ParticleSystem etc.). This method can be used to perform some heavy resource-consuming calculations such as pathfinding, generation of procedural textures and so on.
- PostUpdate() — correct behavior according to the updated node states in the same frame
- UpdatePhysics() — simulate physics: perform continuous operations (pushing a car forward depending on current motor's RPM, simulating a wind blowing constantly, perform immediate collision response, etc.).
- Shutdown() — perform cleanup on component shutdown
- DestroyRenderResources() — destroy all dynamically created render-related resources (for example, textures or shaders) to avoid crashes due to invalid pointers, when the video mode is changed or application is restarted
Components can have parameters that are editable in the Parameters window.
The following entities have auto-generated UI in the Editor based on the data type and the set of attributes:
- public fields of the component class
- any private and protected fileds with the [ShowInEditor] option
public int public_field;
private int private_field;
[ShowInEditor]
private float private_editable_field;
[HideInEditor]
public float public_hidden_field;
Refer to the Component class for more details on supported parameter types and attributes.
Applying Component Logic to a Node#
Logic implementation described in a component is active at run time only if the component is assigned to a node and both node and component are enabled.
There are several ways of applying a component to a node:
-
Select a node, click Add New Property and type the name of a *.cs asset in the Node Properties section of the Node tab. You can do it by dragging the *.cs asset there from the Asset Browser window as well.
Dragging to the node in the Scene viewport is also supported.
- Add a component to a node via code by using the AddComponent<T>(Node node) and Node's AddComponent<T>() functions.
NewComponent component = AddComponent<NewComponent>(node);
NewComponent component = node.AddComponent<NewComponent>();
The logic of a certain component is active only when the component and the corresponding node are enabled. Thus, you can enable/disable logic of each particular component at run time when necessary.
You can assign several components to a single node. The sequence, in which the logic of components is executed, is determined by the order value specified for the corresponding methods (if order values are the same or not specified, the sequence is indeterminable).
Components can interact with other components and nodes.
Running a .NET Core Project#
To run a project using .NET Core click the Play button on the toolbar. This will run an instance of the application in a separate window.
For the correct display of error messages in Windows, the language for non-Unicode programs should be the same as the current system locale.
Presets of custom run options are available in the list. By default, there is a single Default (Debug) preset with the default run options. Click the gear icon to configure the current selected preset of custom run options.
In the window that opens the following run options are available:
Configuration | UNIGINE Engine build to be used |
---|---|
Video API |
Graphics API to be used for rendering:
|
Resolution | Screen size |
Fullscreen | Run the instance either in Fullscreen or windowed mode |
VR Mode |
Enable compatibility with one of supported VR headsets:
|
Video Debug |
Enables the debug context of OpenGL or DirectX:
|
Run Current World | Run the current world opened in the Editor regardless of the default world set by logic. |
Arguments | A set of startup command-line options. |
On changing any custom run option and closing the window, the following actions will be performed depending on the preset selected:
- If the Default (Debug) preset is selected in the list, a new *.launch asset file containing the custom run options will be created in the current folder of the Asset Browser. The corresponding preset will be available in the list of presets.
- If another preset is selected, changes will be applied to it.
Building the Application#
To create a debug or release build of your C# application, use the File -> Create Build option available in UnigineEditor.
See the article Building a Project for more details.
Usage#
As an example, you can use components to implement logic of enemies chasing the player in your game: regardless of their size, shape, speed, all of them will check player's position, and try to find a path to move closer to it as fast as they can. The code will be basically the same, it'll just use different parameters (speed, mesh, or sounds maybe), so you can put all these parameters to a component (to be able to change them at any time) and the code to the corresponding component class (e.g. place enemies in the world in the Init() and chase the player in the Update() method).
Then you should simply assign the component to all enemy objects and set up 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. Should you decide to modify your code later, you can do that in a single source — component class.
Integration with the Microprofile tool, enables you to monitor overall performance of the Component System, as well as to add profiling information for your custom components.
See Also#
- C# Component System API for more details on managing components via C# API.
- C# Component System Usage Example for more details on implementing logic using the C# Component System.