Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

StateMachine<T> and StateMachine needs a common StateMachine base type #27

Closed
tomsseisums opened this issue Aug 31, 2023 · 6 comments
Closed

Comments

@tomsseisums
Copy link

private StateMachine fsm;

Awake()
{
    // Fails, because this is StateMachine<T> and StateMachine<T> is not derived from StateMachine
    fsm = new HybridStateMachine(); 
}

or

private StateMachine fsm;

Awake()
{
    var nestedFSM = new HybridStateMachine();
    nestedFSM.AddState("INITIAL");

    fsm = new StateMachine();
    fsm.AddState("NESTED", nestedFSM);
}

Update()
{
    switch (fsm.ActiveState) {
        case StateMachine { name: "NESTED" } nestedFSM:
            // do something...
            // fails again, because HybridStateMachine isn't derived from StateMachine
            break;
    }
}
@tomsseisums
Copy link
Author

tomsseisums commented Aug 31, 2023

Right, I remember another aspect to this, while technically we can use IStateMachine, AddState does not support IStateMachine.

And also IStateMachine is super limited if used to expose StateMachine for public use, it could make use Trigger and OnAction methods at least. Or give us two types, like IActionableStateMachine that derives from IStateMachine and exposes Trigger/OnAction.

@tomsseisums
Copy link
Author

I guess a better example is this:
image
image
image
image

@tomsseisums
Copy link
Author

tomsseisums commented Sep 6, 2023

And here the IStateMachine issue:
image
image

@tomsseisums
Copy link
Author

tomsseisums commented Sep 6, 2023

And also IStateMachine is super limited if used to expose StateMachine for public use, it could make use Trigger and OnAction methods at least. Or give us two types, like IActionableStateMachine that derives from IStateMachine and exposes Trigger/OnAction.

Right, we have ITriggerable and IActionable, but no way to have both-in-one excluding other public methods.

@Inspiaaa
Copy link
Owner

Hi @tomsseisums,

Thanks for providing so much information!

The issue you are talking about is one of the minor inconveniences of the way that UnityHFSM is implemented internally regarding its support for generics.

StateMachine, StateMachine<T>, HybridStateMachine and other overloads of these classes do actually share a common base class - it's just not directly apparent.

The "actual" StateMachine implementation is in the generic version of the class: StateMachine<TOwnId, TStateId, TEvent>. The other overloads that require less parameters inherit from this base class and help you avoid boilerplate code when working with the defaults (strings).

As HybridStateMachine extends the behaviour of StateMachine, it simply inherits from this class. As it also has to support generics, it inherits from the aforementioned generic version of StateMachine.

If UnityHFSM didn't support generics, the inheritance hierarchy would therefore look like this:

classDiagram
    class StateMachine
    class HybridStateMachine

    StateMachine <|-- HybridStateMachine

With generics, it looks like this:

classDiagram
    class StateMachineTTT["StateMachine[T, T, T]"]
    class StateMachineTT["StateMachine[T, T]"]
    class StateMachineT["StateMachine[T]"]
    class StateMachine["StateMachine"]

    class HybridStateMachineTTT["HybridStateMachine[T, T, T]"]
    class HybridStateMachineTT["HybridStateMachine[T, T]"]
    class HybridStateMachineT["HybridStateMachine[T]"]
    class HybridStateMachine["HybridStateMachine"]

    StateMachineTTT <|-- StateMachineTT
    StateMachineTTT <|-- StateMachineT
    StateMachineTTT <|-- StateMachine

    StateMachineTTT <|-- HybridStateMachineTTT

    HybridStateMachineTTT <|-- HybridStateMachineTT
    HybridStateMachineTTT <|-- HybridStateMachineT
    HybridStateMachineTTT <|-- HybridStateMachine

In other words, using StateMachine<TOwnId, TStateId, TEvent> as the type in your examples should fix the bugs.

As to the IStateMachine interface, I've explored different alternatives and reached the conclusion that the current implementation covers all practical conceivable cases at this point in time, without introducing additional complexity. As you have noticed, the IStateMachine interface was not intended to be used "publicly". Instead it creates a minimal layer of abstraction between the nested state machines, that allows each nested state machine to use its own generic types.

For more information on the use of generics, please check out the WIP README in the release branch for the 2.0 release or this issue.

I hope I could clarify this.

@Inspiaaa
Copy link
Owner

I have now written documentation on this topic which you can find in the wiki here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants