-
-
Notifications
You must be signed in to change notification settings - Fork 68
Legacy Transitions within Hierarchy
Transitions in hfsm.dev can be made from any state or region to any other identified by its type.
All external transitions are queued up and processed at the end of M::Root::update()
call.
The simplest form of transition does the following:
- Find common parent region for both origin and destination
- Deactivate origin branch
- Activate destination branch
using M = hfsm2::Machine;
using FSM = M::PeerRoot<
struct Origin,
struct Destination
>;
struct Origin : FSM::State {};
struct Destination : FSM::State {};
FSM::Instance fsm;
assert(fsm.isActive<Origin>());
fsm.changeTo<Destination>();
fsm.update();
assert(fsm.isActive<Destination>());
In addition to what transition into a state does, transition into a region also activates its sub-states.
The number and the order of activated sub-states depends on the types of transition and region.
using M = hfsm2::Machine;
using FSM = M::PeerRoot<
struct Origin,
M::Composite<struct Destination,
struct SubState1,
struct SubState2
>
>;
struct Origin : FSM::State {};
struct Destination : FSM::State {};
struct SubState1 : FSM::State {};
struct SubState2 : FSM::State {};
FSM::Instance fsm;
assert(fsm.isActive<Origin>());
fsm.changeTo<Destination>();
fsm.update();
assert(fsm.isActive<Destination>());
assert(fsm.isActive<SubState1>());
hfsm.dev allows transitions to be called both from the outside of the FSM, and from within the state of an FSM.
All available transitions are available on the FSM::Instance
object:
using M = hfsm2::Machine;
using FSM = M::PeerRoot<
struct Origin,
struct Destination
>;
struct Origin : FSM::State {};
struct Destination : FSM::State {};
FSM::Instance fsm;
assert(fsm.isActive<Origin>());
fsm.changeTo<Destination>(); // external transition
fsm.update();
assert(fsm.isActive<Destination>());
All available transitions are also available on the M::*Control
objects:
using M = hfsm2::Machine;
using FSM = M::PeerRoot<
struct Origin,
struct Destination
>;
struct Origin
: FSM::State
{
void update(FullControl& control) {
control.changeTo<Destination>(); // internal transition
}
};
struct Destination : FSM::State {};
FSM::Instance fsm;
assert(fsm.isActive<Origin>());
fsm.update();
assert(fsm.isActive<Destination>());
hfsm.dev supports 4 transition types:
-
changeTo()
- default transition restart()
resume()
utilize()
randomize()
And 3 region types:
Composite
Resumable
Utilitarian
Random
The way the default transition (changeTo<>()
) works depends on the region type:
region type | meaning of changeTo<>()
|
---|---|
Composite |
restart() |
Resumable |
resume() |
Utilitarian |
utilize() |
Random |
randomize() |
The way the non-default transitions (restart()
, resume()
, utilize()
and randomize()
) work does not depend on the region type.
Calling restart()
into a region of any type, or changeTo<>()
into a Composite
region - will activate the initial state:
using M = hfsm2::Machine;
using FSM = M::PeerRoot<
struct State,
M::Composite<struct Region,
struct Initial,
struct Secondary
>
>;
struct State : FSM::State {};
struct Region : FSM::State {};
struct Initial : FSM::State {};
struct Secondary : FSM::State {};
FSM::Instance fsm;
assert(fsm.isActive<State>());
fsm.changeTo<Region>();
fsm.update();
assert(fsm.isActive<Initial>());
Calling resume()
into a region of any type, or changeTo<>()
into a Resumable
region - will activate the initial sub-state the first time a region is activated.
Resuming a previously activated region will activate the previously active sub-state:
using M = hfsm2::Machine;
using FSM = M::PeerRoot<
struct State,
M::Composite<struct Region,
struct Initial,
struct Secondary
>
>;
struct State : FSM::State {};
struct Region : FSM::State {};
struct Initial : FSM::State {};
struct Secondary : FSM::State {};
FSM::Instance fsm;
assert(fsm.isActive<State>());
fsm.changeTo<Secondary>();
fsm.update();
assert(fsm.isActive<Secondary>());
fsm.changeTo<State>();
fsm.update();
assert(fsm.isActive<State>());
fsm.resume<Region>();
fsm.update();
assert(fsm.isActive<Secondary>());
fsm.restart<Region>();
fsm.update();
assert(fsm.isActive<Initial>());
Calling utilize()
into a region of any type, or changeTo<>()
into a Utilitarian
region - will:
- for every sub-state - query its Expected Utility Score by calling
utility()
method on them, recursively through the entier hierarchy below. - activate the sub-state with the highest utility score
using M = hfsm2::Machine;
using FSM = M::PeerRoot<
struct State,
M::Composite<struct Region,
struct LowRated,
struct HighRated
>
>;
struct State : FSM::State {};
struct Region : FSM::State {};
struct LowRated
: FSM::State
{
float utility(const Control&) { return 0.5f; }
};
struct HighRated
: FSM::State
{
float utility(const Control&) { return 2.0f; }
};
FSM::Instance fsm;
assert(fsm.isActive<State>());
fsm.utilize<Region>();
fsm.update();
assert(fsm.isActive<HighRated>());
Calling randomize()
into a region of any type, or changeTo<>()
into a Random
region - will:
- for every immediate sub-state - query its Rank by calling
rank()
method on them, no recursion through the hierarchy. - for all the sub-states with the top rank, recursively query its Expected Utility Score by calling
utility()
method on them - using utility scores as relative weights, randomly select and activate a sub-state
using M = hfsm2::Machine;
using FSM = M::PeerRoot<
struct State,
M::Composite<struct Region,
struct FilteredOut,
struct LowRated,
struct HighRated
>
>;
struct State : FSM::State {};
struct Region : FSM::State {};
struct FilteredOut
: FSM::State
{
int8_t rank(const Control&) { return 0; } // filter out using low rank
float utility(const Control&) { return 0.5f; }
};
struct LowRated
: FSM::State
{
int8_t rank(const Control&) { return 1; }
float utility(const Control&) { return 0.5f; }
};
struct HighRated
: FSM::State
{
int8_t rank(const Control&) { return 1; }
float utility(const Control&) { return 2.0f; }
};
FSM::Instance fsm;
assert(fsm.isActive<State>());
fsm.randomize<Region>();
fsm.update();
assert(fsm.isActive<HighRated>()); // note, it could be LowRated if the PRNG is seeded differently
Upon initialization, hfsm.dev performs default transition into the root region.
snippets/wiki_transitions-within-hierarchy.cpp
test/test_internal_transitions.cpp
test/test_resume.cpp