Make a scene inherit another scene way after creation?

Godot Version

Godot v4.4.1.stable.mono - Windows 11 (build 26100)

Question

For example, we have the following scenario of scene inheritance:

B ← C ← D

When at some point you realize a scene A as parent would make sense, can you somehow change B to inherit from A to have a resulting chain of:

A ← B ← C ← D

I know of the feature “New Inherited Scene” but did not find anything to do this afterwards. If it is not possible, is there maybe any workaround by manually fiddling with the files, etc.?

No, you cannot change the parent class.
There is a reason that it’s called inheritance, not adoptation.
kidding :grinning_face:

I think you mean changing the parent node?
In that case, you can use “reparent()”

@MightyPhil no they are talking about Scene inheritance, not Class inheritance.

@lord_stuff no, there is no easy way to do this. You have to re-create all inherited objects. While the scene inheritance feature is useful, it is finicky. If you change anything in any of the yellow nodes in an inherited scene, it no longer inherits from the parent scene, and changes made upstream do not affect it.

There are ways to get around this, but it depends on how complicated your parent scene is. For example if I want to create a scene and make it inheritable, and it only has one or two nodes extra, I will construct those nodes in code. Then I can inherit from the class instead of the scene.

Is it not the same? I always thought they are practically the same. Well, guess I am wrong then.

No it’s not the same. This is an example of scene inheritance. I create an Enemy scene like so:

Then I go up to the Scene menu and select New Inherited Scene.

I select the scene and then create the Zombie scene.

All the nodes with yellow names are inherited and should be changed in the Enemy scene. All the grey-named nodes are unique to my Zombie scene and I can change them there. Changing any of the yellow node in the Zombie scene will prevent the scene from getting any further changes made to the Enemy scene in the future.

The behavior of scenes has many similarities to classes, so it can make sense to think of a scene as a class. Scenes are reusable, instantiable, and inheritable groups of nodes. Creating a scene is similar to having a script that creates nodes and adds them as children using add_child().

Nah, I think it is fine to think scene as class.

The most special thing about a scene, it seems, is it can be edited inside godot.

Hmm, still not seeing the difference. Is your example not like inherit from a parent class and add some new stuff to it?

Yes you can think of a scene as a class. In fact, it is an instantiated object of a class.

If you are a beginner to Godot that’s a good way to look at it.

Ok.

It is exactly like that. Except that instead of creating Enemy in the Godot editor using its scene tree, you have to create it all in code in the enemy_2d.gd file. And also you cannot see or edit any of those nodes in the Zombie scene unless the game is playing. The best you can do is turn it into a @tool script and add a bunch of code to allow you to edit those invisible child nodes.

Here’s a simple example of how to do that: GitHub - dragonforge-dev/dragonforge-curved-terrain-2d: Plugin that adds a CurvedTerrain2D node that can be used to create curved 2D terrains.

I see.
So a scene is a class with some extra features?
or does it break any property of a class?

No. A scene is a complex instantiated object created from a class or collection of classes (Nodes usually).

ok, thanks for the explanation.

1 Like

Thank you both for the responses and the explanations.

I feared that it would not be possible to change scene inheritance afterwards. Recreating all will be impossible at some point. So it is better to steer clear of scene inheritance at all, actually, to not risk that.

Do you know decent alternatives to it?

Scene is a branch of nodes. It can contain no script/classes at all.

@lord_stuff

Scene inheritance is an editor feature. It doesn’t really exist at runtime. In fact even scenes do not exist as entities at runtime in the scene tree. It’s just a large tree of nodes.

Maybe take a look at composition?

1 Like

A scene can contain multiple scripts. One for each node.

Also, the base node of a scene is always made from code - either Godot engine (C++) code, or an attached script in whatever language you’re using. This Curved Terrain plugin is a perfect example of that. (See below)

The Curved Terrain plugin I mentioned above was four nodes in a scene that I turned into a @tool script. This way it could be used as a complex scene, but added as a single node to a project. It’s a Path2D with four additional nodes: Polygon2D, Line2D, StaticBody2D, and StaticBody2D hanging off it. The drawback is only the features that are raised up through code on the four hidden nodes can actually be manipulated.

