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

Decouple State Machine logic from Animation logic #25021

Closed
Tracked by #6
Jay2645 opened this issue Jan 16, 2019 · 11 comments
Closed
Tracked by #6

Decouple State Machine logic from Animation logic #25021

Jay2645 opened this issue Jan 16, 2019 · 11 comments

Comments

@Jay2645
Copy link

Jay2645 commented Jan 16, 2019

Godot version:
Commit ffcb5cd

OS/device including version:
Any

Issue description:

I'd like to take a look at opening a PR that decouples the State Machine code from the Animation code, but before I went ahead and did so, I wanted to make sure that it's something that might get looked at for inclusion and see what the possible options are.

Why?

There's a lot of different use cases for having an FSM as part of the editor. It could be used as a baseline for character movement, ensuring that characters can't jump when they're already in the air and can't sprint while crouching, if a game calls for it. It can also be used to implement combos in a fighting game, for "old-school" button-based cheat codes, for managing achievements, for RPG quests, etc.

On top of that, it would still be used inside the animation engine to drive animation transitions and stuff -- all this would do is decouple it from that, so that players could easily modify state machines inside the editor. FSMs can be done in GDScript or GDNative already, but given we have most of an implementation already inside the engine, it makes sense to expose it, preventing users from having to roll their own (plus they benefit from having the speed of native engine code).

Current state

As it stands now, the state machine logic is contained in "animation_node_state_machine.h" and consists of 3 classes:

There's also the editor tool AnimationNodeStateMachineEditor, which extends from AnimationTreeNodeEditorPlugin.

Changes

The first 2 would be pretty simple to keep without breaking any compatibility -- just create new StateMachineTransition and StateMachinePlayback classes with all the current logic, then have AnimationNodeStateMachineTransition and AnimationNodeStateMachinePlayback inherit from them.

The issue is in the AnimationNodeStateMachine itself. If we tried the same approach of just naively making a StateMachine class, it would need to inherit from AnimationRootNode to avoid potentially breaking compatibility, which isn't very intuitive. Instead, AnimationNodeStateMachine could potentially be changed to function as a wrapper for a StateMachine class. Other suggestions are welcome.

Another sticky situation is AnimationNodeStateMachineEditor. Right now, it provides a way to have users drop down State Machine nodes, link them, and then edit them. It would be nice to keep an editor for state machines, so it could probably simply be named to StateMachineEditor. However, refactoring it like that just to keep the editing code would still make it inherit from AnimationTreeNodeEditorPlugin, which would again imply that it's still part of the animation system. You could simply duplicate any code required for editing nodes and such and have it inherit from a new class, StateMachineEditorPlugin, but code duplication is Bad™. I haven't taken a close look at the editor side of things, however, so it could be that we can get by with minimal to no code duplication.


On a related note, right now the animation state machine simply provides a way to transition between animation states. This makes sense -- each node represents an animation in some form (whether that be a BlendSpace, another state machine, or what-have-you), and when you switch states you transition between them. The issue comes when you're using a FSM for something that isn't related to animation -- what do you use as the "nodes" in the graph? If a user wants to use a FSM for character movement, all they care about is what states are valid for the character to transition into -- which could be solved by looking at the name of the node. Animations have some kind of animation data associated with each node. Meanwhile, fighting games might have various combos that change moves based on the player's previous button presses.

A potential solution would be to create a new State class. State would have a name as well as signals that get broadcast when the state becomes active or inactive. These states would be selected as nodes in the StateMachineEditor class, with possible transitions defined between them. Since the concept of time isn't necessarily a thing when designing transitions between character states, I'm happy with just leaving transitions as data while giving users a way to query which state transitions are valid and manually transitioning between them.

This would also give a new purpose to the AnimationNodeStateMachineEditor, since it would accept a source StateMachine and then map each State to an AnimationNode. The AnimationNodeStateMachine wrapper would play the AnimationNode mapped to the State and automatically transition between them, as it does now.

Summary

Having a FSM that's decoupled from the animation system would let users create their own state machines inside the editor that they could use to power their character movement code, input system, achievement system, quest system, etc. These state machines would also work in the animation system, so a smart developer could use the FSM that determines character movement to also determine character animation.

