Redefining variable type in child class

Godot Version

Godot v4.7

Question

Hi, is there a way to redefine a variable type in a child class?

Example: I would like to have a generic StateMachine and one crafted for the needs of my character extending the generic one.

I have two base class: StateMachine and State
and CharacterStateMachine extending StateMachine and CharacterState extending State.

in the StateMachine class, i defined a variable current_state of type State

var current_state: State

In the CharacterStateMachine, is it possible to redefine the current_state variable type with one that extends its initial type State (CharacterState in this example)?

I feel this is a bit weird and I don’t think this is possible. What would be a good way to handle this? am I thinking about this in a really weird way?

No, but you don’t need to. You can just assign a CharacterState object to a variable of type State. That’s how inheritance works, and how it is intended to work.

Yes, I know. The reason I asked about that is to easily call variables or functions that would be specific to the CharacterState.

I know it would work anyway, but I just felt that having an irrelevant static typing was not a good way to do it.

I’m not sure what issues might emerge, maybe I should not really care.

That has more to do with how you implement the State and CharacterState nodes. For example…

IdleCharacterState ← CharacterState ← State

JumpPlayerState ← PlayerState ← CharacterState ← State

Each of those is a node that I’ve created using Add Node. Were I to reference IdleCharacterState in code like this:

@onready var idle_character_state: IdleCharacterState = $IdleCharacterState

I would get full autocompletion on everything I inherited from, and everything exclusive to that class.

Meanwhile, Movement State Machine is just the editor name for a StateMachine.

It doesn’t know anything about CharaterStates or PlayerStates. It is completely agnostic to whatever states it is switching between. In fact, it’s the exact same code as my Game State Machine.

My State Machine Plugin contains two files, and I use them for all my state machines. The only changes I make to derived classes is to make them actually implement functions beyond logging.

It’s only irrelevant if you make it irrelevant. In which case, if it becomes an issue, you can either fix your architecture so that inheritance is a benefit, or you can use a function to run interference.

Create a setter and getter and override them. Make the State class @abstract, and while defining them to only take and return a State respectively, internally do type checking and casting.

I do not suggest this approach, but it’s available.

I wouldn’t worry about it. As you use your state machine, you’ll refine it.

Thank you, that gives me food for thought, I still have to process some information.

That’s probably the way I should build it but I wasn’t sure I could keep it completely agnostic until the end.

I started learning State Machine with this serie of videos (there is a textual transcription here). I really don’t have the experience to truly judge, but I felt some stuff was a bit ā€œwrongā€ and everything a bit too coupled. Now I’m trying to get a better understanding on that topic.

And in the process, I am studying your own State Machine Plugin and reading some of your long posts about other topics, thanks for the resources!

It looks ā€œcoupledā€ because you forcibly separated things that belong together into too many pieces, just because someone said you should. Fragment less and you won’t have to worry about coupling.

Always try to minimize the number of classes while still modeling the problem domain adequately, and aim to keep your class inheritance tree as shallow as possible, ideally no inheritance at all.

Thanks for the tips.

I actually don’t think of it that dogmatically (but saying ā€œeverythingā€ was a stretch, for sure, I have some specific points in mind). But as I said, I don’t have the experience to truly judge, I’m still assessing and and trying to find what could best fits my needs. I’m currently not overwhelmed or lost in the code, but I felt I could easily be in the future building from there and had to find some adaptations.

It could be just my skills, though, I still have to look at it in more depth.

This is known as future proofing (aka speculative/premature generality). It’s a waste of time and mental effort in most cases, especially if you’re not experienced. Your predictions are almost always guaranteed to be wrong, even if you’re an experienced developer.

Instead of succumbing to that, always implement the simplest solution for your actual known problem, as it exist right now.

This is SO true. I fell into this trap when I first came to Godot, and I see so many experienced developers do the same thing. One ends up creating things that Godot handles in other ways, and much more elegantly.

I talk about my process in this post:

And if you read it, you’ll see where I refactor as I go. It prevents Future Proofing.

I always recommend following a tutorial exactly as it is presented, and then re-implementing it yourself afterwards. It helps reinforce the learning, prevents issues with things not working because you changed something you didn’t realize was important, and makes the tutorial go faster. Keep what you want, discard the rest.

Thank you for the warning.
I had a previous project done with Unreal Engine that I feel has become too much of a mess. It’s not hopeless and I think I can easily extract some elements or ā€œcleanā€ it with a lot of work, but it was hard to continue adding anything, I felt the foundations were not good enough.
That’s what I would like to avoid. Of course, I wasn’t going make the same mistakes again, but I could easily make all new ones :slight_smile:
You’re right, I’ll be wary of that and try to find the right balance, refactoring as you go seems a good point.

No, that’s one I haven’t read, I’ll have a look, thank you.

Good, thank you for the recommandation

Is there a good method to get the information you ignore to ignore in order to prevent doing that?Are there other good information centralisation other than the documentation?

And would you recommand simply getting code from plugin and using it without understanding it in depth?
I’m in the process of learning and trying to rebuild from scratch in order to able to adjust everything. I’m a bit ā€œafraidā€ of building from systems that I don’t know in depth, but maybe I’m also losing my time there.
I guess I could use some basic features that way. Could State Machine be one of them?

Edit: I’m realising with these last questions I might be in the same mindset of Future Proofing instead of just making things work.

Maybe as a small reassurance, @jul-a , even after 30 years of hacking (I refuse to call myself an expert on anything, except maybe trying out stuff), I still run into the future proofing trap.

And the premature optimization trap.

And the ā€˜miscoupling’ pattern we noticed here: when things are close to eachother they communicate faster (shared memory space for instance).

Anyway, before I fall into the associative grandpa story telling trap again, I’ll leave you at it.

Just wanted to compliment you (all) on the great learning curve happening in this thread

Experience. You watch enough videos, and everyone does things slightly differently. After a while you find the way that works best for you. Over time, that will likely change.

The forums.

Depends on your goal. If a plugin does what you need, then yes. This is something we do all the time in programming - we use packages that other people make. Understand the things you want to understand, and then the things you need to understand will expand that knowledgebase.

Being afraid of failure will hold you back in life. Doesn’t matter if your are programming or anything else. Failure is how we learn. The more you fail, the more successful you will be.

Yep. There’s plenty of plugins for that, and state machines are actually a pretty simple thing at their core. Hence why you can make them out of Enums.

Yep.