@tool
class_name CurvedTerrain2D extends Path2D

## The image to fill the inside
@export var fill_texture: Texture2D
## The image to use as the edge of the terrain.
@export var edge_texture: Texture2D
## The thickness of the edge terrain. Adjusting this will change the look and smoothness of the edge.
@export var edge_depth: float = 30.0
@export_category("Collision")
## The physics layers this [CurvedTerrain2D] is in. Curved terrain objects can exist in one or more of 32 different layers. See also [member CurvedTerrain2D.mask].
## [br][br][b]Note:[/b] Object A can detect a contact with object B only if object B is in any of the layers that object A scans. See Collision layers and masks in the documentation for more information.
@export_flags_2d_physics var layer: int = 1
## The physics layers this [CurvedTerrain2D] scans. Curved terrain objects can scan one or more of 32 different layers. See also [member CurvedTerrain2D.layer].
## [br][br][b]Note:[/b] Object A can detect a contact with object B only if object B is in any of the layers that object A scans. See Collision layers and masks in the documentation for more information.
@export_flags_2d_physics var mask: int = 1
## Turns visible collision shapes on and off in the editor. Has no effect in the game.
@export var show_collision_shape: bool = false

var polygon_2d: Polygon2D
var line_2d: Line2D
var collision_static_body_2d: StaticBody2D
var collision_polygon_2d: CollisionPolygon2D


func _ready() -> void:
	curve.bake_interval = 20
	polygon_2d = Polygon2D.new()
	polygon_2d.texture_repeat = CanvasItem.TEXTURE_REPEAT_ENABLED
	add_child(polygon_2d)
	
	line_2d = Line2D.new()
	line_2d.texture_repeat = CanvasItem.TEXTURE_REPEAT_ENABLED
	line_2d.texture_mode = Line2D.LINE_TEXTURE_TILE
	add_child(line_2d)
	
	collision_static_body_2d = StaticBody2D.new()
	add_child(collision_static_body_2d)
	collision_polygon_2d = CollisionPolygon2D.new()
	collision_static_body_2d.add_child(collision_polygon_2d)
	
	texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
	_regenerate()


func _process(_delta: float) -> void:
	if not Engine.is_editor_hint():
		set_process(false)
		return
	_regenerate()


func _regenerate() -> void:
	if !curve or curve.point_count == 0:
		collision_polygon_2d.polygon = []
		polygon_2d.polygon = []
		line_2d.points = []
		return
	
	var points = curve.get_baked_points()
	var collider_points = curve.get_baked_points()
	
	if points.size() > 1:
		points.append(points[1])
	
	collision_static_body_2d.collision_layer = layer
	collision_static_body_2d.collision_mask = mask
	
	polygon_2d.polygon = points
	polygon_2d.texture = fill_texture
	
	line_2d.points = points
	line_2d.texture = edge_texture
	line_2d.width = edge_depth
	
	if collider_points.size() > 2:
		collision_polygon_2d.polygon = collider_points
	collision_polygon_2d.visible = show_collision_shape

This is going to be the next best solution for you if you don’t want to use an inherited scene. In my opinion, they have their uses. However @MightyPhil 's suggestion of composition over inheritance is a good alternative. Node objects make implementing composition very easy.

1 Like

Of course it can. I just wanted to point out that it’s not required to contain any.

1 Like

Sorry, I misunderstood what you wrote.

1 Like

People often confuse scenes for node script classes because it’s typically the case that a root node in a scene has a script attached and that script class/name often matches the scene name. However there is no actual requirement for this nor are scenes in any way dependent on script classes. A scene is just a pre-packaged group (branch) of nodes. Those nodes can be configured in any way, each may or may not run a script.

What’s also interesting is that scenes do not actually exist at runtime in the scene tree. Once a packed scene has been instantiated and added to a tree, there’s no structure that holds this group of nodes together as a scene. The single thing that can loosely hint that a node was created as a part of a scene is its owner property, which contains a reference to the scene root node, but this can be changed at whim by user script at runtime. So there are no guarantees by engine that the owner property actually points to the scene root.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.