Modules
The block module system is a way to add behaviour to blocks without having to write any code.
There are some official modules included by default:
- Steering
- Spinning
- Spewing (Particle Systems)
- Shooting
Are you looking for the documentation for these official modules?
Additional modules can also be added by mods and then be used by other mods.
Using Modules
Blocks can contain a Modules
element. Inside, one or more modules can be configured.
Note: While it is possible to add multiple modules to a block, not all modules are necessarily compatible with each other. Two modules that both try turning or rotating the block are unlikely to work well in tandem for example.
Here's an example for declaring an official module:
<Block>
...
<Modules>
<Steering>
[Steering Parameters Here]
</Steering>
</Modules>
</Block>
Adding modules of other mods is similar:
<Block>
...
<Modules>
<SomeModdedModule
modid="<id of mod that adds module here>" >
[Module Parameters Here]
</SomeModdedModule>
</Modules>
</Block>
Note that you also need to ensure that the mod that adds the module is loaded before yours
by setting an appropriate LoadOrder
value in one (or both) of the mods.
Each module can define its own parameters, so look up the documentation for the respective module to find out how to configure it.
Documentation for the official modules is also available.
Mapper Types in Modules
Where modules use mapper types in the block mapper, it is usually necessary to first define
them in the ModuleMapperTypes
element of Block
, for example:
<Block>
<ModuleMapperTypes>
<Key displayName="Some Name" key="unique-key" default="defaultKey" />
</ModuleMapperTypes>
</Block>
Here's how to declare each of the available mapper types:
<!-- The valid values for default are entries of the UnityEngine.KeyCode enum.
In the simplest case, this means a capital letter, like C -->
<Key displayName="Some Name" key="unique-key" default="defaultKey" />
<!-- unclamped is optional and false by default. If it is set to true, min and max only
apply to the slider itself but it is possible to type in values outside of these bounds. -->
<Slider displayName="Some Name" key="unique-key" min="0.0" max="10.0"
default="5.0" unclamped="false">
<Toggle displayName="Some Name" key="unique-key" default="false" />
<Value displayName="Some Name" key="unique-key" default="5.0" />
<ColourSlider displayName="Some Name" key="unique-key"
r="1.0" g="1.0" b="1.0" snap="false" />
Then you can reference the key in a module like this (example from the Steering module):
<LeftKey key="unique-key" />
This system makes it possible to share sliders, keys, or toggles among modules, if they should be controlled simultaneously.
Writing custom Modules
There are two parts to a module: A class extending BlockModule
that is used to deserialize
the module and its parameters from the block XML file and a class extending
BlockModuleBehaviour<TModule>
that is attached to the block as a component.
The BlockModule
class (hereafter called TModule
) is basically a collection of properties
that represent the parameters available to users of the module.
It must have an [XmlRoot("ModuleName")]
attribute, where ModuleName
is the name that
users of the module should specify in their block XML files.
For information on how to write the class so that it is correctly deserialized, see Serialization.
The BlockModuleBehaviour
class (hereafter called TBehaviour
) is used to actually add
behaviour to the block. It has access to the deserialized TModule
via the Module
property.
Apart from that, it has basically the same API available to it as a BlockScript
.
When both classes are created, the module needs to be registered by calling
CustomModules.AddBlockModule<TModule, TBehaviour>(name, canReload)
. name
is the same
name that is in the XmlRoot
attribute. canReload
specifies whether the modules supports
value reloading. See below for more information.
This should be called during execution of the mod's OnLoad
method.
When adding mapper types (other thank keys) in modules, prefix the key with a relatively unique value, like the name of your mode, to prevent conflicts with other modules that may eventually be put on the same block by users.
Mapper Types in Custom Modules
To use the ModuleMapperTypes
system described above, add a field like this to TModule
:
[XmlElement, RequireToValidate]
public MKeyReference MyKey;
Here, MyKey
is also the name that will be used in the XML file. Other available classes
are MSliderReference
, MToggleReference
, MValueReference
, and MColourSliderReference
.
Then, in TBehaviour.SafeAwake
:
myKey = GetKey(Module.MyKey);
GetKey
returns an MKey
, just like the normal AddKey
. Here it is assumed that the
myKey
variable/field was previously declared.
Resources in Custom Modules
While normally, a mod can only access its own resources, modules can also access the resource of the mod that declares the block.
Accessing those resources is similar to the key setup. First create an XML element of type
ResourceReference
or a MeshReference
, with a RequireToValidate
attribute.
(MeshReference
has additional child elements for position, rotation and scale.)
Then, in the behaviour, GetResource
can be called with the reference and returns a
ModResource
instance that can be cast depending on the resource type.
Reloading for Custom Modules
Custom modules can optionally support the value reloading system. This allows modders using
the module to turn on Debug
for their mod and edit some (or all) of the module's XML
parameters during runtime and immediately see the effects in-game.
For details on how to implement reloading, see the
Reloading section in Serialization.
There are two special cases to consider when implementing reloading for modules specifically:
By extending BlockModule
, your TModule
type automatically implements IReloadable
.
The two methods of the interface are empty by default and declared in the base class,
you can override them if you need them.
Additionally, you can also override void OnReload()
in the TBehaviour
class, since reloaded
values should also be applied to existing blocks. If the behaviour just accesses the modules'
values in methods like Update
, this override is probably not necessary, but for some values
certain actions may need to be taken, for example to update properties of other components
that were previously created.
Lastly, tell the mod loader that the module supports reloading by setting the canReload
argument of CustomModules.AddBlockModule
to true
.
Don't forget to document which values can be reloaded and which can't!