Closed JKot-Coder closed 10 months ago
I think we have this problem as well, currently we implemented a workaround for this by separating sub machine as states to the main state machine, instead of using sub machine as a state.
I have this issue also. Lets say there is a statemachine similar to above containing state A, SubstateMachine, and State E, where the states B and C and D are in the Substate machine. Normal transitions are defined in the base state machine from A->Substate and Substate->E. Substate transitions are defined from B->C and C-D. You would think that once the substate machine's transitions are complete (B->C and C->D) then base state transtion Substate->E will occur. But what happens is once substate B finishes, the base state transition of Substate->E will fire and force state C to simply exit immediately, skipping the rest of the Substates, and then continue to state E. This essentially makes the use of substate machines useless unless you do NOT define a normal transition in the base state machine from the Substate->E and use a trigger transition instead called from the last substate. This is a nasty bug to track down and am looking into a fix for it but if the the creator Inspiaaa could add some insight or a quick fix that would be great.
@jszung
You can take a look at this commit https://github.com/JKot-Coder/UnityHFSM/commit/76214843ce7436387b31bd9f9ca3e6a89d44606a, it works fine for me. After this fix, you should explicitly call fsm.StateCanExit()
from the SubstateMachine to toggle the state of the high-level state machine.
Thanks for pointing out this issue. I have been working on a fix for this problem in another branch, but have not merged it into the master branch yet, although I intend to in the future.
In the meantime, you can already use it by cloning the feature/exit-states
branch (https://github.com/Inspiaaa/UnityHFSM/tree/feature/exit-states) which introduces two new features.
The main problem in the current version is that you can only define transitions between two states within one state machine, but cannot define the exit condition that leads to a transition "up the hierarchy"; it is implicitly assumed.
This branch aims to fix the issue by introducing the concept of an "exit state", a state in which the state machine is allowed to exit up the hierarchy. In @jszung 's example, we would declare D
the exit state. Once the state machine SubstateMachine
enters D
and has a transition pending itself (to E
), it will exit to E
. This system also respects the needsExitTime
behaviour of the exit state: when the exit state can safely transition, the state machine can exit.
You can mark a state as an exit state in the constructor:
var state = new State(isExitState: true);
or using the shortcut methods:
subStateMachine.AddState("D", isExitState: true);
For this to work, the subStateMachine
needs its needsExitTime
property set to true
.
For another example, you can have a look at the tests: https://github.com/Inspiaaa/UnityHFSM/blob/feature/exit-states/Tests/TestHierarchicalTiming.cs
Unrelated to the issue above, this branch also introduces "ghost states". These are states that the state machine does not want to remain in and seeks to exit as soon as possible. Once the fsm enters a ghost state, it will immediately try to exit if there is an outgoing transition (of course respecting the needsExitTime
behaviour of the ghost state). This means that multiple transitions can occur in one OnLogic
call.
States can be marked as ghost states in a similar fashion:
var state = new State(isGhostState: true);
An example can be found in the tests: https://github.com/Inspiaaa/UnityHFSM/blob/feature/exit-states/Tests/TestGhostStates.cs
Let me know if this helps.
version 1.9
stateMachine.AddTransition("Submachine", "D", (t) =>
{
return submachine.ActiveStateName == "Submachine state C";
});
add condition can resolve the problem. the condition prevent root machine pending until submachine entering "Submachine state C". so there is nothing to fix in my opinion.
ps:I prefer using trigger in most situation
After working some more on this issue, I have developed a new, more versatile solution. It takes a slightly different approach to the "exit states" that I had proposed and implemented previously.
In my new solution, instead of declaring in which states the parent FSM may exit and move to the next state, you can explicitly define the exit transitions themselves. This allows you to leverage the full power of the existing transition and timing systems.
This new feature is part of the recent 2.0 release. For an installation guide, please see the README.
For usage examples and documentation you can take a look at the README, the quick feature overview, or the advanced guard AI tutorial.
I implement a simple chain of states like:
A => submachine state B => submachine state C => D
What I expected: Changing state one by one every mouse click.
What I get:
Enter: A Mouse click. Can exit: A
Enter: Submachine state B Mouse click. Can exit: Submachine state B
Enter: Submachine state C
The problem is this: the state of the root state machine instantly changes to "D". This happens because StateCanExit always propagates StateCanExit to the root state machine without any mechanism defining this behavior.
Enter: D