{{tag>dragonscript behavior}}
[[:start|Start Page]] >> [[main|DragonScript Scripting Language]] >> [[dragengine:modules:dragonscript:abstractions|Abstraction Layers: How you want to build your Game]] >> [[dragengine:modules:dragonscript:behavior_elements|Behavior Elements]] >> **ECBehaviorStateMachine**
* [[behaviors_use_cases|Behaviors Explained: By Use-Case]]
* [[behaviors_a_to_z|Behaviors Explained: From A to Z]]
====== ECBehaviorStateMachine ======
{{youtube>F0xMEw-vtdw?medium}}
Example usage of State machine Behavior. See [[https://github.com/LordOfDragons/deexamples/blob/master/exampleApp/data/scripts/StateMachineExampleClass.ds|StateMachineExampleClass.ds]] and [[https://github.com/LordOfDragons/deexamples/blob/master/exampleApp/data/content/stateMachines/colorizer.desm|colorizer.desm]]
Behavior element behavior adding StateMachine support.
Loads state machine from file (*.desm) and creates a #@LinkApiDocDEDS2_HTML~classDragengine_1_1StateMachineSystem_1_1SMContext.html,SMContext~@#. Other behaviors can add #@LinkApiDocDEDS2_HTML~interfaceDragengine_1_1StateMachineSystem_1_1SMAction.html,actions~@# and #@LinkApiDocDEDS2_HTML~interfaceDragengine_1_1StateMachineSystem_1_1SMCondition.html,conditions~@# to this state machine allowing to compose state machine actions and conditions easily.
You can assign #@LinkApiDocDEDS2_HTML~interfaceDragengine_1_1StateMachineSystem_1_1SMAction.html,actions~@# and #@LinkApiDocDEDS2_HTML~interfaceDragengine_1_1StateMachineSystem_1_1SMCondition.html,conditions~@# without using other behaviors by using one or more factories. The factories are added to the behavior. Upon creating the behavior instances the factories are asked to create and assign SMAction and SMCondition.
This behavior can be used in different ways depending on the needs.
//Single State Machine Usage//
This is the most simple solution where only one state machine is used. For many situations this is enough and is the most simple solution to use. Add one ECBehaviorStateMachine with no ID to the element and as many other behaviors adding actions and conditions as required. Set the ''stateMachine.path'' property to define the state machine to load. Then use behavior listeners to run events on the state machine at the appropriate time.
See [[#element_class_example|Element Class Example]] for example code.
//Multiple State Machine Usage//
You can add multiple instances of ECBehaviorStateMachine to the element to load and use multiple state machines. This can be for using multiple state machines together to model complex logic. In this situation behaviors assigining actions and conditions do add them to all instances of ECBehaviorStateMachine. This is usually not a problem since the state machines are written to use only the actions and conditions making sense to be used.
See [[#element_class_example|Element Class Example]] for example code.
====== Instance Counts ======
This behavior can be used multiple times on an element to add multiple custom colors to mainpulate. Use the behavior identifier to tell them apart.
====== Element Class Properties ======
Element class properties have the prefix ''stateMachine.'' or ''stateMachine({id}).'' if id is not empty.
===== path =====
Set path to state machine file to load.
* Full name: ''stateMachine.path'' or ''stateMachine({id}).path''
* Type: string
* Default Value: empty string
* Expected File Type: ''*.desm''
* Example (*.deeclass) actor.desm
===== debug =====
Debug mode prefix. If not empty string enables debug mode with prefix to locate log messages easily.
* Full name: ''stateMachine.debug'' or ''stateMachine({id}).debug''
* Type: string
* Default Value: empty string
* Example (*.deeclass) Debug-Actor-SM
===== Events =====
This behavior has no events.
====== State Machine Actions ======
This behavior adds these state machine actions.
===== element.set =====
Set owner element parameters.
^Parameter^Value^Description^
|visible|''true'', ''false''|Set if element is visible.|
|touchable|''true'', ''false''|Set if element is touchable.|
This is an example of using this action to fully hide the owner element:
false
false
===== element.update =====
Update owner element.
^Parameter^Value^Description^
|dispose| |Dispose of owner element. Make sure this is the last action run in a transition or state. Running actions afterwards can lead to problems.|
|teleport|''spawn'', ''coordsys''|Teleport owner element to target location. Supports these target types:
* ''spawn'': SpawnPoint instance.
* ''coordsys'': Conversation coordinate system managed by an [[behavior_convocoordsystem|ECBehaviorConvoCoordSystem]] instance.
If element is [[behavior_attachable|attachable]], detaches first if attached.|
|teleport.target|string|Identifier of target|
|log|string|Print a log message using Element.log() function.|
This is an example of using this action to dispose element:
This is an example of using this action to teleport the element to a spawn point:
spawn
portal1.exit
===== eventElement.update =====
Update behavior element resolved from state machine event parameter. If event parameter is null action has no effect. Supported are event parameters castable to these types:
* BehaviorElement
* CollisionInfo. Requires collider with owner set to BehaviorElement instance.
^Parameter^Value^Description^
|interactElement.assign|string|Assign event element to [[behavior_interactelement|ECBehaviorInteractElement]] with identifier matching value string.|
|interact|string|Run interaction with name value on event element.|
|interact.parameters|string|Optional parameters to use with ''interaction''.|
|runEvent|string|Run named event on all ECBehaviorStateMachine instance of event element with the state machine element as event parameter.|
This is an example of using this action:
do something
===== gameWorld =====
Runs action from game world behavior [[gwbehavior_btsm|GWBehaviorBTSM]]. Game world behaviors can add state machine actions to GWBehaviorBTSM similar to how element class behaviors can add state machine actions to this behavior. This allows state machines to access actions specific to a game world. The action is run as if it has been added to this behavior. All parameters defined in the action are passed to the game world action with the addition of the parameter ''element'' set to the behavior element owning the state machine. If the element owning this behavior is not in a game world or the action can not be found, nothing is done.
^Parameter^Value^Description^
|action|string|Name of action to run.|
|receiveEvents|''true'', ''false''|Enable receiving game world events|
|runEvent|string|Run event on all state machines of element set to receive game world events. The state machine owner is set as event parameter.|
This is an example of using this action to spawn a new element:
spawn
MyGameObjectClass
propertyName1:propertyValue1 propertyName2:propertyValue2
spawn point name
This is an example of using this action to run event on [[appbehavior_behaviorstatemachine|application state machines]]:
app.btsm.runEvent
fade over black
====== State Machine Conditions ======
This behavior adds these state machine conditions.
===== element.check =====
Evaluate owner element parameters.
^Parameter^Value^Description^
|element.visible|''true'', ''false''|Element is visible or not.|
|element.touchable|''true'', ''false''|Element is touchable or not.|
|element.inGameWorld|''true'', ''false''|Element is located in game world or not.|
|element.hasParent|''true'', ''false''|Element is parented to another element or not.|
This is an example of using this condition to check if the element is visible, touchable, in a game world and not parented to another element:
element.check
true
true
true
false
===== eventElement.check =====
Check one or more parameters of behavior element stored in state machine event parameter. If event parameter is null condition fails. Supported are event parameters castable to these types:
* BehaviorElement
* CollisionInfo. Requires collider with owner set to BehaviorElement instance.
^Parameter^Value^Description^
|eventElement.flags.set|string|Event element has [[behavior_flags|ECBehaviorFlags]] with named flag set.|
|eventElement.flags.clear|string|Event element has [[behavior_flags|ECBehaviorFlags]] with named flag cleared.|
|eventElement.interactElement.assigned|string|Event element is assigned to [[behavior_interactelement|ECBehaviorInteractElement]] with identifier matching value string.|
|eventElement.interactElement.assigned.not|string|Event element is not assigned to [[behavior_interactelement|ECBehaviorInteractElement]] with identifier matching value string.|
This is an example of using this action:
eventElement.check
can pick up
===== gameWorld =====
Evaluate condition from game world behavior [[gwbehavior_btsm|GWBehaviorBTSM]]. Game world behaviors can add state machine conditions to GWBehaviorBTSM similar to how element class behaviors can add state machine conditions to this behavior. This allows state machines to access conditions specific to a game world. The condition is evaluated as if it has been added to this behavior. All parameters defined in the condition are passed to the game world condition with the addition of the parameter ''gameWorld.element'' set to the behavior element owning the state machine. If the element owning this behavior is not in a game world or the condition can not be found, false is returned.
^Parameter^Value^Description^
|gameWorld.condition|string|Name of condition to evaluate.|
This is an example of using this condition to evaluate a game state:
gameWorld
winningCondition.check
10
====== State Machine Events ======
This behavior sends these state machine events.
===== init =====
State machine context has been created and the initial state has been set.
===== addToGameWorld =====
Element has been added to game world. ''gameWorld'' action/condition is now valid to be used.
===== removeFromGameWorld =====
Element has been removed from game world. ''gameWorld'' action/condition are no more valid to be used.
===== enterFrame =====
First frame update after element has been added to game world.
===== activate =====
Send by BAAStateMachine or BAAVRHandStateMachine if action has been activated.
===== activateWhileLoading =====
Send by BAAStateMachine or BAAVRHandStateMachine if action has been activated while loading a game state.
===== enteredGameWorld =====
Send by BAAStateMachine or BAAVRHandStateMachine if element has been added to game world while action is active. Same as ''addToGameWorld'' but send only if BAAStateMachine is used.
===== leftGameWorld =====
Send by BAAStateMachine or BAAVRHandStateMachine if element has been removed from game world while action is active. Same as ''removeFromGameWorld'' but send only if BAAStateMachine is used.
===== playerTakesControl =====
Send by BAAStateMachine or BAAVRHandStateMachine if player takes control of element.
===== playerReleasesControl =====
Send by BAAStateMachine or BAAVRHandStateMachine if player releases control of element.
===== think =====
Send by BAAStateMachine or BAAVRHandStateMachine on element think.
===== playerThink =====
Send by BAAStateMachine or BAAVRHandStateMachine on element think if player controls element.
===== postThink =====
Send by BAAStateMachine or BAAVRHandStateMachine on element post think.
===== playerPostThink =====
Send by BAAStateMachine or BAAVRHandStateMachine on element post think if player controls element.
===== cancel =====
Send by BAAStateMachine or BAAVRHandStateMachine if action has been cancelled.
===== collisionResponse =====
Send by BAAStateMachine or BAAVRHandStateMachine if [[behavior_colliderai|ECBehaviorColliderAI]] hit something. Use [[#eventelementupdate|eventParameter.update]] or [[#eventelementcheck|eventParameter.check]] to work with the hit element.
====== Behavior Tree Actions ======
This behavior adds these behavior tree actions if behavior tree is present. If behavior has non-empty identifier replace ''stateMachine'' with ''stateMachine(id)''.
===== stateMachine.set =====
Set state machine parameters.
^Parameter^Value^Description^
|reset| |Reset state machine to initial state.|
|state|string|Set state machine to named state.|
This is an example of using this action to switch state machine to state ''jump to state'':
jump to state
===== stateMachine.event =====
Run event on state machine.
^Parameter^Value^Description^
|id|string|Identifier of event to run.|
This is an example of using this action to run event ''do something'' on the current state machine state:
do something
===== stateMachine.check =====
Check one or more state machine parameters. Action succeeds if all parameter value matches their respective state machine parameter otherwise action fails. This action is typically used as first action in a sequence to run the sequence only if a state machine parameter matches (or not).
^Parameter^Value^Description^
|state|string|Current state machine state identifier matches value.|
|state.not|string|Current state machine state identifier does not match value.|
|state.starts|string|Current state machine state identifier begins with value.|
|state.starts.not|string|Current state machine state identifier does not begin with value.|
|state.ends|string|Current state machine state identifier ends with value.|
|state.ends.not|string|Current state machine state identifier does not end with value.|
|state.contains|string|Current state machine state identifier contains value.|
|state.contains.not|string|Current state machine state identifier does not contain value.|
|wait| |If present action returns BTResult.running instead of BTResult.failed to wait until the checks are all fulfilled|
This is an example of using this action to run a sequence only if state is ''ready state'':
ready state
====== Behavior Tree Conditions ======
This behavior adds these behavior tree conditions if behavior tree is present. If behavior has non-empty identifier replace ''stateMachine'' with ''stateMachine(id)''.
===== stateMachine.check =====
Check one or more state machine parameters. Conditions returns true if all parameter value match their respective state machine parameter. This condition is typically used to run an action or sequence of actions as long as state machine conditions are true.
^Parameter^Value^Description^
|stateMachine.state|string|Current state machine state identifier matches value.|
|stateMachine.state.not|string|Current state machine state identifier does not match value.|
|stateMachine.state.starts|string|Current state machine state identifier begins with value.|
|stateMachine.state.starts.not|string|Current state machine state identifier does not begin with value.|
|stateMachine.state.ends|string|Current state machine state identifier ends with value.|
|stateMachine.state.ends.not|string|Current state machine state identifier does not end with value.|
|stateMachine.state.contains|string|Current state machine state identifier contains value.|
|stateMachine.state.contains.not|string|Current state machine state identifier does not contain value.|
This is an example of using this condition to run an action only if state is ''ready state'':
stateMachine.check
ready state
====== Required Behaviors ======
This behavior requires no other behaviors.
====== Optional Behaviors ======
* [[behavior_behaviortree|ECBehaviorBehaviorTree]]: Add actions and conditions for behavior trees to use.
====== Persistency ======
This behavior does support element class to be persistable (setPersistable). Saves state machine content (#@LinkApiDocDEDS2_HTML~classDragengine_1_1StateMachineSystem_1_1SMContext.html,SMContext~@#).
====== API Documentation ======
#@LinkApiDocDEDS2_HTML~classDragengine_1_1Scenery_1_1ECBehaviorStateMachine.html,ECBehaviorStateMachine~@#.
Since DragonScript Module Version ''1.3''
====== Use Cases ======
* Create element logic without writing state machine using scripts.
* Reuse element logic across different elements.
* Split element logic into smaller logic units using multiple state machines (potentially reused).
====== Element Class Example ======
Example of the //Single State Machine Usage//.
class MyElementClass extends BehaviorElementClass
func new()
var ECBehaviorStateMachine smbehavior = ECBehaviorStateMachine.new(this)
smbehavior.getPath().setValue("/content/myBehavior.desm")
// here you can add now behaviors assigining actions and conditions for
// the state machine to use
...
// add listeners to behaviors to send events to the state machine
var ECBehaviorTwoStateAnimated animated = ...
animated.addActionConditionFactory(MyEventListener.Factory.new(smbehavior))
end
// two state listeners used to send events
class MyEventListener extends ECBehaviorTwoStateAnimated.DefaultListener
class Factory implements ECBehaviorTwoStateAnimated.ActionConditionFactory
public var ECBehaviorStateMachine stateMachine
func new(ECBehaviorStateMachine stateMachine)
this.stateMachine = stateMachine
end
func ECBehaviorTwoStateAnimated.Listener createListener(ECBehaviorTwoStateAnimated.Instance instance)
return MyEventListener.new(stateMachine.instance(instance.getElement()))
end
end
public var ECBehaviorStateMachine.Instance stateMachine
func new(ECBehaviorStateMachine.Instance stateMachine)
this.stateMachine = stateMachine
end
func void stopActivate(ECBehaviorTwoStateAnimated.Instance instance)
stateMachine.runEvent("animated activated")
end
func void stopDeactivate(ECBehaviorTwoStateAnimated.Instance instance)
stateMachine.runEvent("animated deactivated")
end
end
end
Example of the //Multiple State Machine Usage//.
class MyElementClass extends BehaviorElementClass
func new()
var ECBehaviorStateMachine smSpecific = ECBehaviorStateMachine.new(this, "specific")
smSpecific.getPath().setValue("/content/mySpecificBehavior.desm")
var ECBehaviorStateMachine smGeneric = ECBehaviorStateMachine.new(this, "idle")
smGeneric.getPath().setValue("/content/myGenericBehavior.desm")
// here you can add now behaviors assigining actions and conditions for both
// state machines to use
...
// add listeners to behaviors to send events to the state machine. the same
// MyEventListener is used as in the single state machine example. the example
// here uses two animated behaviors each sending events to one of the state
// machines. in general using multiple state machines the events should be
// disjoint but it is fully valid to send the same event to both if the
// state machines are written to deal with this properly.
var ECBehaviorTwoStateAnimated animated1 = ...
animated1.addActionConditionFactory(MyEventListener.Factory.new(smSpecific))
var ECBehaviorTwoStateAnimated animated2 = ...
animated2.addActionConditionFactory(MyEventListener.Factory.new(smGeneric))
end
end
The above example loads and runs two state machines. The first one is running specific state machine. The second state machine provides two separate state machines able to handle multiple logic at the same time. By writing the state machines properly the two state machienes do not interfere with each other and allow to split up state machines into multiple ones which can help create complex logic more simple and stable.
//Action and Condition Factory//
This example shows how to use a factory to add a condition checking for a trigger and an action manipulating a trigger.
class MyElementClass extends BehaviorElementClass
func new()
var ECBehaviorStateMachine smbehavior = ECBehaviorStateMachine.new(this)
smbehavior.getPath().setValue("/content/mySpecificBehavior.desm")
// add factory to create actions and conditions later on. this example uses
// a code block for simplicity. using classes implementing ActionConditionFactory
// is preferred since it allows to reuse factories across element classes.
smbehavior.setActionConditionFactory(block ECBehaviorStateMachine.Instance instance
// add condition evaluating to true if "player.insideZone" trigger is in fired state.
// the condition can be used in the state machine using the name "player is inside zone".
instance.setCondition("player is inside zone", SMConditionTrigger.new(\
"player.insideZone", SMConditionTrigger.TestMode.fired))
// add action pulsing (fire and immediately reset) "player.damage" trigger.
// the action can be run in the state machine using the name "hurt playe".
instance.setAction("hurt player", SMActionTrigger.new("player.damage", SMActionTrigger.Action.pulse))
end)
end
end
====== Behavior Factory ======
Using element class supporting adding behaviors the behavior can be added like this:
actor.desm
====== Live Examples ======
* [[https://github.com/LordOfDragons/deexamples|DEExamples Repository]]: Element class changing color using a state machine receiving trigger events.
* [[https://github.com/LordOfDragons/deexamples/blob/master/exampleApp/data/scripts/StateMachineExampleClass.ds|StateMachineExampleClass.ds]]
* [[https://github.com/LordOfDragons/deexamples/blob/master/exampleApp/data/content/stateMachines/colorizer.desm|colorizer.desm]]