-
Notifications
You must be signed in to change notification settings - Fork 0
States
A state is a configuration of an entity that can be activated on demand.
States can be thought of as a combination of logic (event-triggers, threads) and data (components, variables).
States are inherently self-contained, although side effects from entering a state may be exposed by its implementation.
For example, a state can opt to change a component in such a way that the modifications are persistent between state changes. Likewise, states also have the ability to alter global variables that in turn may impact the behavior of other states and routines. Developers utilizing states should take into consideration the possible side effects of a state before switching to it.
With that said, the concept behind a state is to be able to switch to it in order to conditionally change the behavior of an entity. This does have the implication that the state will behave consistently, although this may not always be the case in practice.
Imports allow for states to transitively include other states into the final entity. These imported states are commonly referred to as sub-states, and may be seen as sub-operations to be performed under specific conditions.
An import declaration may be performed using the top-level import field. This field behaves identically to an archetype's states field.
For details on how to declare imports, click here.
Alternative keys allowed: imports, state, states
A merge operation is a means of combining two or more states into one. This is similar to how archetypes are composed, as it allows for states themselves to be split into composite elements, giving greater flexibility in construction.
A merge operation can be performed using the state's top-level merge field.
The merge field can be either a string, object, or array value.
In the case of a string, the value is used as a path to the state with which to merge:
"merge": "path/to/composite_state"In the case of an object, the contents will be used in-line as the merging state:
"merge":
{
"rules":
{
"OnButtonPressed|Button::Jump":
"print": ["Jump button pressed"]
}
}In the case of an array, the contents will be enumerated, merging each string or object element accordingly:
"merge":
[
"path/to/some_state",
"path/to/some_other_state",
{
"delay": 3.5
}
]Merge operations differ from state imports, by allowing the combination of multiple state definitions into one activatable state.
Alternative keys allowed: merge_state, merge_states, using
Upon activation a state launches any top-level threads associated with it. These threads begin execution immediately, unless otherwise configured. Threads themselves may also spawn their own sub-threads.
Sub-threads usually do not have a tie to their originating thread, but they are inherently tied to the state that spawned their originating thread. This means that in the case of sub-threads, unless they are detached, the lifetime of the thread is still tied to the state that spawned it, implicitly terminating if the entity's state changes.
Threads can be defined using the top-level threads field of the state. This field can be either an array, string, or object.
For details on how to declare and define threads, please view the dedicated article on the subject.
Alternative keys allowed: do, execute
States allow for delayed activation through the top-level delay field. If specified, all non-critical operations that would take place on state-change will be delayed.
"delay": 10Delays completion of a transition to this state for 10 seconds.
Alternative keys allowed: timer, wait
...
States provide several features that allow the developer to manipulate an entity through the configuration of its components.
Mutates an entity in a way that persists indefinitely, regardless of the active state.
This feature can be invoked using the state's top-level modify field, which uses standard component syntax for modification:
"modify":
{
"NameComponent": "New Entity Name"
}Modifies components without guaranteeing preservation of previous member-values.
Please take note that since this is a component mutation, any unmodified data members are left as-is.
In the above example, this doesn't mean much, since we're updating the whole NameComponent, but if we were to modify a specific member of a component, like so:
"modify":
{
"ModelComponent":
{
"casts_shadow": false
}
}...then no other field would be affected.
As noted above, these changes will persist, even after the state's lifetime.
Alternative keys allowed: persist, share, shared, =
Defines local component instances to be encapsulated by the state.
This feature allows a state to own a completely isolated version of a component, whose lifetime is linked with the current activation of the state. The benefit of this is that any existing components are stored while the state is active, meaning all changes to these isolated instances are effectively temporary, decaying with the state they originated from.
This feature can be invoked using the state's top-level local field, which uses standard component syntax for initialization:
"local":
{
"NameComponent": "Local Instance of NameComponent"
}"local": ["VelocityComponent"]Alternative keys allowed: local_storage, isolate, isolated
Creates an isolated, state-local copy of one or more components. This functions similarly to local in that the component instances created are completely isolated from the components the entity had prior to state activation.
The main benefit of copy over local is that copy allows the developer to adopt a snapshot of a component at the point of state activation, rather than supplying completely new values.
Copy operations happen repeatedly, on each activation of the state.
This feature can be invoked using the state's top-level copy field, which uses standard component syntax for modification:
"copy": ["VelocityComponent"]"copy":
{
"PointLightComponent":
{
"linear": 0.005
}
}Alternative keys allowed: local_copy
Creates a divergent (persistently stored) copy of one or more components at the time of first state activation.
A cloned component has similar properties to local and copy in that its existence is contained within the state that created it. The way in which clone differs, however, is that a clone is created the first time a state is activated, tying its lifetime not to a particular activation of a state, but rather all activations of that state for that entity. This in turn turns a component into a semi-permanent store of data that can be referenced across state activations.
Local components have similar isolative properties, but may include modifications that overwrite existing component-state.
This feature can be invoked using the state's top-level clone field, which uses standard component syntax for modification:
"clone":
{
"TransformComponent": {},
"CameraComponent":
{
"far_plane": 400
}
}"clone": ["AnimationComponent"]Alternative keys allowed: init_copy, local_modify, copy_once
Adds one or more components to an entity for the duration of the state. The added components are implicitly removed upon state-change.
NOTE: If the entity had these components prior to this add operation, the components will still be implicitly removed upon state-change.
This feature can be invoked using the state's top-level add field, which uses standard component syntax for modification:
"add": "GravityComponent""add":
{
"GravityComponent":
{
"intensity": 0.25
}
}Please note that since this is a component mutation, any unmodified data members are left as-is.
This means that in the case of an existing GravityComponent, such as in the first example, no modification will take place. However, the decaying behavior of add does take place, meaning that even though we didn't modify the members of GravityComponent, it will still be removed at the end of the state's lifetime.
Additionally, if GravityComponent did not previously exist in these examples, it would be (default) initialized, then modified if applicable.
Alternative keys allowed: +
Removes a component from an entity during the activation of the state. The removed component will not be implicitly restored upon state-change. For temporary removal of a component, see exclude.
This feature can be invoked using the state's top-level remove field. Unlike add, this does not take advantage of standard component syntax, instead opting for component names as input.
These component names can be provided either as a string:
"remove": "ModelComponent"...or as an array of strings:
"remove": ["ModelComponent", "GravityComponent"]As noted above, the removed components are dropped completely, regardless of any further transitions to another state.
Alternative keys allowed: -
Captures a component, tying its lifetime with subsequent activations of the state.
An include operation works opposite to an exclude operation, in that rather than freezing a component while the state is active, an include operation captures a component instance, effectively transferring its ownership to the state.
Capture happens once, during the initial activation of the state.
While the included component is in use, any prior component instance is stored, similar to an exclude operation.
Please take note that since the nature of include is to capture an instance of a component, the entity may be left without the component when the state is inactive.
This feature can be invoked using the state's top-level include field.
Similar to exclude, this does not take advantage of standard component syntax, as no modification is performed. Instead, a list of component names is expected as input.
These component names can be provided either as a string:
"include": "NameComponent"...or as an array of strings:
"include": ["NameComponent", "AnimationComponent"]Alternative keys allowed: store, storage, temp, temporary, #
Similar to remove, exclude allows for components to be deactivated during the lifetime of a state.
The way in which exclude differs however, is that rather than outright detaching the component, exclude essentially freezes the existing component instance in a temporary store while the state is active, reinstating the component once the state deactivates.
This feature can be invoked using the state's top-level exclude field.
Similar to remove, this does not take advantage of standard component syntax, as no modification is performed. Instead, a list of component names is expected as input.
These component names can be provided either as a string:
"exclude": "GravityComponent"...or as an array of strings:
"exclude": ["GravityComponent", "VelocityComponent"]Alternative keys allowed: frozen, freeze, %, ^