Skip to content
Moritz Brückner edited this page Sep 14, 2022 · 15 revisions

Traits

Table of Content:

Introduction

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!

Trait Types

There are several trait types, serving different purposes:

Trait types in the UI

  • 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).

Fake User

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:

Trait fake user setting

So when using nodes or functions that require a trait name, make sure that the trait is exported!

Trait Events

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.

Canvas Trait Events

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;
});

Trait Properties

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.

Supported Property Types

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

Properties Example

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:

Trait properties

Warnings

If Armory detects invalid @prop declarations, warnings will get displayed:

Trait properties warnings

  • 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.

Folder Organisation

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.

Clone this wiki locally