Entity system / class inheritance question

Godot Version

4.6.2 stable

Question

I know this question has a simple answer, but I’m wondering if it has a more complex answer.

I have a simple Entity inheritance system.

I have a Entity class that extends Node3D and all my enemy scenes have their root node as a Node3D and RigidBody and CollisionShape nodes under it.

My question is how can I incorporate this setup with my player. Currently the root node of my play is a CharacterBody3D node - so it cant just extend Entity.

So the simple answer is make the root node of my player a Node3D and put the CharacterBody3D as a child node - yes, I’ll probably do this.

But I rather like having my players root node as a CharacterBody3D.

I know theres a dozen ways to do this, but what would be the easiest way to have my player scene inherent the Entity class and keep its root node as CharacterBody3D, should I just bite the bullet and make it a Node3D? Or is there a nicer way to have the best of both worlds?

Thanks for your help, cheers.

Is… there any reason you’re using Node3D as the base class for entities? CharacterBody3D nodes were meant to be moved by code, so if you ever set the position or velocity of the entity, you should probably use class_name Entity extends CharacterBody3D.

Oohh, I thought having multiple CharacterBody3D’s was not good (I think I heard that in a video somewhere), I’ve been using RigidBody3D’s for every other “enemy” or “npc” or “entity” in the game, I’ve been working under the assumption that you should only have 1 CharacterBody3D Node in a Scene.

So using many multiple CharacterBody3D’s in a Scene is okay? Or preferred even? Are there any draw backs? I do like the sound of turning all my RigidBodies into CharacterBodies!

You can have as many CharacterBodies as you please, there are no draw backs, they may perform better than RigidBodies.

Any physics body will have trouble if it is a child of another physics body, so while you can have plenty of sibling CharacterBodies and RigidBodies you will run into issues if you have RigidBodies as children of other RigidBodies, same to a lesser degree with CharacterBodies; maybe that’s the advice you heard elsewhere.

Aahh I see, thank you for the clear explanation.

Fantastic, so my “better” solution would be. Make my Entity class inherit CharacterBody3D instead of Node3D, remove all the RigidBodie nodes from my other Entites, and turn their root Node3D into a CharacterBody3D, and make all Entity Scenes inherit the new Entity class, so the player and all entities and now at their base, CharacterBody3Ds and can be treated the same?

Is that the gist?

Actually, when defining a class using class_name and extending anything derived from Node, you can add a node whose type is whatever you wrote after class_name. Other than that, you are absolutely correct.

Get rid of the Entity class altogether.

Why would they need to do that? They said they were planning to add more enemies (presumably inheriting the Entity class).

To be able to inherit the best fitting type of node for each type of entity. Starting with a wide base class like this is not a good idea when you need to extend Godot’s node class hierarchy. That hierarchy is technical and typically won’t be “compatible” with your game’s semantic class hierarchy. Which is exactly what the OP stumbled over in the first place.

Hello there;

I did something close to this in my own project, simplified a bit. To be honest I haven’t fully settled on how it should be structured, but the general shape ends up looking roughly like this.
My base Entity extends plain Node as a composition root: it holds components and hands each one a reference to itself at bind time. Entities that actually need a physical body extend a thin layer on top of Entity, and that layer picks the body type. So the base never locks in a node type (which is @normalized’s point), while a shared Entity type still exists for @mike171 to treat everything uniformly.

public abstract partial class Entity : Node
{
    [Export] public Array<Component> Components { get; set; } = [];

    private readonly Dictionary<Type, Component> _byType = [];

    public override void _Ready()
    {
        foreach (var c in Components)
        {
            _byType[c.GetType()] = c;      // concrete type as key → O(1) lookup
            c.Bind(this);
        }
    }

    public T Get<T>() where T : Component =>
           _byType.GetValueOrDefault(typeof(T)) as T;
}

A component holds data;

