States System

From HEWIKI
Jump to: navigation, search

Contents

See also: Stateful Object Spec Oracle

The States System works with the $STATES system node to provide centralized knowledge of the current state of the dynamic objects in an area, such as doors that are opened or closed, levers that have been pulled or not, and other stateful objects.

Overview

HeroEngine allows for the creation of extremely complex environments and game objects that are capable of dynamic changes, which can be triggered by game events or player actions. Each of these changes modifies the "state" of an area. The States System communicates an area's current state, and the $STATES system node provides for a centralized knowledge of an area's state, as well as the actions (code) that process when a state transitions from one value to another.

The power of the states system is that it allows game objects to communicate with each other without needing to know how an object does what it does. As more of the game systems are tied into the States System, more complex interactions are possible. Furthermore, once your technical team has created the appropriate states for your game design, state setup can be embedded as commands in the Asset Library so that World Builders can simply drop actions in and use simple point-n-click operations to "wire" the stateful actions of various objects together.

States can be either abstract in nature, meaning they lack a physical representation in the game, or they may be concrete game objects which are known as "Stateful" objects. States and StatefulObjects may also trigger state changes in other states and stateful objects.

StatefulObjectsLeverAndDoor.jpg

For example:

A (stateful) lever may be wired to trigger the activation of a door. The (stateful) door may then transform its rotation from a closed to opened position, unblock a pathfinding node, and play a sound of metal scraping across stone.

All of which is accomplished without requiring the lever know how the door actually works. The lever merely set the state for the door from "closed" to "opened".

Components

The states system is composed of these major components:

What problem(s) does this solve?

What problem(s) does this not solve?

Concepts

States have Discrete Values

A state has a current value which is the absolute for the state at a particular moment in time. That is to say, states have one and only one value at any time.

States Reside In One Local GOM

States are nodes that reside in the Local GOM for the area in which they are created. In other words, each area knows only about the states created in that area.

Elements of a State

States are composed of three major components:

Values

A state has both a default value and a current value, and other possible valid values.

When first created, any state has two values: NOTSET and * (wildcard).

The NOTSET value is a special one used when the state object initializes during area spinup and player logon. It allows for transitions from a noninitialized state (notset) to a default value. It will be rare that you would have reason to explicitly set a state to NOTSET.

The *(wildcard) value is used to create a transition where it should always happen when transitions to (or from depending where you place the wildcard) a particular value. Note that the wild card does not mean match on any value, rather it allows for a definition where you do not care what one of the values is.

Action Lists

Action lists define a set of actions that should occur when the action list is "played" (the result of a transition occurring). An action list may be used by 0...N transitions, though an action list that is used by no transitions will never execute in the normal course of the state system's operation.

Each action list is composed of any number of "state actions". By default, the states system includes the following actions:

You may extend the state system to include new, custom state actions for your own needs.

Transitions

Transitions define a fromValue, toValue and an action list to execute. Whenever a state is set to a new value, the states system checks for three potential transitions and executes any that are found. The transitions checked are:

For each of those transitions, if there is an action list defined it will be "played" executing each of its state actions.

Objects of the State System are Assigned Unique Keys

While the states system is capable of using strings for all of its operations, internally all objects created by the states system are assigned unique IDs, which are used whenever one object refers to another. This capability is used to solve the problem of renaming objects, it also allows for the IDs to be used in script rather than hard coding a string value that could be later be changed to display a different string.

All of which means that while you may have script code that sets the value of some state by string, it is safer and more resistant to later changes to use the unique key assigned to a given string value.

The Area States System is a Concrete Implementation

The clean engine area states system is a concrete implementation of the states system. The system was designed as abstract classes, allowing you to freely use them to create other types of stateful systems leveraging all of the generic code and extending/overriding it as desired.

For most things, the concrete implementation included in clean engine (area states) should be more than adequate but the capability to extend it is there should it be needed.

Usage

