-
Notifications
You must be signed in to change notification settings - Fork 319
traits
Armory uses a trait (component) system to insert logic into Blender objects and make them interactive. Traits can be attached to scene objects or scenes itself. To inspect traits placed in the scene, switch to the Orphan Data
view in the Outliner
. The traits are displayed in the Collections
group as they are internally represented as such:
To add a trait to an object or a scene, navigate to Object Properties > Armory Traits
or Scene Properties > Armory Scene Traits
and click on the small +
icon right next to the empty list of traits. A dialog will open, asking you for the type of the newly created trait.
A common mistake is to forget adding a trait to an object or scene when a new logic node tree was created. A logic tree alone is not a trait, it needs to be referenced from a node trait to be used!
There are several trait types, serving different purposes:
-
Haxe
: writing scripts from scratch in Haxe, with full access to the Armory API. -
Wasm
: see WebAssembly -
UI
: working with canvas & user interface -
Bundled
: pre-made/bundled Haxe scripts to handle common stuff like character controllers (-> less boilerplate code for you to write). -
Nodes
: assembling logic visually in the Logic Node Editor (node reference).
If a trait is disabled, it is not included in the exported game by default to save space. This is problematic when you want to add the trait later in the game. To export the trait anyway, there is a Fake User
option that works in a similar fashion than the identically named option for Blender data types. If enabled, the trait is exported even if it is disabled:
So when using nodes or functions that require a trait name, make sure that the trait is exported!
Each trait exposes events to make it possible to get notified about its life-cycle. To listen to an event, create a function and pass it to one of the following functions so that it is called when the event occurs:
-
Trait.notifyOnAdd()
- trait is added to an object -
Trait.notifyOnInit()
- object which this trait belongs to is added to scene -
Trait.notifyOnRemove()
- object which this trait belongs to is removed from scene -
Trait.notifyOnUpdate()
- update game logic here -
Trait.notifyOnRender()
- update rendering here -
Trait.notifyOnRender2D()
- update 2D rendering here
As the scene is being built asynchronously, onInit
events can get called at a time when not all scene objects are present yet. If the trait construction depends on other scene objects, use the Scene.active.notifyOnInit()
event which gets called as soon as the scene is fully constructed.
The UI canvas of UI traits (CanvasScript
) is also loaded asynchronously and possibly unavailable at the time of the trait's instantiation. To reliably call functions of CanvasScript
traits, make sure to only call them from within a callback that is passed to CanvasScript.notifyOnReady()
:
canvas = Scene.active.getTrait(CanvasScript);
canvas.notifyOnReady(() -> {
// Here you can safely interact with the canvas
canvas.getElement("myElement").width = 200;
});
For scripts, it is possible to pass parameters or set script properties directly from Blender. This makes it easy to create a variation of objects that share the same trait while having different parameters for it.
Variables that should be visible in Blender have to be preceded with @prop
metadata in the source code.
Please note that only variables declared with the var
keyword are supported! Using final
does not work as the properties are set via Haxe's Reflection API at the start of the game after trait construction.
The following data types are supported:
Primitive/basic types | Iron object types | Iron float vectors |
---|---|---|
Int |
iron.object.Object |
iron.math.Vec2 |
Float |
iron.object.CameraObject |
iron.math.Vec3 |
Boolean |
iron.object.LightObject |
iron.math.Vec4 |
String |
iron.object.MeshObject |
|
iron.object.SpeakerObject |
package arm;
import iron.object.CameraObject;
import iron.math.Vec2;
import iron.math.Vec3;
import iron.math.Vec4;
class MyTrait extends iron.Trait {
// Primitive data types
@prop var intValue: Int = 40; // Type annotations are possible, but only required when no initial value is given.
@prop var floatValue = 3.14;
@prop var stringValue = "Hello world!";
@prop var booleanValue = true;
// Object data types
@prop var objValue: iron.object.Object; // Object types need type annotations to be recognized
@prop var camObjValue: CameraObject; // The type can be imported...
@prop var lightObjValue: iron.object.LightObject; // .. or not, both will work
@prop var meshObjValue: iron.object.MeshObject;
@prop var speakerObjValue: iron.object.SpeakerObject;
// Vector data types
@prop var vector2DValue: Vec2 = new Vec2(0.2, 0.5); // Initialization possible...
@prop var vector3DValue: Vec3; //... but not required
@prop var vector4DValue = new Vec4(1, 2, 3, 4);
// Not visible in Blender, `@prop` is missing
var notVisibleValue = 0.0;
// ...
}
This example results in the following image:
If Armory detects invalid @prop
declarations, warnings will get displayed:
- The property type must be supported (see Supported property types).
- The property must be syntactically correct.
- If the property has no type annotation and the type is infered from the given default value, the default value must be a constant literal or a constructor expression (using the
new
keyword). - Static properties are allowed but strongly discouraged. Multiple objects can write to the same static property, resulting in different results depending on the internal initialization order of the traits.
- Final or inline properties are not allowed.
By default all traits are created into your project's /Sources/arm
folder when you create them in Blender.
In general it's desirable to create a folder structure that matches logically your game division. For example you could decide to split your game in multiple scenes, and ideally keep the code of each Scene in separate subfolders (one per scene) so that it's easy to maintain later on.
In order to create a folder hierarchy tree in Armory, you can use the Haxe package syntax when assigning a name to your new trait. Therefore if you assign a name such as general.BoxBehavior
, a new trait will be created in the /Sources/arm/general
subfolder, with the name "BoxBehavior.hx".
You can create infinitely many nested subfolders this way by appending more dot-separated names to the trait name, such as "general.terrain.TerrainCollider" which will create a file named "TerrainCollider.hx" under the path /Sources/arm/general/terrain
.
If at some point you want to assign a different trait to your object, you simply need to select the trait from the list displayed when you click on the Class
dropdown, right below the Traits List.
Note: Please bear in mind that some reserved words are not allowed to be used as package names, therefore trait names such as "my.new.Trait" won't be valid due to new
being a reserved word.