How can I override a child node of an inherited scene?

Godot Version

4.2.1

Question

I’m very new to Godot and love it so far and am currently trying to build a little RPG. :slight_smile:

However, I’m a bit stuck with how I can create multiple scenes effectively, without repeating myself.

Goal

My goal is to make it easy to create many monster types that are pretty similar but still need their own identity here and there.

Issue

Let’s say I have a Player and a MonsterA, MonsterB, etc. scene. Players and monsters share many traits. They all have a health bar, a sprite, a hitbox, and a weapon. My idea is to use inheritance to create a base class that handles all of that: Actor

Now, for the script part, everything is clear so far. I can just create a base class and have method overrides.
But I’m stuck with overriding Nodes in the scene tree. The idea is that there’s default Weapon on Actor and if MonsterA needs a different weapon or hitbox, I can override the default one. But Godot doesn’t let me replace the existing Weapon or Hitbox.

How do you handle such a use case? I cannot imagine I’m the only one with this problem.

If I don’t use the inherited scene option, I lose all the default settings, like position information or a default health bar style, that the base class provides.

1 Like

You cannot replace nodes from the “mother scene”, but you can still customize its properties.
And when that is not enough, you can create “placeholder” nodes that will store some information and put custom nodes under them.
For instance, you can create a Marker node named “WeaponContainer” in which you put your custom weapon scene for each monster. The WeaponContainer will contain all position information, and maybe some logic to instantiate the adequate weapon, or even handle weapon basic logic

Although I haven’t tried it myself, I imagine you can use metadata values to specify the node you’d like to add to the Actor scene. It’s accessible from the bottom of the Inspector tab when a node is selected and you can specify a variety of different data types to be associated with the node.

For example, say for Monster1 you load it into the main scene via script. When initializing a new monster instance, you could write:
monster1.set_meta('meta value name', 'variant to set value to')
and pass in a nodepath (if your weapon item is already within the scene) or a file from the file manager (for calling load() or preload(). :)

then, upon monster1’s ready call it grabs it’s own metadata via get_meta() and loads the associated file/nodepath as a child. :]

this method wouldn’t start the monster with a weapon placeholder should no value be passed in on initialization - but you could add a placeholder as it’s own scene and add like you would an actual weapon via this :)

Apologies if it doesn’t work as expected - I am not able to test this myself at the moment but I hope it helps you proceed in the right direction, and I recommend looking into metadata values - they are quite helpful in a variety of applications!

Thanks for your response!

For instance, you can create a Marker node named “WeaponContainer” in which you put your custom weapon scene for each monster. The WeaponContainer will contain all position information, and maybe some logic to instantiate the adequate weapon, or even handle weapon basic logic

That’s a great idea, yeah. I need a WeaponSlot kind of thing anyway. How do you make sure that only Weapons can be children of it?

I’ve also looked into using templates of hitbox, weapon, health bar scenes, etc., adding them in the MonsterA scene and then linking them to the actor using @export variables in Actor. Do you think that’s a bad idea?

Thanks for your response!

Ah, so, as a way of dynamically adding a weapon while spawning the monster? That sounds like a really good idea. I was not yet thinking about that.

I think this means, I should probably not go with the inherited scene approach too much. At least not inheriting the nodes.
An @export variable would work as well instead of metadata, correct? Because I was just thinking about how to link the weapon to the actor’s script when typing my last reply.

1 Like

The Composition pattern might be a good fit for this use case:

1 Like

One solution is to connect the child_entered_tree signal of the WeaponSlot to itself, and check if the child is of a correct type (with a condition such as if child_node is Weapon), otherwise you delete it. Then you make the WeaponSlot script run in editor with the @tool keyword.
That way when you try to add something in editor under a WeaponSlot, it will only accept the particular type you have enforced.

That is a good idea and something that is quite usual. It’s a good way to enforce the type as well because Node exports can be typed (as opposed to PackedScene exports for instance)
For example :
@export var weapon : Weapon # extends Node3D
will only let you select Weapon nodes in the scene.

1 Like

Certainly! As francois.delataste explained; it’s quite helpful for enforcing types, and usually easier to access in scripts than metadata. The added inspector node for changing the export value in the editor is nice too.

Thanks for all the responses.

I think I will scratch the inherited scene approach, as it is too limiting without being able to override stuff. I will use the simple @export var weapon: Weapon approach, together with _get_configuration_warning for now.
Later, I will probably transition to a composition pattern (thanks @soundgnome), as it feels a bit too early to do that now. And I lack the needed experience to know how big a component should be.