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?

_________________________________________________________________________________________

Edit: I can mark only one answer as the solution, but I would like to highlight this one as well: Redefining variable type in child class - #13 by KerimCetinbas

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.

Hello there;

gdscript doesn’t support variable shadowing, method overloading, or covariant field types, so the closest you can get is a pseudo getter/setter;


extends StateMachine
class_name CharacterStateMachine

var character_state: CharacterState:
  get:
    return state as CharacterState
  set(value):
    state = value

in c# you can shadow it new keyword. But it’s kills inheritance chain, you should forward it to parent object

public class CharacterState : State {}

public partial class CharacterStateMachine: StateMachine
{
        public new CharacterState State
    {
        get => (CharacterState)base.State;
        set => base.State = value;
    }
}

edit: as c# generics can be used

public partial class State: Resource {}

public partial class CharacterState : State {}

public abstract partial class StateMachine<T> : Node where T : State
{
  public T State { get; set; }
}

public partial class CharacterStateMachine: StateMachine<CharacterState> {}

only downside you can’t attach open generic objects to scene tree or decorate generic fields with export

Thank you! Not sure if it is reassuring though, as I understand I’ll never escape this trap :slightly_smiling_face:
But I take this as a good reminder to remain vigilant

Thank you for your detailed answer. I took the time to study your plugins (from your game template) and rebuild them step by step. What I have now is mostly identical to your plugin, but it was a good example of something worth understanding in depth, that was very instructive. I’ll feel totally comfortable building from it, but I think I first needed to acquire the theoretical base knowledge on that subject.

It gived some nice good practices insights too. Again, thank you for the resources.

Small side question: is there a place to discuss about it?

@KerimCetinbas Thank you for your answer. I kind of move along with that issue. I initially expected to get autocompletion and error detection that “relevant” static typing provides. I think your suggestion might help with that, I’ll give it some thought.

No, but feel free to post something here and start a discussion on it.

There is a related manta called “YAGNI” (“You aren’t going to need it”). In general, think about if something is actually needed before implementing it. Don’t get carried away implementing e.g. an entire linear algebra library if all you really need to do is add and subtract vectors. Godot does things like this because it is a general purpose game engine with contributions from thousands of people. None of us have that kind of manpower - no matter how tempting it is to emulate the expansive and general purpose nature of the platform we’re building on top of.

There is a long-standing mantra that “premature optimization is the root of all evil.” These words ring with a lot of wisdom, but I think it is far too easy for beginners to misunderstand this as “performance doesn’t matter.” We should be thinking about things like the algorithms and data structures we plan on using before we get too carried away. There are multiple approaches at hand to solve most problems, and most of the time they have trade-offs.

This is demonstrated in something as simple as containers. For the problem of storing a collection of objects, Arrays, Lists, and Dictionaries do more-or-less the same thing, but they each have implications and trade-offs. Arrays provide the fastest random access and iteration, but the slowest insertion. Lists provide fast insertion at any position, but the speed of random access decreases linearly with size. Dictionaries have a larger memory footprint, but fall in the middle performance-wise.

It isn’t the “evil” sort of premature optimization to think about things like this. A lot of times, the data structures we choose for our core systems will have profound impacts on the rest of the design. This can be seen in a comparison between Godot’s tree-based scene architecture vs. Unity or Bevy’s array-based entity-component-systems. The architecture of everything else reflects the underlying data structures, and it is not the sort of thing that can be “optimized” at the end of the development cycle. It would be nearly as difficult as trying to lose weight by replacing your skeleton.

The “evil” kind of premature optimization is when you go around searching for problems where they do not exist. For instance, taking a perfectly functional procedure and making it twice as long and much harder to understand because you wanted to eliminate duplicate calls to “relatively slow” trigonometric functions. This generally applies to any functions which take an input and provide an output without side effects. If the output is correct, your work is done. The better your architecture, the more functions will fall in this category.

On the other hand, when creating an interface which is going to be used by many other pieces of code, it is definitely worth stopping for a moment and giving it the basic sniff test before building months of work on top of it. “Does this make sense? Are there any obvious bottlenecks?” A little extra work up front can save a lot of effort re-designing it later. On the other hand, sometimes it is impossible to tell if something will work well unless you try. Sometimes your requirements can change in the middle of the project, too.

Oh, crap.

Ok, I just had really minor questions / suggestions while I was at it. Not sure that could request their own threads, but I’ll think about it if I have more.

Well then ask them here while you’re thinking about it.

Well, as I said, they were minor questions, and most already slipped out of my mind…
I was wondering if there was a place where I can ask those if they come back to my mind as I dive in the code again.

I just remember that one about how you pass the camera facing direction in your character-3d:

In my project, I’m getting my camera facing direction using this function, that keeps my code independant from whatever camera I use: get_viewport().get_camera_3d().global_basis.

I was wondering if there were any reason to NOT do it that way.

But now that I’m reading your code again, I’m understanding that you’re just doing it differently and rely on the camera to provide a specific “ready to use” facing direction (that one used here), not just the camera facing direction. I’m processing that in my character, but I now understand that for some cameras, it might make sense to process it differently and then do it on the camera.

So ultimately, I haven’t really any questions left here for now…