public abstract partial class Component : Resource
{
    public Entity Entity { get; private set; }
    public virtual void Bind(Entity entity) => Entity = entity;
    public virtual void UnBind(Entity entity) { }
}
public partial class VelocityComponent : Component
{
    [Export] public float Speed { get; set; } = 5.0f;   // tuning, set in the inspector
    public Vector3 Direction { get; set; }              // runtime state
    public Vector3 Velocity => Direction * Speed;
}

I think they can holds bussiness logic too, so entities can stay thin and can be composited like lego pieces

public partial class HurtComponent : Component
{
    [Export] public float Health { get; set; } = 100f;

    public void TakeDamage(float amount)
    {
        Health -= amount;
        if (Health <= 0f)
            Entity.QueueFree();
    }
}

The body itself lives on the inheritance side. Entities that need one extend BodiedEntity, which exports the actual body node and drives it each physics frame. Reading the velocity component rather than the other way around, so VelocityComponent never knows a body exists:


public abstract partial class BodiedEntity : Entity
{
    [Export] public CharacterBody3D Body { get; set; }

    private VelocityComponent _velocity;

    public override void _Ready()
    {
        base._Ready();
        _velocity = Get<VelocityComponent>();
    }

    public override void _PhysicsProcess(double delta)
    {
        Body.Velocity = _velocity.Velocity;
        Body.MoveAndSlide();
    }
}

public partial class PlayerBody : BodiedEntity { }

Much simpler to just not have the Entity class. Is it really needed? What actual problems does it solve in your specific implementation?

Implementing a high-maintenance monstrosity like this to force a common base is a total overkill. Especially if you’re doing it for imaginary reasons like having everything “neatly unified”.

Fair question, and you’re right that forcing a common base just to have everything unified would be pointless. If that were the only reason, I’d drop it too.

But that’s not what it’s carrying its weight for here. The base isn’t there to unify types, it’s there to hold the component list and resolve Get() in O(1). That’s the concrete job: an entity is the composition root that owns its components and hands them back by type. Any object that needs to be queried for components needs that, and putting it on a shared base means I write it once instead of repeating the dictionary-and-lookup on every class that wants components.
So the question I’d turn it around to: if you have multiple things that all hold components and get queried the same way, where does that component-access code live if not on a shared base? If there’s a lighter way to get typed component lookup without the base, I’m genuinely open to it, that’s a real question, not rhetorical.

If it turns out only one type ever holds components, then yeah, you’re right, the base earns nothing and I’d inline it. It pays off specifically when several types share that component machinery.

How’s that relevant to OP’s question? They didn’t describe their problem domain in enough detail to suggest any kind of specific solution, let alone some pseudo ecs thing. More likely they just went with a broad base class just because “you’re supposed to do so”.

Yeah, fair, and to be clear I’m not putting this forward as the way to do it, it’s just where I landed trying to solve the same thing, and I’m still not fully sure about it myself.

On the node-type point, I think this structure actually resolves the exact thing you’re wary of. Entity extends plain Node, so it locks in nothing, no body, no transform. The physics type only gets picked one layer down in BodiedEntity, and even there it’s an [Export], not a hardcoded base. So the shared type never forces a node type on anyone.

And that same split is really the answer to mike171’s question too, maybe that didn’t come through clearly in my post. Their wall is that a CharacterBody3D root can’t also inherit Entity. But here the body isn’t inherited at all, it’s an exported reference under the entity, so the root’s physics type and “being an entity” stop fighting each other. That’s the same problem we’re both circling, I just landed on keeping a shared Entity at the top and letting the body hang off it.

The part I’m not willing to give up (and where I’d genuinely want a better way if there is one) is that shared Entity type. For me it earns its keep at the boundary, not internally, internally it’s all just components on a node. It’s when something outside needs to treat an object as an entity and call Get() that one type to talk to actually helps. Drop it and every external system has to re-derive “does this hold components, and how do I ask it”.
So I’m open on the internals, but I haven’t found a way to lose the shared type without making the outside messier. If you’ve got one, I’d take it.

Just stop clenching on the necessity of the concept of Entity, and most of your dilemmas will go away.

I don’t like to indulge in abstractions, or discussions about them, without first getting well acquainted with the actual problem domain.