The creation of a new state is achieved through a series of steps. It is possible to navigate those steps either with a GUI, in HSL, or via templates created using the stateful object spec system. Creation of states using the stateful object system is somewhat specialized and will not be included in the general usage, instead it will be in its own section.

Invoking the State Editor GUI

Invoking the Area States Editor

The States Editor may be invoked using the Hotspot menu (control-->shift-->left-click in the top left corner of the viewport), and it is found under the menu option in the Tools Tab (left). If other types of state systems have been created using the abstract classes of the States System, it will be necessary to add new menu options to invoke their editors.

Invoking the Area States Editor

Additionally, objects created by the stateful object spec system have a right-click menu option available (while in "edit mode") for editing their state (right).


Create a New State

States may be created:

Create: State Editor GUI

Create a State Using the GUI

Clicking the "Add" button on the states system editor will cause a new state object to be created assigning a default name to it. New states automatically have the NOTSET and * values added to their values list.

The name assigned to the state may be changed by doubleclicking the new state and using the Properties Tab of the State Editor GUI to change the _StateDisplayName field. It is considered a "best practice" to make the name for your state unique for a given area, but the state system functions internally on unique IDs to it is not a requirement.


Create: HSL

Creation of a new state using HSL requires you find the "StateObjects" node for a particular system and calling the create method on it. The StateObjects node acts as a collection of States for a given states system like the area states. In the case of name collisions, the states system automatically makes the states name unique by adding an incremented integer to the specified name.

Information on adding new states systems can be found in the advanced usage section.

// Find the stateObjects node, clean engine currently has one states system _areaStates
//
areaStates as NodeRef of Class _StateObjects = $STATES._GetStatesNodeByClass( "_areaStates")
 
// Specify a name for the new state
name as String = "Foo"
 
// Create the AreaState node
var state = areaStates._CreateStateObject( name )

Add State Values

States may only be set to values that have been added to the state as valid ones. IE You may not set a state to "bob" unless bob has previously been added as a potential value. State values should express discrete concepts such as a door is OPEN or CLOSED or a monster spawner is ACTIVE, TRIGGERED or INACTIVE. Restricting your values to discrete concepts in this manner ensures that transitions make conceptual sense and helps clearly express what happens when a state transitions from one value to the next.

Add Value: State Editor GUI

Create New State Value

A new state value may be added by opening the state editor for a particular state (double clicking on the state in the states editor gui), swapping to the Values Tab and clicking the Add button (right). This will cause the states system to add a new value to the state with a default name.

Edit Default Value Name

Double clicking (or clicking the edit button) on the new value invokes a node property editor for the state value allowing the name of the value to be updated to a more appropriate one (left).


Add Value: HSL

Assuming you have a reference to a state, creating a new value requires a two lines of code.

// Retrieve the values collection node
var values = state._GetStateValidValuesRoot()
 
// Add a new value by string
values._CreateStateValidValueForValue( "OPENED" )

Add Action Lists

Action lists act as a collection of actions to "play" when a transition occurs. A given action list may be used by 0...N transitions. However, an action list that is used by no transitions will never execute its actions through the normal processes of the states system.

Add Action List: States Editor GUI

Create New Action List

Using the states editor, adding a new action list requires but a single click on the Add button located on the ActionLists Tab of the GUI. The new action list will be assigned a default name which you may change by editing the action list (either by double-clicking it or by selecting it and hitting the edit button).


Add Action List: HSL

Action lists are organized by the ActionListsRoot node, assuming you have a reference to the state you may create a new action list by retrieving the root node and calling a method (which functions as a Singleton).

// Assuming you have a reference to the state
var actionLists = state._GetStateActionListsRoot()
 
// Create the actionList, the method is implements the Singleton Pattern
var barList = actionLists._GetStateActionListByName( "bar" )

Add Actions

State actions represent well defined behaviors that utilize some amount of parameterized data to perform their tasks. For example, one type of state action is TRANSFORM which has the ability to transform the position/rotation/scale of a specified node. Most state actions have a "location" in which they are to be executed, this refers to the local GOM which will be affected (client, server or both client and server).

