Custom 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, 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.
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).
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
- parallelupdate() - is executed after the update() and before the render() and can be used to perform some heavy resource-consuming calculations such as pathfinding, generation of procedural textures, etc.
- render() - correct behavior according to the updated node states in the same frame
- flush() - 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 world shutdown
- destroy() - destroy all created resources to avoid crashes due to invalid pointers, when the video mode is changed or application is restarted
For 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 property (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 property 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.
The basic workflow is as follows:
- Inherit a new C++ class representing your component from the ComponentBase class.
- In the header file determine and declare the list of parameters to be used by this component. All of these parameters with their default values (if specified) will be stored in a dedicated property file.
- Implement component logic inside the certain methods (init(), update(), render(), etc.), that will be called by the corresponding functions of the Engine's main loop.
- Assign the created property to a node to give it the desired functionality.
Each time a property registered in the Component System is assigned to a node, an instance of the corresponding component is created. This instance will be deleted when the corresponding property is replaced with another one or removed from the node’s list, or when the node is deleted.
The logic of a certain component is active only when the corresponding node and property are enabled. Thus, you can enable/disable logic of each particular component at run time when necessary.
You can assign several properties corresponding to different 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 determined in accordance with the hierarchy of nodes).
You can also create components for existing properties.
Components can interact with other components and nodes.
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.
The Custom Component System is extendable and can be easily modified to add the desired functionality if necessary.