User Tools

Site Tools


dragengine:modules:dragonscript:behavior_statemachine

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Next revision
Previous revision
dragengine:modules:dragonscript:behavior_statemachine [2020/09/13 15:59] – created dragonlorddragengine:modules:dragonscript:behavior_statemachine [2025/05/09 19:04] (current) dragonlord
Line 1: Line 1:
 {{tag>dragonscript behavior}} {{tag>dragonscript behavior}}
 <WRAP youarehere> <WRAP youarehere>
-[[:start|Start Page]] >> [[main|DragonScript Scripting Language]] >> [[abstractions#behavior_elementsquick_and_easy_development|Behavior Elements: Quick and Easy Development]] >> **ECBehaviorStateMachine**+[[: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**
 </WRAP> </WRAP>
  
Line 16: Line 16:
 </WRAP> </WRAP>
  
-Behavior element behavior adding [[https://developer.dragondreams.ch/docs/dragonscript/scriptapi/latest/classDragengine_1_1StateMachineSystem_1_1StateMachine.html|StateMachine]] support.+Behavior element behavior adding StateMachine support.
  
-Loads state machine from file (*.desm) and creates an [[https://developer.dragondreams.ch/docs/dragonscript/scriptapi/latest/classDragengine_1_1StateMachineSystem_1_1SMContext.html|SMContext]]. Other behaviors can add [[https://developer.dragondreams.ch/docs/dragonscript/scriptapi/latest/interfaceDragengine_1_1StateMachineSystem_1_1SMAction.html|actions]] and [[https://developer.dragondreams.ch/docs/dragonscript/scriptapi/latest/interfaceDragengine_1_1StateMachineSystem_1_1SMCondition.html|conditions]] to this state machine allowing to compose state machine actions and conditions easily.+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. This behavior can be used in different ways depending on the needs.
  
-Single state machine 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.+//Single State Machine Usage//
  
-This is an example code for such a setup: +This is the most simple solution where only one state machine is usedFor many situations this is enough and is the most simple solution to useAdd one ECBehaviorStateMachine with no ID to the element and as many other behaviors adding actions and conditions as requiredSet 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. 
-<code> + 
-class MyElementClass extends BehaviorElementClass +See [[#element_class_example|Element Class Example]] for example code
-   public func new() + 
-      var ECBehaviorStateMachine smbehavior = ECBehaviorStateMachine.new(this+//Multiple State Machine Usage// 
-      smbehavior.getPath().setValue("/content/myBehavior.desm") + 
-       +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. 
-      // here you can add now behaviors assigining actions and conditions for + 
-      // the state machine to use +See [[#element_class_example|Element Class Example]] for example code
-      ..+ 
-       +====== Instance Counts ====== 
-      // add listeners to behaviors to send events to the state machine +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. 
-      var ECBehaviorTwoStateAnimated animated ... + 
-      animated.addActionConditionFactory(MyEventListener.Factory.new(smbehavior)) +====== Element Class Properties ====== 
-   end +Element class properties have the prefix ''stateMachine.'' or ''stateMachine({id}).'' if id is not empty
-    + 
-   // two state listeners used to send events +===== path ===== 
-   class MyEventListener extends ECBehaviorTwoStateAnimated.DefaultListener +Set path to state machine file to load
-      class Factory implements ECBehaviorTwoStateAnimated.ActionConditionFactory +  * Full name: ''stateMachine.path'' or ''stateMachine({id}).path'' 
-         public var ECBehaviorStateMachine stateMachine +  * Type: string 
-          +  * Default Value: empty string 
-         public func new(ECBehaviorStateMachine stateMachine) +  * Expected File Type: ''*.desm'' 
-            this.stateMachine = stateMachine +  * Example (*.deeclass<code xml><string name='stateMachine.path'>actor.desm</string></code> 
-         end + 
-          +===== debug ===== 
-         public func ECBehaviorTwoStateAnimated.Listener createListener(ECBehaviorTwoStateAnimated.Instance instance) +Debug mode prefix. If not empty string enables debug mode with prefix to locate log messages easily. 
-            return MyEventListener.new(stateMachine.instance(instance.getElement())) +  * Full name: ''stateMachine.debug'' or ''stateMachine({id}).debug'' 
-         end +  * Type: string 
-     end +  * Default Value: empty string 
-      +  * Example (*.deeclass<code xml><string name='stateMachine.debug'>Debug-Actor-SM</string></code> 
-     public var ECBehaviorStateMachine.Instance stateMachine + 
-      +===== Events ===== 
-     public func new(ECBehaviorStateMachine.Instance stateMachine) + 
-        this.stateMachine = stateMachine +This behavior has no events
-     end + 
-      +====== Behavior Tree Actions ====== 
-     public func void stopActivate(ECBehaviorTwoStateAnimated.Instance instance) + 
-        stateMachine.runEvent("animated activated"+This behavior adds these behavior tree actions if behavior tree is presentIf behavior has non-empty identifier replace ''stateMachine'' with ''stateMachine(id)''. 
-     end + 
-      +===== stateMachine.set ===== 
-     public func void stopDeactivate(ECBehaviorTwoStateAnimated.Instance instance) + 
-        stateMachine.runEvent("animated deactivated"+Set one or more state machine parameters
-     end + 
-  end +^Parameter^Value^Description^ 
-end+|reset| |Set state machine root state as current state| 
 +|state|string|Set state machine state to state with identifier matching string value| 
 + 
 +This is an example of using this action: 
 +<code xml> 
 +<action name='stateMachine.set'> 
 +  <parameter name='state'>open</parameter> 
 +</action>
 </code> </code>
  
-**Action and Condition Factory**+===== stateMachine.event =====
  
-You can assign [[https://developer.dragondreams.ch/docs/dragonscript/scriptapi/latest/interfaceDragengine_1_1StateMachineSystem_1_1SMAction.html|actions]] and [[https://developer.dragondreams.ch/docs/dragonscript/scriptapi/latest/interfaceDragengine_1_1StateMachineSystem_1_1SMCondition.html|conditions]] without using other behaviors by using one or more [[https://developer.dragondreams.ch/docs/dragonscript/scriptapi/latest/interfaceDragengine_1_1Scenery_1_1ECBehaviorStateMachine_1_1ActionConditionFactory.html|Action/Condition Factories]]. The factories are added to the behavior. Upon creating the behavior instances the factories are asked to create and assign [[https://developer.dragondreams.ch/docs/dragonscript/scriptapi/latest/interfaceDragengine_1_1StateMachineSystem_1_1SMAction.html|SMAction]] and [[https://developer.dragondreams.ch/docs/dragonscript/scriptapi/latest/interfaceDragengine_1_1StateMachineSystem_1_1SMCondition.html|SMCondition]].+Run event on state machine.
  
-The example below shows how to use a factory to add a condition checking for a trigger and an action manipulating a trigger. +^Parameter^Value^Description^ 
-<code> +|id|string|Identifier of event to run| 
-class MyElementClass extends BehaviorElementClass + 
-   public func new() +This is an example of using this action: 
-      var ECBehaviorStateMachine smbehavior ECBehaviorStateMachine.new(this) +<code xml
-      smbehavior.getPath().setValue("/content/mySpecificBehavior.desm"+<action name='stateMachine.event'> 
-       +  <parameter name='id'>open door</parameter> 
-      // add factory to create actions and conditions later on. this example uses +</action>
-      // 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+
 </code> </code>
  
-====== Instance Counts ======+===== stateMachine.check =====
  
-You can add multiple instances of **ECBehaviorStateMachine** to the element to load and use multiple state machinesThis 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 problem since the state machines are written to use only the actions and conditions making sense to be used.+Check one or more state machine parametersAction succeeds if all parameter value matches their respective state machine parameter otherwise action fails. This action is typically used as first action in sequence to run the sequence only if a state machine parameter matches (or not).
  
-This is an example code for such a setup: +^Parameter^Value^Description^ 
-<code> +|state|string|Current state identifier matches string value| 
-class MyElementClass extends BehaviorElementClass +|state.not|string|Current state identifier does not match string value| 
-   public func new() +|state.starts|string|Current state identifier starts with string value| 
-      var ECBehaviorStateMachine smSpecific = ECBehaviorStateMachine.new(this, "specific") +|state.starts.not|string|Current state identifier does not start with string value| 
-      smSpecific.getPath().setValue("/content/mySpecificBehavior.desm") +|state.ends|string|Current state identifier ends with string value| 
-       +|state.ends.not|string|Current state identifier does not end with string value| 
-      var ECBehaviorStateMachine smGeneric = ECBehaviorStateMachine.new(this, "idle") +|state.contains|string|Current state identifier contains string value| 
-      smGeneric.getPath().setValue("/content/myGenericBehavior.desm") +|state.contains.not|string|Current state identifier does not contain string value| 
-       +|wait| |If present action returns BTResult.running instead of BTResult.failed to wait until the checks are all fulfilled| 
-      // here you can add now behaviors assigining actions and conditions for both + 
-      // state machines to use +This is an example of using this action: 
-      ..+<code xml> 
-       +<sequence> 
-      // add listeners to behaviors to send events to the state machine. the same +  <action name='stateMachine.check'> 
-      // MyEventListener is used as in the single state machine example. the example +    <parameter name='state'>open</parameter> 
-      // here uses two animated behaviors each sending events to one of the state +  </action> 
-      // machines. in general using multiple state machines the events should be +  <!-- actions here run only if state machine is in the "open" state --> 
-      // disjoint but it is fully valid to send the same event to both if the +</sequence>
-      // 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+
 </code> </code>
  
-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.+====== Behavior Tree Conditions ======
  
-====== Element Class Properties ======+This behavior adds these behavior tree conditions if behavior tree is present. If behavior has non-empty identifier replace ''stateMachine'' with ''stateMachine(id)''.
  
-Element class properties have the prefix **stateMachine.** or **stateMachine(id).** if id is not empty.+===== stateMachine.check =====
  
-===== path ===== +Check one or more state machine parametersConditions 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
-Path to state machine XML (*.desm) to load+ 
-  * Full name: "stateMachine.path" or "stateMachine(id).path" +^Parameter^Value^Description^ 
-  * Type: Path (*.desm) +|stateMachine.state|string|Current state identifier matches string value| 
-  * Default Value "" (empty string) +|stateMachine.state.not|string|Current state identifier does not match string value| 
-  * Example (*.deeclass) <code xml><string name='stateMachine.path'>/content/stateMachines/puzzleBox.desm</string></code>+|stateMachine.state.starts|string|Current state identifier starts with string value| 
 +|stateMachine.state.starts.not|string|Current state identifier does not start with string value| 
 +|stateMachine.state.ends|string|Current state identifier ends with string value| 
 +|stateMachine.state.ends.not|string|Current state identifier does not end with string value| 
 +|stateMachine.state.contains|string|Current state identifier contains string value| 
 +|stateMachine.state.contains.not|string|Current state identifier does not contain string value| 
 + 
 +This is an example of using this condition: 
 +<code xml> 
 +<action name='myAction' id='doing something'> 
 +  <parameter name='stateMachine.state'>open</parameter> 
 +  <condition>stateMachine.check</condition> 
 +</action> 
 +</code>
  
 ====== Required Behaviors ====== ====== Required Behaviors ======
  
-This behavior does not require other behaviors.+This behavior requires no other behaviors.
  
 ====== Optional Behaviors ====== ====== Optional Behaviors ======
  
-This behavior does not support optional behaviors.+  * [[behavior_behaviortree|ECBehaviorBehaviorTree]]: Add actions and conditions for behavior trees to use.
  
 ====== Persistency ====== ====== Persistency ======
  
-This behavior does support element class to be persistable (setPersistable). Saves state machine content ([[https://developer.dragondreams.ch/docs/dragonscript/scriptapi/latest/classDragengine_1_1StateMachineSystem_1_1SMContext.html|SMContext]]).+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 ====== ====== API Documentation ======
  
-[[https://developer.dragondreams.ch/docs/dragonscript/scriptapi/latest/classDragengine_1_1Scenery_1_1ECBehaviorStateMachine.html|ECBehaviorStateMachine]].+#@LinkApiDocDEDS2_HTML~classDragengine_1_1Scenery_1_1ECBehaviorStateMachine.html,ECBehaviorStateMachine~@#.
  
-Since DragonScript Module Version **1.3**+Since DragonScript Module Version ''1.3''
  
 ====== Use Cases ====== ====== Use Cases ======
Line 171: Line 172:
 ====== Element Class Example ====== ====== Element Class Example ======
  
-Element class using a state machine loaded from "myBehavior.desm".+Example of the //Single State Machine Usage//.
  
 <code> <code>
 class MyElementClass extends BehaviorElementClass class MyElementClass extends BehaviorElementClass
-   public func new() +  func new() 
-      var ECBehaviorStateMachine smbehavior = ECBehaviorStateMachine.new(this) +    var ECBehaviorStateMachine smbehavior = ECBehaviorStateMachine.new(this) 
-      smbehavior.getPath().setValue("/content/myBehavior.desm")+    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
              
-      // here you can add now behaviors assigining actions and conditions for +      func new(ECBehaviorStateMachine stateMachine)
-      // 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 +
-          +
-         public func new(ECBehaviorStateMachine stateMachine) +
-            this.stateMachine = stateMachine +
-         end +
-          +
-         public func ECBehaviorTwoStateAnimated.Listener createListener(ECBehaviorTwoStateAnimated.Instance instance) +
-            return MyEventListener.new(stateMachine.instance(instance.getElement())) +
-         end +
-     end +
-      +
-     public var ECBehaviorStateMachine.Instance stateMachine +
-      +
-     public func new(ECBehaviorStateMachine.Instance stateMachine)+
         this.stateMachine = stateMachine         this.stateMachine = stateMachine
-     end +      end 
-      +       
-     public func void stopActivate(ECBehaviorTwoStateAnimated.Instance instance) +      func ECBehaviorTwoStateAnimated.Listener createListener(ECBehaviorTwoStateAnimated.Instance instance) 
-        stateMachine.runEvent("animated activated"+        return MyEventListener.new(stateMachine.instance(instance.getElement())) 
-     end +      end 
-      +    end 
-     public func void stopDeactivate(ECBehaviorTwoStateAnimated.Instance instance) +     
-        stateMachine.runEvent("animated deactivated"+    public var ECBehaviorStateMachine.Instance stateMachine 
-     end+     
 +    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
 end end
 +</code>
 +
 +Example of the //Multiple State Machine Usage//.
 +
 +<code>
 +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
 +</code>
 +
 +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.
 +<code>
 +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
 +</code>
 +
 +====== Behavior Factory ======
 +
 +Using element class supporting adding behaviors the behavior can be added like this:
 +<code xml>
 +<?xml version='1.0' encoding='UTF-8'?>
 +<elementClass name='MyClass' class='GenericBehaviorElement'>
 +  <behavior type='ECBehaviorStateMachine'>
 +    <!-- set element properties. omit property prefix if used inside behavior tag -->
 +    <string name='.path'>actor.desm</string>
 +  </behavior>
 +  
 +  <!-- for adding multiple behaviors use unique identifiers -->
 +  <behavior type='ECBehaviorStateMachine' id='second'/>
 +</elementClass>
 </code> </code>
  
dragengine/modules/dragonscript/behavior_statemachine.1600012771.txt.gz · Last modified: 2020/09/13 15:59 by dragonlord