Entity System and “composite” entities

I'm new to the Entity Component System pattern and there's something I cannot figure out. Let's say I have the following entity:

Player: {
Components: [
Position: {
x: 0,
y: 0
},
Health: {
max: 100,
current: 50
},
Sprites: { ... }
}
}


When I render an entity that has a Health component I also would like to render a health bar above that entity. Do I treat the health bar as a new entity and attach it to the Player i.e., the Player becomes a composite of entities; or it [the Health bar] should be part of the Health component, in which case how would it be rendered / created?

To illustrate what I'm trying to say:

Player: {
ChildEntities: [
Character: {
Components: [
Position: { x:parent.x, y:parent.y },
Sprites: { parent... }
]
},
HealthBar: {
Components: [
Position: { x:parent.x, y:parent.y },
Health: { max:parent.max, current:parent.current }
]
}
],
Components: [
Position: {
x: 0,
y: 0
},
Health: {
max: 100,
current: 50
}
}
}


Is there an idiomatic / preferred way of doing this? Thanks.

You are possibly conflating two separate things. I'll explain them below, but first I'll strongly note that you should spend some time playing around with existing game engines and get a sense for how they solve problems before trying to write your own game/engine from scratch.

Transform Hierarchies

Most engines have some form of hierarchy of transforms, aka a scene graph. In this case, a "child entity" would store its position relative to that of its parent. In this way, complex game objects made up of multiple sub-objects can move around in the world in a unified way.

In this case, you might attach the health bar object to the player object but give the health bar a (relative) position of [0,+10] so it float over the character's head.

Such a hierarchy is almost exactly what you have with your Player / ChildEntities.

Observers

The other useful concept is that of an observer for a component or an object. This essentially means that object A is able to see and inspect another object B's properties.

In this case, your health bar object wouldn't have it's own health component but would instead have a property that denotes which health component to observe.

Exactly how observing works is it's own topic. You could just store a handle/reference and manually query properties, or use an event system, or use a reactive flow, or allow all three and select on a case-by-case basis.

Regarding binding the observers, doing this right is somewhat tricky. You could have the health bar contain the component/logic to observe the character, or you could have the character contain the logic to attach the health bar as an observer, or you could have a third-party controller that makes these decisions. Again, in the real world, you'll probably have all three approaches used throughout your code selected on a case-by-case basis.

Here's one way to go about it.

Let's make the HealthBar its own entity and take advantage of the fact that entities can reference the same component instances.

So instead of setting up a parent/child relationship between the Player and HealthBar, we'll have them share components, in this case, the Position and Health components as seen below:

// create the position and health components
var pos = {x: 0, y:0};
var health = {max: 100, current: 50};

// create player entity, referencing position and health components
var Player = {
Components:[
Position: pos,
Health: health,
Sprites: {...},
...            // other components that define a player entity
]
}

// create healthbar enitity, referencing position and health components
var HealthBar = {
Components:[
Position: pos,
Health: health,
Sprites: {...},
...            // other components that define a healthbar entity
]
}


With this setup, whenever the player entity takes damage there will be a system in place to update the health component accordingly. Since the health bar has a reference to the same health component it can later use that information to update its state before its rendered.

I will admit that I'm making something of an assumption about your sprites. Most graphic APIs I've come across have some kind of scene-graph where Sprites are containers that have their own coordinate space. So while two Sprites may have the same position, their contents (which can be other Sprites) can be positioned relative to the origin of that container Sprite. So, in this case, the position component is shared between the two entities so that when the Player's position is updated the HealthBar's position is updated as well but the graphic that makes up the healthbar will be positioned within its sprite container so that it does not obscure the player sprite.