However, there are a few problems:

  • Our StateMachine would have to remain a valid node in the animation player, so it would have to inherit from AnmiationRootNode. A couple solutions for this would be either changing AnimationNodeStateMachine to function as a wrapper to the StateMachine or using multiple inheritance (which I'm not sure is okay in Godot -- I'm fairly new to the engine, but I don't see multiple inheritance used anywhere).

  • The state machine editor would have to be decoupled from the animation plugin, too. I haven't looked at this too hard, so I'm not sure exactly how much things would need to change. Concerns involve possible code duplication as well as ensuring that the state machine editor still appears as an option when an AnimationTree is selected in the editor.

  • The "type" of node allowed in the State Machine would have to be specified somewhere. Right now, transitions between animations make sense, as does transitioning to something like a BlendTree. But how would transitioning to, say, a string work? A possible solution for this would be 2 separate editors -- a "normal" state machine editor which has a State class which users could subclass with their own logic (potentially), as well as a separate editor which allows users to pair each State in the state machine with an AnimationNode, such as a BlendSpace.

  • This is potentially a step down a slippery slope, as expanding upon the state machine system would lead to requests like hierarchical FSMs. As it is, this is expanding the "simple" state machine system already in Godot slightly just by the addition of a State class. While I think it's a potential step forward, I can also see how it could potentially lead to bloat -- this would turn a single AnimationNode into a mapping between a State and an AnimationNode, and allows for gameplay logic inside of the State class. The question at hand is whether enough games can find a use for a FSM for it to be warranted as a core engine feature. I'd say that FSMs are flexible enough that pretty much any game can find a use for them, but I've heard @reduz wanted to limit the system to something simple that's only used for animation.

Any changes would likely have to wait for 4.0. I know that there's been some discussion inside #19773 (which this system could potentially address), and there's also an active pull request expanding the animation state machines already (#24402).

Should we keep FSMs limited to the animation system and have users roll their own if they need them, or should we make them more generic?

@razcore-rad
Copy link
Contributor

Why would StateMachineEditor have to inherit from AnimationTreeNodeEditorPlugin, couldn't AnimationTreeNodeEditorPlugin just use StateMachineEditor as a component? Sorry, I just skimmed through fast, I haven't read all other potential problems.

I think it's a good idea.

@Jay2645
Copy link
Author

Jay2645 commented Jan 16, 2019

@razcore-art As it stands right now, the editor side has AnimationNodeStateMachineEditor (allows editing of "State Machine" animation nodes) inheriting AnimationTreeNodeEditorPlugin (allows editing of all animation nodes).

I was suggesting that a naive approach would be to simply refactor AnimationNodeStateMachineEditor into a StateMachineEditor class, but that there might be code in AnimationTreeNodeEditorPlugin providing editing functionality that may need to be duplicated (disclaimer: I haven't taken a close look at the code in question).

In the end, though, it could use StateMachineEditor as a component if we decide not to make a dedicated State class, or it could have users input a "source" StateMachine and then map the nodes to animation files if we do wind up having a dedicated State class.

@groud
Copy link
Member

groud commented Jan 16, 2019

Mostly duplicate of #8847.

The main argument against it is that there are plenty of ways to implement a Finite State Machine, so we do not want to implement that as built-in in Godot. And it is apparently the case for all AI features (such as behavior trees for example).

@groud
Copy link
Member

groud commented Jan 16, 2019

Also, @reduz said once he did not wanted to complexify the StateMachine system, and keep it for animation. So such change is unlikely to happen.

@jkb0o
Copy link
Contributor

jkb0o commented Jan 16, 2019

I am super interested in this. I use a lot of Spine and for now, I'm badly missing something for managing states. Godot's AnimationTree looks promising, but I didn't find an easy way to integrate it with Spine runtime (generating native Godot animations from Spine isn't an option).

@Jay2645 I will be glad to contribute into this or at least help with testing integrations form StateMachine client side.

@Chaosus Chaosus changed the title [Proposal] Decouple State Machine logic from Animation logic Decouple State Machine logic from Animation logic Jan 22, 2019
@slapin
Copy link
Contributor

slapin commented Feb 7, 2019

Well, I would be interested in some extension in AnimationStateMachine if it would have single special state which could send signal. Currently this can be implemented with function tracks, but that is quite slow (verbose) process to do so sending signal directly from StateMachine about getting to some state would solve all the problems. Otherwise code which works with state maching gets VERY hairy if state machine is complex enough.

If you want GUI-enabled FSM for your own, there is node editor in Godot where you can implement any node-based abstraction. From there you can generate json or Dictionary object and use that in your code for state management if you consider this effective.

As for AI tools in general the @reduz opinion is quite shallow. Yes, there are many ways to implement AI but there is quite limited set of AI needs for engine, mainly spatial hashing and awareness, which currently not possible to fullfill without some core developer help. Additionally, there is no multiple ways to properly (effectively) implement behavior trees in Godot currently (no way to implement that) unless your game is very slow pace. Do not even try this "pattern" with Godot, you will be sorry, just use actually provided tools (scripts and dictionaries are all you can have for AIs). You can implement kind of HSM with nodes and that is actually working pattern for small scale AI and it works well enough.
If you're just going for visual AI editing as in "no coding needed" you're quite out of luck here and the actual concept of this is a bit ill, because you just replace one language with another and call it "no coding". Also most things are actually more work to do visually. Normal AI systems are mostly working with code, where you implement all the AI blocks you need. You use the tree to combine them in way for fast tweaking. Godot does not have any concept to produce this in efficient manner, as core for that is needed to be implemented in C++ and most blocks are written in C++ and you can add high-level blocks in scripting language on top of that. So the only current plausible scalable AI concept with Godot is HSM on nodes. This scales to 10-20 characters, but latter you will have hard time optimizing it. But if you instead of using nodes directly put all the construct into Dictionary at startup and use that instead you save about 15% CPU. Proper blackboard implementation using dictionaries for each parameter and each object as key works very effective, using that you can scale to about 40 agents on i7 each running AnimationTree too. If you use less AI ticks for distant/invisible objects or more simple AI navigation methods for them you can have even more if you want. Not tested that on low end hardware though.

@ShawnMcCool
Copy link

I always appreciate the argument against creating a general purpose tool of high complexity that only generates edge-cases. I'm entirely happy with sticking with code.

HOWEVER

It would be lovely to have a visual state machine for simple use-cases.

@slapin
Copy link
Contributor

slapin commented Mar 29, 2019 via email

@Xrayez
Copy link
Contributor

Xrayez commented Jul 6, 2019

Reasonable or not, people will start to mess with this and do hacks and workarounds to turn an "Animation State Machine" to a "State Machine" via scripting. So might as well provide (perhaps separate) implementation for a simple state machine to prevent users from exploiting existing animation system and make this clear in documentation.

@madmiraal
Copy link
Contributor

Feature and improvement proposals for the Godot Engine are now being discussed and reviewed in a dedicated Godot Improvement Proposals (GIP) (godotengine/godot-proposals) issue tracker. The GIP tracker has a detailed issue template designed so that proposals include all the relevant information to start a productive discussion and help the community assess the validity of the proposal for the engine.

The main (godotengine/godot) tracker is now solely dedicated to bug reports and Pull Requests, enabling contributors to have a better focus on bug fixing work. Therefore, we are now closing all older feature proposals on the main issue tracker.

If you are interested in this feature proposal, please open a new proposal on the GIP tracker following the given issue template (after checking that it doesn't exist already). Be sure to reference this closed issue if it includes any relevant discussion (which you are also encouraged to summarize in the new proposal). Thanks in advance!

@Xrayez
Copy link
Contributor

Xrayez commented Jul 11, 2021

For those interested, I've created a discussion about implementing a general-purpose FSM in Goost, see goostengine/goost#96. The main idea is that FSM functionality could be implemented as a new scripting language akin to VisualScript, but more specific.

I have previously resurrected so called multi-script PR: goostengine/goost#92, and I think a similar approach could be taken.

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

No branches or pull requests

9 participants