Components with C++
To write a custom C++ component, the first thing you need is a custom engine plugin. Once you have that, and have it enabled in your project settings, any custom component that you define in that plugin will show up in the editor and can be attached to game objects.
The Sample Game Plugin shows all the pieces that you need, including multiple components to get inspiration from. This article describes the steps to create a simple custom component.
Before you continue, please read the components chapter, as it already covers most things that you need to know.
Component Manager Declaration
For every type of C++ component there is a corresponding component manager. The component manager is responsible for allocating and deallocating components and for updating them. Each component manager is tied to a single world, so if you have multiple worlds, each world will hold its own instance of each component manager.
A component manager is a world module, so it can register functions to be called during specific update phases of the world.
For the vast majority of components we only need a component manager that calls Update()
on our component type once a frame. We can declare such a simple manager like this in the header file for our component:
Component Class Declaration
Next, we declare our component class. All components must derive (at least indirectly) from plComponent
. Also vital is to insert the PL_DECLARE_COMPONENT_TYPE
macro, where you pass in the own component class name, the base class, and the component manager class.
Here we override a couple of functions from plComponent
. For the binary serialization we must implement plComponent::SerializeComponent()
. As long as you test your component only inside the editor, you don't yet need to implement these functions, as the editor stores reflected properties automatically. However, once you want to export your scene, these functions are used, and if you forgot to properly serialize something, the exported scene will not work correctly.
Note that our sample component has a (non-virtual) function called Update()
. This is necessary because we use the plComponentManagerSimple
here, which expects to find such a function. If you write your own component manager, you can do this differently.
Reflection Block
In our cpp file we need to insert a reflection block for our component type. This tells the engine all the details about our component, for instance which properties it has.
This information is used in various ways. The editor uses it for the UI. Attributes on each property allow you to configure what default values the editor should use, and whether it should clamp the range for values, etc. Bindings to other languages also use this information to generate the necessary code. Everything that is not mentioned in this block, is internal to the C++ code and hidden from the tools.
Initialization and Update
Next up, we implement our basic component code:
Components rarely need to do much in their constructor and destructor. Most setup should be done in plComponent::OnSimulationStarted()
. For components that should already have functionality in the editor, while the simulation is not yet running, you should do your setup in plComponent::OnActivated()
instead. There is no OnSimulationStopped()
, as this would always be the same as plComponent::OnDeactivated()
.
As you can see, this component modifies the position of its owner object during its update. This is why we had to use plComponentMode::Dynamic
in the reflection block, to tell the engine that objects with this component attached may change their position.
Serialization
Finally, to make our component also work in exported scenes, we need to implement serialization:
This writes out the data in the latest format. If you change the format, you should increase the version number of your component in the reflection block at the very top.
Obviously, at runtime we also need to deserialize our component. This is where we implement backwards compatibility for older exported scenes:
Conclusion
Adding a custom component in C++ is not hard. Use the Sample Game Plugin as a playground to get started. Of course with C++ you have the typical restriction that you can't hot reload code, you have to close the editor, compile your plugin and reopen the editor. Hot Reloading C++ Game Plugins in the Editor describes a mechanism that can basically do all that for you with a single button press, though.
Armed with these basics, you should have a look at existing components to see how to solve specific issues.
See Also
Components
Custom Code
Sample Game Plugin