State actions include logic to perform their actions appropriately when a player enters the area to sync up that player's view of the world without rerunning code on the server. The practical implication of this is that state actions will not attempt to remodify a field (SETFIELD action) or recall a script (CALLSCRIPT) on the server when a player logs in, but they will perform those actions if they are set to be performed on the client. The reasoning for this behavior is that the actions already performed their duties on the server so it would not be appropriate to reexecute them there, whereas actions that need to be performed on the client need to be performed when a player logs in to synchronize them with how other players in the area see things.


State Action Types
CALLSCRIPT 
The callscript action calls a specified script at a shared function _OnDoStateAction in the specified local GOMs (client, server or client and server). Arbitrary data is passed in a string field _StateActionData and the script to be called is stored in the string field _StateActionToScript.</td>
PLAYFX 
Plays the specified FX (created using the clean engine _fxSpecOracle system) stored in the field _stateActionPlayFxSpecKey , with targets and caster set as specified in the state action's _stateActionPlayFxTarget and _stateActionPlayFxCaster fields.</td>
PLAYSOUND 
Plays the specified sound targeting the position of the target node with the sound's size indicated by the soundSize enumeration.
SETFIELD 
Sets the field on the specified node located in the specified local GOMs (client, server or client and server) to a value. This action only works for scalar fields (id, int, float, vector3, string, noderef, and boolean) that can be autoconverted to/from string.</td>
SETPROPERTY 
Sets a property on the specified node located in the local client GOM.
SETSTATE 
Sets the current value of a state to another specified value. Internally this action stores unique state object keys for the state and values so that subsequent name changes do not cause problems. The GUI automatically translates these keys into nice human readable strings.
TRANSFORM 
Transforms the target node's position/rotation/scale in the local client GOM by offsets (from the saved server value). This results in the local view of an object's position/rotation/scale changing. To return the object to its original value you simply provide offsets of (0,0,0).
UNMARSHAL 
Unmarshals a string marshaled using the marshalUtils:MarshalNodeWithClass functions to the specified target node in the specified location (client, server or both client and server local GOMs). The marshal string must be in the format expected by the marshalUtils utility script for this state action to function. Primarily useful in initializing a node located in a local Client GOM with classes and fieldValues dicated by the server.

Add Actions: Action List Editor GUI

Add a New State Action

Using the new state action chooser, select the type of state action you would like to have added to the action list. The states system will create a state action object of the appropriate type with its parameters initialized some whatever makes sense. The action list will update itself displaying the added state action which you may now double-click or select its row and click the editor button to open a node property editor for the state action.


CallScript

Add a Call Script State Action

