Godot design flaw: inheritance VS composition

Godot Version

Any

Question

Hello everyone,

I’ve been using Godot for a while now and I’m used to other engines such as Unity and Unreal as I use them daily for school.

However, recently I’ve researched on best practices, workflow and design of Godot but I stumbled uppon one big problem…

Godot is based on inheritance more than composition (you have nodes, but nothing that allow for single responsibility nodes equivalent to Unreal components; if you know a way to make this work, please, mention it), but GDScript being a duck typing language, it uses principles that better fit composition that inheritance.

Let’s say you have an ennemy class that represents all ennemies and a turret class that represents things that shoot bullets. Now, you want to create a tank that is an ennemy moving turret. How can you do that by being structuraly consistent and strong?

The problem here is that interfaces don’t exist in GDScript (nor multiple inheritance), so the contract design is not really strong. The best you can do is using the method “has_method” but then you have to hardcode a string and rely on a less performant system. If you try using composition instead, you are stuck with your tank that is either an ennemy OR a turret containing the other one or neither that contains both, hence the logic is spread everywhere and hard to maintain.

Of course, on such a small scale, using “has_method” is not too bad, but as the projet grows, it becomes more and more problematic.

Is there a good approach on this kind of problem with Godot? If so, what is it? Otherwise, why is Godot designed that way (using a weird mix between inheritance and composition designs) and are there plans to fix it or other solutions (forks, plugins, future version, etc.)?

Thank you!

- A serious video game student that want to use Godot for future potentially big projects :stuck_out_tongue:

So what I would do personally isn’t make a “turret class” I would make a “bullet spawner” node that could go on turrets or tanks or player guns. Turrets and Tanks would be the AIBase node type and “enemy” would only be set from either a team enum inside of the AIBase node (and Player node so players would be on a team and enemies would be on another team) or create yet another team node that the AIBase or Player scenes could pull from.

Additionally, why in your example isn’t turret and tank both extending the “Enemy” base class?

While has_method or has_meta are useful at times, I hardly use them when I structure things ahead of time to be more node-focused.

Overall think of Godot’s Node3D as both “AActor” and “UActorComponent” depending on how you use it. After all in Unreal, an actor is its RootComponent. If you move the RootComponent via transform you are moving the whole actor. There are times when the root component differs from the actor but break down that wall and you have essentially what is Node3D in Godot.

Lastly, since in Unreal you can’t have multiple inheritance without interfaces and in Godot you can extend a Node, it’s about equal. An interface in Godot can be simply created with has_method or has_meta and making assumptions (then documenting those assumptions.) I personally don’t love that way of creating interfaces but it doesn’t make it too different or overly complex from Unreal’s system.

This link might help, the explanation was good

4 Likes

I would definitely say Godot leans more towards composition. I don’t personally like Inheritance when it comes to game dev because it feels too strict, so I don’t use inheritance except for some niche scenarios, or when extending the basic built-in nodes.

If you write every node script as a single responsibility object, you are achieving the purpose of composition. Extension/subclassing should still be for single purpose, but maybe in a different way (like how DirectionalLight2D and PointLight2D both extend Light2D, or VisibleOnScreenEnabler2D extending VisibleOnScreenNotifier2D)

Don’t be afraid to add nodes, don’t clump functionality into one single node, if your code starts to become big, look at functionality that could be separated.

I typically use a hybrid approach. My main game objects have their own class (Player, NPC, Projectile, etc.) that derives from some type of node. Then, I use composition from that point on.

I found that I was duplicating a lot of code to make this work in a general way, so I made this library to help (C#). It uses C# source code generators to generate some of the boilerplate glue code.