Code Generation - GoldhawkInteractive/X2-Modding GitHub Wiki
Code Generation is activated within Unity using the menu Artitas -> Generate
and based on runtime reflection.
This requires a project to be in a compiling state before code generation can happen.
See CodeGenerationTestcase
if you want to execute the generation in the build process or through code.
-
IComponent
DSL: By default, the Artitas Codegeneration framework generates DSL forIComponent
s which allow you to easily access, add or remove components from anEntity
. The following is generated forIComponent
s:-
Add{X}(...)
: An add method that will initialize anIComponent
using all its publicly accessible fields and properties. If a specific method is required for initialization, see[Initializer]
. -
Remove{X}()
: Removes the component from theEntity
. -
{X}()
: Accesses the component on theEntity
.
-
- Special DSL is generated for any
IComponent
that inherits from the following types:-
RangeComponent
: A component that represents a value constrained to a range. (Minimum, Maximum & Value) The following DSL is generated on the Entity for this:-
Delta{X}(value)
: Adds the given value to the Value of the Range on theEntity
, this requires that {X} exists on theEntity
. -
Delta{X}Minimum(value), Delta{X}Maximum(value), Delta{X}Range(value)
: Adds the given value to the Minimum, Maximum or transposes the entire range on theEntity
. This requires that{X}
exists on theEntity
.
-
-
OneLink<>, ManyLink<>
: Represents a relationship between twoEntities
and needs to be annotated with[LinkedTo]
which points to the other side of the relationship:-
LinkTo{X}(Entity)
: Links the parameterEntity
to thisEntity
by adding the{X}
component to thisEntity
with the givenEntity
. TheRelationshipManager
then handles the other side of the relationship. -
IsLinkedTo{X}(Entity)
: Returns whether the parameterEntity
is linked to thisEntity
through{X}
. -
DetachFrom{X}(), DetachFrom{X}(Entity)
: Will remove the link from thisEntity
, accepting a parameterEntity
from which to detach if this is aManyLink<>
.
-
-
StaticTag
: Represents the association of a single Entity to a Type.-
TagAs{X}()
: Tags theEntity
as{X}
and removes any previousEntity
associated with this tag. -
UnTagAs{X}()
: Removes the{X}
tag from theEntity
. -
IsTaggedAs{X}()
: Returns whether theEntity
is tagged as{X}
.
-
-
StaticGroup
: Represents the association of a group of Entities to a Type.-
Join{X}()
: Adds theEntity
to the{X}
group. -
Leave{X}()
: Removes theEntity
from the{X}
group. -
IsMemberOf{X}()
: Returns whether theEntity
is in the{X}
group.
-
-
Several custom attributes exist in the codegeneration framework which allow you to customize or enhance the code generated.
-
[Archetype]
: Annotate anypublic static readonly Matcher.Builder
field with the following will be generated:-
{X}Attribute: An attribute named after the field, which points at it, which can be used on any
Entity
type (parameter, etc) to indicate whatEntity
is expected. -
Create{X}: An extension factory on
Entity
that allows you to create anEntity
according to the Archetype.
-
{X}Attribute: An attribute named after the field, which points at it, which can be used on any
-
[Initializer]
: Annotate any method (not constructor) in anIComponent
and aEntity.Add{X}
method will be generated which accepts these parameters, delegating to that method to initialize the component. Care must be taken that this method properly resets and initializes anIComponent
due to the pooling strategy used on Components. Alternatively, you can inherit fromIPoolable
to define custom release logic when the component is returned to the pool.
To enable the pooling and quick lookup of Components, a specific Entity
type needs to be generated. By default, a GlobalWorldConfiguration
is generated that will use a specific Entity
that has optimized operations on Components
for all Component
types. To use this optimized World, just instantiate it using GlobalWorldConfiguration
. You can define WorldConfiguration
s for specific sets of Component if so required.
To hook into the lifecycle of pooling, you can inherit from IPoolable
. Any pooling code generated by the code generation will hook up the Component.
Generic Parameters aren't yet supported on components for which DSL is generated. Generics are fine on components which are abstract, or interface; i.e. those for which no DSL is generated.
The codegenerators in Artitas remove any common suffices (Component, Link, Links, Group, Tag) from the name of the Component types. Adding the suffices to the name of components gives benefits such as avoiding clashes with simple data types they might wrap or quickly filtering on groups of types.
The RangeGenerator
is a CodeGenerator that will generate Range Components for you.
Annotate any class inheriting from IComponent
with the RangeAttribute
and give it the names of the RangeComponents that need to be generated. The Range Components will now be generated into the namespace in which the class resides.
When it comes to renaming or deleting components, code may break and result in project state where code generation doesn't work anymore and require that changes be made manually. The following is a list of ways of what might and might not lead to problems, and solutions as to avoid these problems from occurring:
- Renaming fields: Safe. Using IDE refactoring will not lead to any compilation issues.
-
Changing type of a field: Unsafe, Easy. Apparently, no IDE refactoring is supported for "Type Migration", so you'll manually need to change the type, and where it is used. It might be easier to first remove the component from generation (using
[IgnoreForGeneration]
), comment out call sites, regenerate and update those call sites. - Renaming Components: Unsafe, Easy. Make sure that your IDE refactors existing methods in the rename.
- Removing/Adding new fields: Unsafe, Easy. This will update the AddXXX() DSL with new parameters and break wherever these methods are called.
- Moving a Component: Safe. Make sure you use the IDE namespace refactoring/move.
-
Deleting/Removing a Component: Unsafe, Tedious.
- Add the
[IgnoreForGeneration]
attribute to the Component class you want to remove and call Code Generation. This will remove any associated DSL and ID caching from the generated code. - Delete the remaining component usage from your codebase and fix usage where occurring.
- Add the
Call Generate a second time. The problem is that the Range Components themselves are generated in the first pass. The second pass picks up the generated components and then generates the DSL.