The CallScript state action will call a script specified at a shared function _OnDoStateAction passing in the state action node (or a copy of the node in the cast of scripts called in a local GOM other than the area server's). The state action node supports the inclusion of arbitrary string data in a field _StateActionData, additional behaviors and data could be GLOMmed on should a system require additional functionality beyond what is possible calling an arbitrary script.



PlayFx

Add a Play FX State Action

The PlayFx state action plays a special effect created using clean engine system _FxSystem using the target and caster specified by the state action. In practice, this is only useful for FXs whose targets are static.

For example you might use this type action to execute an FX that has dust and particulate matter drifting to the ground in conjunction with the "opening" of an ancient door. Since the door is a static object (ie is always present in the area's local GOM) it is possible to set the door's ID as the caster/target for the FX.


PlaySound

Add a Play Sound State Action

The PlaySound state action will attempt to set the specified sound located at a given target node. There is no capability to error check this the sound specified actually exists, so when debugging problems with this state action double check the path/sgt specified is correct..


SetProperty

Add a SetProperty State Action

The SetProperty state action will attempt to set the specified property for a given target node to the specified value. This state action relies on autoconversion, as such the value string must be in an appropriate format for the target property. There is no capability to error check this value, so when debugging problems with this state action double check the format of your value string.


SetField

Add a Set Field State Action

The SetField state action sets the specified field on the target node to a specified value. This state action relies on autoconversion, consequently you may only use this state action for the scalar fields that are handled by autoconversion.

The node's field may be set in one or more local GOMs as specified by the _stateactionexecutionlocation, supporting setting the field in the client, server, or client and server local GOMs.


SetState

Add a Set State State Action

The SetState state action sets the specified state to the specified value for that state. This capability is the major feature facilitating the loosely coupled interaction of systems and items using the states system. Under the hood, the string values displayed by the GUI are actually unique _stateObjectKeys allowing for states or their values to be renamed without affecting functionality of the system.


Transform

Add a Transform State Action

The Transform state action will transform the position/rotation/scale of a specified target node by offsets from the server's saved value for those fields. This state action is useful for "animating" objects such as a door that opens by rotating -45 on its X axis. Transformations are performed in the local Client GOM.

Since transformations are performed as offsets from a server position, setting the offsets to (0,0,0) returns the transformed node to the state the server has as its "normal/saved" state.

The reverseable flag causes the transformation to do a reverse transition as soon as the transformation target value is reached, returning the object to its original values.


UnmarshalData

Add a Unmarshal State Action

The UnmarshalData state action unmarshals a string created using the MarshalUtils functions (MarshalNodeWithClass and MarshalNodeWithClassSpecifiedFields). The state action will unmarshal the string stored in its _stateActionData string using the MarshalUtils script's unmarshal functions, GLOMming on classes as necessary (provided they exist in the target local GOM).

As a result of the data being assembled as XML, it is problematic to create one of these state actions by hand. It is however, reasonable to tweak the string once it is created by altering the _stateActionData. Care must be taken to avoid disturbing the structure of the XML string.

Aside.gif It is possible as we refine our reflection solution for server-client nodes that this state action will be for most situations obselete.



This state action is typically used to initialized a node located in the local Client GOM, adding classes and field values to the node when a player enters the area.


Add Actions: HSL

There are different methods for creating a new state action, each with a signature that specifies the parameters it needs for the action to function.

CallScript

The callscript action calls a specified script at a shared function _OnDoStateAction in the specified local GOMs (client, server or client and server). Arbitrary data is passed in a string field _StateActionData and the script to be called is stored in the string field _StateActionToScript.

// Assuming you have a reference to an action list
//
// unique method _StatesObjectCreateStateActionCallScript( statesObjectClass as String, scriptToCall as String, 
//    data as String, location as Enum _StateActionExecutionLocations ) as NodeRef of Class _StateAction
//
  var callScript = $STATES._StatesObjectCreateStateActionCallScript( "_areaStates", somScript, Data, CLIENT )
  someActionList._AddStateActionToActionList( callScript )

PlayFX

Plays the specified FX (created using the clean engine _fxSpecOracle system) stored in the field _stateActionPlayFxSpecKey , with targets and caster set as specified in the state action's _stateActionPlayFxTarget and _stateActionPlayFxCaster fields.

For example you might use this type action to execute an FX that has dust and particulate matter drifting to the ground in conjunction with the "opening" of an ancient door. Since the door is a static object (ie is always present in the area's local GOM) it is possible to set the door's ID as the caster/target for the FX.

// Assuming you have a reference to someActionList
//
// unique method _StatesObjectCreateStateActionPlayFx( statesObjectClass as String, 
//   fxKey as id, caster as _target, target as _target, location as 
//   Enum _StateActionExecutionLocations ) as NodeRef of Class _StateAction
//
  var PlayFx = $STATES._StatesObjectCreateStatePlayFx( "_areaStates", fxKey, caster, target )
  someActionList._AddStateActionToActionList( PlayFx )

PlaySound

Plays the specified sound segment located at the position of the target node.

For example you might use this type action to play a sound of a creaking door in conjunction with the "opening" of an ancient door. Since the door is a static object (ie is always present in the area's local GOM) it is possible to set the door's ID as the target of the sound

// Assuming you have a reference to someActionList
//
// unique method _StatesObjectCreateStateActionPlaySound( statesObjectClass as String, 
//   targetID as ID, sound as String, soundSize as enum soundSizes, location as 
//   Enum _StateActionExecutionLocations ) as NodeRef of Class _StateAction
//
  var PlaySound = $STATES._StatesObjectCreateStatePlaySound( "_areaStates", targetID, sound, soundSize )
  someActionList._AddStateActionToActionList( PlaySound )

SetField

Sets the field on the specified node located in the specified local GOMs (client, server or client and server) to a value. This action only works for scalar fields (id, int, float, vector3, string, noderef, and boolean) that can be autoconverted to/from string.

// Assuming you have a reference to someActionList
//
// unique method _StatesObjectCreateStateActionSetField( statesObjectClass as String, 
//   targetID as ID, fieldName as String, data as String, 
//   location as Enum _StateActionExecutionLocations ) as NodeRef of Class _StateAction
//
  var setField = $STATES._StatesObjectCreateStateSetField( "_areaStates", targetNodeID, "someField", "someValueAsString", SERVER )
  someActionList._AddStateActionToActionList( setField)

SetProperty

Sets a property for the specified node located in the local client GOM to a specified value. The property specified must be of a type for which autoconversion from string works.

This action is only valid in the CLIENT Local GOM so there is no opportunity to pass the execution location through the signature.

// Assuming you have a reference to someActionList
//
// unique method _StatesObjectCreateStateActionSetProperty( statesObjectClass 
//   as String, targetID as ID, PropertyName as String, data as String ) 
//   as NodeRef of Class _StateAction
//
  var setProperty = $STATES._StatesObjectCreateStateSetField( "_areaStates", targetNodeID, "someProperty", "someValueAsString" )
  someActionList._AddStateActionToActionList( setProperty )

SetState

Sets the current value of a state to another specified value. Internally this action stores unique state object keys for the state and values so that subsequent name changes do not cause problems. The GUI automatically translates these keys into nice human readable strings.

// Assuming you have a reference to someActionList
//
// unique method _StatesObjectCreateStateActionSetState( statesObjectClass as String, 
//   stateKey as ID, stateValueKey as ID ) as NodeRef of Class _StateAction
//
  var setState = $STATES._StatesObjectCreateStateActionSetState( stateKey, stateValueKey )
  someActionList._AddStateActionToActionList( setState )

Transform

Transforms the target node's position/rotation/scale in the local client GOM by offsets (from the saved server value). This results in the local view of an object's position/rotation/scale changing. To return the object to its original value you simply provide offsets of (0,0,0).

// assuming you have a reference to the openActionList, this transform would rotate the node "theNode"
//   by 45 degrees over 5 seconds
//
// unique method _StatesObjectCreateStateActionTransform( statesObjectClass as String, target as NodeRef, 
//    positionOffset as Vector3, rotationOffset as Vector3, scaleOffset as Vector3, duration as TimeInterval, 
//    location as Enum _StateActionExecutionLocations ) as NodeRef of Class _StateAction
//
  var openTransForm = $STATES._StatesObjectCreateStateActionTransform( "_areaStates", theNode, "0,0,0", "-45,0,0", "0,0,0", 0:00:05.000, CLIENT )
  openActionList._AddStateActionToActionList( openTransForm )

Unmarshal

Unmarshals the marshal string of a node marshalled with the marshalUtils script onto a target node, GLOMming on classes as appropriate to the target node to hold the data from the original node (assuming the target GOM has those classes defined).

Aside.gif It is possible as we refine our reflection solution for server-client nodes that this state action will be for most situations obselete.



This state action is typically used to initialized a node located in the local Client GOM, adding classes and field values to the node when a player enters the area.


// Assuming you have a reference to someActionList
//
// unique method _StatesObjectCreateStateActionUnmarshalData( statesObjectClass as String, targetID as ID, 
//   data as String, location as Enum _StateActionExecutionLocations ) as NodeRef of Class _StateAction
//
  var unmarshalAction = $STATES._StatesObjectCreateStateActionUnmarshalData( "_areaStates", targetID, marshalledString, CLIENT )
  someActionList._AddStateActionToActionList( unmarshalAction )

Add Transitions

Transitions define a fromValue, toValue and an action list to execute. Whenever a state is set to a new value, the states system checks for three potential transitions and executes any that are found. The transitions checked are currentValue --> *(wildcard), currentValue --> newValue, and *(wildcard) --> newValue. For each of those transitions, if there is an action list defined it will be "played" executing each of its state actions.

When an area is spun up, or a player logs into an area, the NOTSET-->DefaultValue and DefaultValue-->CurrentValue(if default is different from the current) transitions play. This allows for the construction of initialization transitions using the NOTSET value.

Add Transitions: State Editor GUI

Add New Transition

Adding a new transition requires a single click on the Add button located under the Transitions Tab of the State Editor. The states system will add a new transition with some default values to the transitions list. Double-clicking the new transition in the transitions list or selecting it and hitting the Edit button invokes the transition editor which may be used to set the default values to those defining a particular transition.

Edit Defaults


Add Transitions: HSL

Creating a transition via HSL requires only a couple method calls assuming you have a reference to the state itself.

// Assuming you have a reference to the state already, and that state has the values NOTSET, OPENED
//   and an actionlist named OPENING defined
//
 
// Get the transitions root for the state
var transitions = state._GetStateTransitionsRoot()
 
// Add a Transition
var trans = transitions._CreateStateTransitionByValues( "NOTSET", "OPENED", "OPENING" )

Set a State's Default Value

A states default value is important because it defines the starting point for a state during area spinup or player logon, during these events the NOTSET-->Default Value transition runs (if defined). After the default, then the Default Value-->Current Value transition runs (if defined and the default/current values are different).

Set a State Default Value: State Editor GUI

Set State Default Value

Setting the default value for a state using the state editor GUI is accomplished using the dropdown box for the field _stateDefaultValueID located on the Properties Sheet of the editor. Once a default value is choosen, click the Ok button to submit the changes to the server.


Set a State Default Value: HSL

The state's default value may be set either by String or by the _stateObjectKey for a value. Regardless of which method you use, the value must be one that is "valid" for the state. That is to say, the value must already have been declared using the _CreateStateValidValueForValue method.

The preferred method for setting a state to a new value is using the _SetStateByValueKey which uses the unique object key assigned to the objects of the states system (such the values for a given state). Using the _statefulObjectKey avoids a host of problems associated with using strings.

Set Default Value by String

This method is used to set a state's default value to a new value based on a string. The value must already have been declared as a valid one for the state by _CreateStateValidValueForValue.

// Assuming you have a reference to the state
state._SetStateDefaultValueByValue( "CLOSED" )

Set Value by _statefulObjectKey

Aside.gif This is the preferred method for setting a default value. Using the object keys avoids a host of problems associated with renaming.



This method is used to set a state's default value to a new value using the unique object key generated for objects of the states system. The value must already have been declared as a valid one for the state by _CreateStateValidValueForValue.

// Assuming you have a reference to the state, and a valid _statefulObjectKey for some value
state._SetStateDefaultValueByValueKey( key )

Setting a State's Current Value

Once a state is set up, eventually you will want to set its current value to a new value. Setting a new state value can be done via GUI, HSL and a Right-Click Menu (stateful objects).

Setting a State's Current Value: States Editor GUI

Set Current State Value

Setting the current value for a state using the state editor gui is done using the dropdown box for the field _stateValueID located on the Propery Sheet of the editor GUI. After selecting the desired value, the Apply State Value button to apply the new value to the state.

Resultant to the application of the new value, (appropriate) state transitions will occur.


Setting a State's Current Value: HSL

The state's value may be set either by String or by the _stateObjectKey for a value. Regardless of which method you use, the value must be one that is "valid" for the state. That is to say, the value must already have been declared using the _CreateStateValidValueForValue method.

The preferred method for setting a state to a new value is using the _SetStateByValueKey which uses the unique object key assigned to the objects of the states system (such the values for a given state). Using the _statefulObjectKey avoids a host of problems associated with using strings.

Set Value by String

This method is used to set a state's current value to a new value based on a string. The value must already have been declared as a valid one for the state by _CreateStateValidValueForValue.

// Assuming you have a reference to the state
state._SetStateByValue( "CLOSED" )

Set Value by _statefulObjectKey

Aside.gif This is the preferred method for setting a state to a value. Using the object keys avoids a host of problems associated with renaming.



This method is used to set a state's current value to a new value using the unique object key generated for objects of the states system. The value must already have been declared as a valid one for the state by _CreateStateValidValueForValue.

// Assuming you have a reference to the state, and a valid _statefulObjectKey for some value
state._SetStateByValueKey( key )

Setting a State's Current Value: Right-Click Menu (Stateful Objects)

Set Current Value Via Right-Click Menu

Stateful objects have a built-in right-click menu for manipulating their states, this includes setting the current value of the state to one of the values declared as valid for the state.


Wiring States Together

The power of the states system lies in its ability to enable loosely coupled connections between systems and stateful objects. This loose coupling requires neither side of the equation to know or understand how the other works so long as they talk to each other by setting states from one value to another.

As your developers create new systems/objects using the states system, they can immediately leverage that loose coupling to connect old (stateful) systems to the new system/objects.

The act of wiring one state to another is nothing more than the addition of a SETSTATE state action to the action lists that occur when the first state goes to a particular value.

For example: Take our stateful lever example, it has two values ON and OFF. If I want the lever to open a door whenever it is set to ON, then I simply add a SETSTATE action to any actionlist that plays as a result of a transition from any Value-->ON that sets the door's state to OPEN. Likewise, if I want the door to close whenever the lever's state is set to a value of OFF, I add a SETSTATE action to any actionList that plays as a result of a transition from OFF-->any Value.

Wire States: Using the StatefulObject Right-Click Menu

Wire States Using Right-Click

Assuming you have a statefulObject or one that implements the appropriate context menu item shared functions for wiring states, wiring a stateful object to set another state's value is trivial. While in edit mode in HeroBlade, right-click on the statefulObject and use the menu that appears to wire the values of that objects state to set values for other states.

There is no right-click menu for states that exist without a physical (graphical) representation in the world.

Note: You must do this for each of the values of your statefulObject's state that you wish to trigger another state. That is to say, if you set ON to set some door's state to OPENED you must ALSO set the OFF to set the door's state to CLOSED (if you want it to function in that manner).


Wire States: State Editor GUI

This is identical to adding a SETSTATE action to the appropriate actionlists.

Wire States: HSL

As with using the GUI, wiring a state to set another state requires nothing more that the addition of a SETSTATE action to the state's action lists.

Advanced Usage

Data Structures of the States System

The Data Structures of the States System are implemented as abstract classes, allowing for the potential for multiple concrete implmentations. Clean Engine currently has only a single concrete implementation, Area States.

Data Structure

States System Node

The system node $STATES acts as the interface to the states system, like most system nodes implemented for the Clean Engine it allows for extension/overriding of certain methods by GLOMming a game-specific class onto the prototype (named STATES).

State Objects Collection

Each concrete implementation of the abstract system has a single state objects node (child of the _stateObjects class) that serves as the anchor for all states in that system. Each state objects node is anchored to the state root node which in turn is anchored to the area root node via a base hard association.

State Object

The state object is the fundamental unit for the states system. Each states object is made up of three node collections; transitions, values, and action lists. Action Lists in turn are node collections of actions.

Listening For State Value Changes

The State Object is a child of the ObsSubject class and passes events using those methods. When the state's current value changes, and event is raised using the eventObject class as the container for the event.

// me is the state object
eObj as NodeRef of Class EventObject = CreateNodeFromClass("EventObject")
eObj.eventType = "setstatevalue"
eObj.eventCausedByID = oldValueKey      // old value _stateObjectKey
eObj.eventAffectsID = me._StateValueID  // new value _stateObjectKey
 
me.setChanged()
me.NotifyListeners( eObj )

Listening to for this event simply requires the creation of a listener (child of the obsListener class) and adding it to the subject (in this case the state object node).

// Create a node listener that will receive the event notification
//
var listener = state.createNodeListener( notifyNodeID, false )
state.addListener( listener )

Adding a New States System

UnderConstruction.gif This is a very advanced topic which may be documented at some later date, should the need for such documentation be expressed.


The states system is an abstract system, for which the area states system is the concrete implementation. It is possible to make another concrete implementation, but it is very complex.

Currently, as there is no pressing need nor is there currently any plan by any licensee to use this capability, we are going to leave this undocumented.


Reference

The Stateful Object Spec Oracle

The stateful object spec oracle is used in conjunction with the Library's ability to run a /command during instantiation of objects from the library to create a state for those object(s).

Stateful Object Specification Oracle

Creation of a Sample State in HSL

// assume aDoor is a noderef to an asset that is a door we wish to make stateful
//
  statefulObject as noderef = aDoor
 
  areaStates as NodeRef of Class _StateObjects = $STATES._GetStatesNodeByClass( "_areaStates")
 
  areaStateName as String = "Sample"
 
  // Create the AreaState node
  var state = areaSTates._CreateStateObject( areaStateName )
 
  // GLOM on the stateful object class or one of its children, this supports right-click menus for wiring
  //   finding the object's state and editing the state
  GLOMClass("_statefulObject", statefulObject )
 
  // Change base_hard_association to link it to the statefulObject, allowing simultaneous deletes
  state._SwapStateBaseHardAssociationToTarget( statefulObject )
 
  var values = state._GetStateValidValuesRoot()
  // Set valid values to use for transitions
  values._CreateStateValidValueForValue( "OPENED" )
  values._CreateStateValidValueForValue( "CLOSED" )
 
  // default state, ie all doors start closed
  state._SetStateDefaultValueByValue( "CLOSED" )
 
  var actionLists = state._GetStateActionListsRoot()
  // Create the actionList  
  var openActionList = actionLists._GetStateActionListByName( "open" + areaStateName )
  var closeActionList = actionLists._GetStateActionListByName( "close" + areaStateName )
  var initOpenActionlist = actionLists._GetStateActionListByName( "initOpen" + areaStateName )
  var initClosedActionList = actionLists._GetStateActionListByName( "initClose" + areaStateName )
 
  var transitions = state._GetStateTransitionsRoot()
  // Add Transitions 
 
  var initOpen = transitions._CreateStateTransitionByValues( "NOTSET", "OPENED", "initOpen" + areaStateName )
  var open = transitions._CreateStateTransitionByValues( "CLOSED", "OPENED", "open" + areaStateName )
  var close = transitions._CreateStateTransitionByValues( "OPENED", "CLOSED", "close" + areaStateName )
  var initClose = transitions._CreateStateTransitionByValues( "NOTSET", "CLOSED", "initClose" + areaStateName )
 
  // Add actions to actionlists
  var openTransform = $STATES._StatesObjectCreateStateActionTransform( "_areaStates", switchID, "0,0,0", "-45,0,0", "0,0,0", 0:00:05.000, CLIENT )
  var closeTransForm = $STATES._StatesObjectCreateStateActionTransform( "_areaStates", switchID, "0,0,0", "0,0,0", "0,0,0", 0:00:05.000, CLIENT )
  closeActionList._AddStateActionToActionList( closeTransForm )
  initClosedActionList._AddStateActionToActionList( closeTransform )
  initOpenActionList._AddStateActionToActionList( openTransform )
  openActionList._AddStateActionToActionList( openTransform )
 
  // Initialize to the default value
  state._SetStateByValue( "CLOSED" )
Personal tools
Namespaces
Variants
Actions
Navigation
Toolbox