Wrapping my head around 1 node = 1 script

I’m not a Godot (or a gamedev) veteran, but I think I looked for and use a similar approach to what you described (separating reusable logic into different scripts that can be combined - components) and asked a somewhat related question here so I’ll try and add my 2 cents as well.

First, I support what’s already been said by others - you can use the Node type for components that don’t need position and all that stuff. I think that all the Node type adds to the basic Object is the logic for being added to a scene/node hierarchy (which is what you need in Godot, if you want to be able to build complex entities out of components using the “Godot way”).

Now I’ll try and address some of your other concerns…

There’s a couple of ideas a had for this. One is to just search the children of an entity for a node of specific type. This shouldn’t be too slow unless you have many components per entity. I use something like this:

extends Object
class_name CompUtils

static func get_component(node: Node, comp_type: Variant):
	for i in range(node.get_child_count()):
		var child := node.get_child(i)
		if is_instance_of(child, comp_type):
			return child
	return null

Used like this:

var char_health = CompUtils.get_component(_character, HealthComponent)

Another way is to use Godot’s unique node feature which basically lets you assign a name to a node that’s unique in the scene and to get that node using that same name even if you move it somewhere else in the hierarchy (but still as part of the same scene). If it’s of interest to you, under the hood, the lookup is done using a hash map so it should be reasonably fast.

I went with the first option because in my current project I don’t expect my entities to have more than 10 or so components and, at the moment, I don’t have the need for my components not to be direct children of the root node for my entity scenes.

What I’ve also seen people do is have their “damage” component that has the collision shape that gets hit also have a property which is a health component that should receive the damage. That way, when you set up the entities scene, you need to remember to assign the health component to the damage component (you can also have a warning pop up in the editor for this), but there’s no lookup at all and you have the added flexibility of having multiple damage areas with different health.

This might not be that satisfying, but I don’t really see this being that wrong. In ECS, I’d imagine your “spinner” component would operate on the component holding the position which in Godot’s case is the root Node2D (or Node3D) node. If it was anything else (e.g. health), you’d just lookup the appropriate component in the parent. I also don’t think this is so “anti-godot” as you might think. For example, I think CollisionShape2D assumes to be a child of CollisionBody2D and registers itself as a shape. In other words, some nodes are designed to be used in combination with others. I also think a 2D component assuming it’s parent inherits Node2D is a pretty safe assumption. On the other hand, if the component isn’t for 2D specifically, the assumption isn’t needed in the first place.

Another idea that comes to mind is for the “spinner” component to operate on itself, but to put everything that needs to spin (e.g. a sprite) as children of that component. That way, the “spinner” wouldn’t have to go to it’s parent.

Finally, if it helps, I currently have a small (and so far still simple) project where a have different entity scenes with 5-6 components each. I’ve done tests with ~200 entities on screen and this worked without issues and most of CPU time went on physics and navigation anyway.

I hope this helps.

5 Likes