-
Notifications
You must be signed in to change notification settings - Fork 40
Outline
- (DONE) About HSM
- (DONE) Benefits of HSM
- (DONE) License
-
(DONE) Installation
- Build Environment
- Configuration
- RTTI
-
(DONE) Tools
- plotHsm
-
(DONE) Introduction
- In this chapter, we'll learn the basics of how to use the HSM library to implement simple state machines.
- We'll only see flat state machines, not hierarchical just yet
-
(DONE) The Simplest State Machine
-
(DONE) States and Transitions
-
(DONE) Improving Readability
- nesting states in a struct
-
(DONE) State OnEnter/OnExit
- OnEnter/OnExit vs Constructor/Destructor
-
(DONE) ProcessStateTransitions
- stateMachine.ProcessStateTransitions() will call GetTransition on the current state and process the returned transition until there are no more state changes.
- example: call ProcessStateTransitions(), change a variable, call ProcessStateTransitions() again
- realtime (e.g. games): call ProcessStateTransitions() every frame (state of world may change each frame)
- event-based (e.g. UI): on event, change state and call ProcessStateTransitions()
- note: this function's job is to return a transition, not to execute state-specific logic. In other words, there should be no side-effects (variable changes) in running this function. Instead, use the Update function (covered next).
-
(DONE) UpdateStates
- right now, we know that a state can perform an action in OnEnter/OnExit
- sometimes you want your states to do something while they're the current state, say every frame in a game
- for this, there's UpdateStates which simply calls Update on the current state
- should be called right after ProcessStateTransitions so that only the current valid state runs its update logic
- example
- is only a convenience
- configurable parameter list
-
(DONE) Ownership
- StateMachine instance may optionally have an owner
- typically, owner is the instance of the class that aggregates the StateMachine instance
- States derive from hsm::StateWithOwner instead of hsm::State
- Advantage: access (public) members from Owner
- 'friend struct States' trick to access private members of Owner
-
(DONE) Storing Data
- As we saw, you can store data on the Owner. This data persists across state changes.
- You can also store data in states, which only lasts as long as the state does.
-
Summary
-
(DONE) Introduction
-
(DONE) Why Use Hierarchical State Machines?
-
(DONE) Drawing HSMs
-
(DONE) Inner and Outer States
-
(DONE) The State Stack
-
(DONE) InnerEntry Transitions
- example
-
(DONE) Inner Transitions
- example
- don't do it :)
-
(DONE) State Stack Queries
- IsInState, IsInInnerState, etc.
-
(DONE) ProcessStateTransitions Revisited
- the StateMachine class manages a stack of states
- this is what makes it hierarchical
- explicit: you can query the state stack
- implicit: state stack is modified only via transitions
- the algorithm
- the StateMachine class manages a stack of states
-
(DONE) UpdateStates Revisited
- calls Update on states from outermost to innermost
-
Summary
-
(DONE) Introduction
-
(DONE) StateValue
-
(DONE) StateArgs
-
(DONE) Storing Data on Cluster Root State
-
(DONE) Restarting states
- sibling transition to self
-
(DONE) Selector states
- good for choosing first state (stand vs falling)
- good for when sibling states can make sibling transitions to every other state with the same conditions - you "ping-pong" to the selector state
-
(DONE) Done States
-
(DONE) Share functions via base states
- Starting with StateWithOwner, but also for any set of states
-
(DONE) Deferred transitions
- storing Transitions to later return them in GetTransition
-
(DONE) Reusable states
- Made possible with StateArgs + Deferred transitions
-
(DONE) Sharing States
- Sharing states with unrelated owners (templated)
- Sharing states with related owners (StateWithOwner)
-
(DONE) Sharing State Machines (State Overrides)
-
(DONE) Parallel HSMs
- single HSM solves problem of being in many interdependent states
- but what about being in many independent, but related, states at the same time?
-
Summary
-
(DONE) Introduction
-
(DONE) Prefer InnerEntry + Sibling to Inner
-
(DONE) Scope data as "deeply" as possible: state, outer state, then owner
-
(DONE) Clean up in cluster's outermost state's OnExit
-
(DONE) Avoid state stack queries outside of states
- Prefer having states drive actions (via StateValue for e.g.) over using IsInState outside the state machine.
- Less coupled; easier to rename states; easier to reuse states.
-
(DONE) Make each state machine update count
- Do as much as possible within a single ProcessStateTransitions (within a single frame, rather than over multiple frames)
- Use transient 'Done' states instead of setting bools
- Do as much as possible within a single ProcessStateTransitions (within a single frame, rather than over multiple frames)
-
Prefer inner states over inheriting states
- talk about the two orthogonal axes: state stack vs inheritance chain. Confusing to mix both. Can be simpler to use the state stack: don't worry about forgetting to chain virtual overrides, for example.
- prefer to put reusable functions in inherited (base) state classes, but call these functions from the state class itself
-
Handle RAII using StateValue, RAII classes as state data members, or OnEnter/OnExit
- use enable/disable physics as example
- talk about ref-counting problem, that using StateValue can solve, if delaying the enable/disable is possible
- RAII classes are just useful to avoid having to add OnEnter/OnExit, exactly like how they can be used to avoid having to add constructor/destructor
-
Summary