ES Design Question

That that would imply that i also need one system to create the actual damage dealing entities for each damage type?

EntitySet<AOE,Fire>
EntitySet<AOE,Physical>

Currently if have:

EntitySet<AOE,Damage> //Damage contains a EnumMap
that creates 
Entity[ApplyDamage(float damage,Id target)] //One entity for each type of damage and for each target in range

300 components implies about 300 systems. That seems high.

Yes, around 200 for a “not so big” RPG game. For composing purpose, all kind of Assets: Positions, Model, Animations, Physics, AI, materials and lighting attributes are all components. Usually this kind games have various type of triggers, vehicles and character’s behaviours add up because they are all components too. A lot of them are Prefab that already made and use in a few games. For new game we create around 20-30 more, but aware of unnessesary duplication. Some team have big problem using multi resolution assets as they duplicate every kind of Components to several versions and pollute the library really quick. Later we have a build system that move multi resolution configuration out of Components.

I really appreciate your time to reply, but honestly I don’t really need a quick consult right now, but certainly will back with it later in more proper way. Don’t want to hijack every topic without informative and contructive words.

In the articial intelligence’s behavior tree, I check the existance of components almost all the time since there is no contract (for now) between the two mechanisms.

I don’t think it is bad but surely, if it is present in a lot of different situations, then it may be the sign of a dangerous design.

For my part, I always think about system/component dependancy : “Is there a valid reason, beyond my actual technical issue, for the system Sx to know about the component Cy?”

No, in my example you’d have one entity set with some entities that had Damage(Fire) and some that had Damage(Physical). You were sounding like you had one Damage(Fire, Physical) and I’m saying that in my approach that’s two different entities (same component type).

The AOE system can deal with both of them the same since it’s only job really is to pass those on to things entering and leaving the ‘range’. Anything wanting just Damage(Fire) or just Damage(Physical) can still filter on just those.

This then becomes:
Entity[TargetTo(target), Damage(Fire)]
Entity[TargetTo(target), Damage(Physical)]
…if something enters an area with both of those.

Else you will have a real problem for AOEs that overlap slightly as you will somehow have to merge components. And two systems arguing over the same component is a big no-no.

By the way, recently I added WatchedEntity to Zay-ES. It’s only tangentially related to this conversation but it lets you basically have a single-entity set… but the entity can also include a whole mess of components that will just be null if it doesn’t have them. (Versus set membership that is all or nothing.)

This is very useful for player entities where you often just watch the player and a handful of components in different situations. (That’s where it’s related to this conversation since previously I would either create ugly one entity sets or I’d do a lot of getComponent() calls.)

It looks like this
EntityData:
public WatchedEntity watchEntity( EntityId entityId, Class… types );

/**
 *  A specialized entity that can be updated like an EntitySet,
 *  thus effectively providing an EntitySet of one.  This is useful
 *  for monitoring a single entity as one would monitor a set since
 *  there is no way to filter an EntitySet by a single ID.
 *
 *  <p>Unlike the entities in an EntitySet, this Entity can actually
 *  have null values for some components depending on their current
 *  state.  The WatchedEntity is watching a specific ID with a subset
 *  of its components but is not using the components for any kind of
 *  "membership" inclusion (like EntitySet does).</p> 
 *
 *  @author    Paul Speed
 */
public interface WatchedEntity extends Entity {
    
    /**
     *  Returns true if this entity has changes
     *  ready to be applied.
     */
    public boolean hasChanges();
    
    /**
     *  Applies any accumulated changes to this entity since
     *  the last time applyChanges() was called and returns true
     *  if changes were applied.  
     */
    public boolean applyChanges();
    
    /**
     *  Applies any accumulated changes to this entity since
     *  the last time applyChanges() was called and returns true
     *  if changes were applied.  Changes that caused an update 
     *  will be added to the supplied updates set.
     */
    public boolean applyChanges( Set<EntityChange> updates );        

    /**
     *  Releases this entity from processing further entity
     *  updates.  After this call hasChanges() will always return
     *  false and applyChanges() will do nothing.
     */
    public void release();
}

It would be nice if you could look over this test case quickly. Just to make sure i at least got the basic flow right. It still feels wrong to me. Seems i cannot wrap my head around this. The multiple damage types thing complicated the stuff by a large factor. And looking up 1-n relationships feels awful performance wise

Yeah, but that’s because you have a parent child relationship where none is necessary.

You have two AOEs, not one. They just happen to be at the same spot.

So i would have to trigger two AOE effect. It seems that at some place i need the relationship in any case, be it at the trigger level, or at the creation

What would that relationship be used for? Once the AOEs are created, why does it matter where they came from? At any rate, it seems there is no reason to have that relationship matter when applying damage entities to entering/exiting entities.

Perhaps we are still talking about different things.

I am not talking about the damaging itself, but about the creation of the actual damage dealers.

I need the relationship to spawn AOE damage dealers with more then one damage type for example. Like trigger one spell that spawn two AOE’s like in the example above

Something is still disconnecting.

public void castTheFooSpell() {
    create AOE #1
    create AOE #2
}

Done.

Mhmh. That would work of course. I guess, i really cannot use a pure component driven design.

It’s what I’ve been saying all along. Something has to be doing the game logic… might as well make that part as simple as possible.

Yes, you could have some spell entity template thing that when invoked simply iterated over the AOEs it knows how to create and creates them. So you’d have RangeTemplate or some other non-real-component so that it wouldn’t show up in your regular AOE sets.

Or you can just implement the ‘create the entities’ logic directly and not try to make all of that automatic. It’s more trouble than it’s worth I think.

…then again, my 'create the Foo Spell" would have been a groovy script so it’s easily changeable anyway.

1 Like

I think I had a similar problem with the upgrading of my firing ship in my invader like game. I wanted that have one, double, triple and quadruple bullets. But a bullet was still just a bullet and I didn’t what to have components for this one, double, triple and quadruple shooting. So I handled the bullets always the same I just had a game logic which did spawn the wished amount of bullets nicely formated :smile: So this generation is I suppose that what @pspeed mean by simple game logic.
I planed to have then different bullet types, but spawning I still would have to solve with some kind of game logic not with the ES directly. The ES just handles those bullets no matter who or how they are spawned.
I suppose for your spells it is the same you need some game logic outside ES to spawn them, once spawned, your ES systems can deal with it (visual, damage, heal, what so ever).

And I like that kind of items management with those systems, components and entities, it feels sometimes like a magic trick to me when I see the result :smile:
Beside TDD this is one of the things which brought back the old days feeling where I hacked on a ZX81…

Maybe an exemple will help?

(Note, I call systems ‘processors’, it helps me)

The projectiles of my ship are entities. Here is their blueprint (each line is a pure data imutable component) :

When the CollisionProcessor (reacts to Physic & CircleCollisionShape & PlanarStance) detects a collision, it put a Touching component on my projectile.

Look at the four last components (xxxOnTouch), I have one system for each :

  • DestroyedOnTouchProcessor
    (reacts to Touching & DestroyedOnTouch)
    Removes the entity from the entity data

  • EffectOnTouchProcessor
    (reacts on Touching & EffectOnTouch)
    Creates a new entity with the effect contained in EffectOnTouch component (a particle emmiter in the exemple).

  • ShockwaveOnTouchProcessor
    (reacts on Touching & ShockwaveOnTouch)
    Creates a shockwave entity at the collision point stored in Touching component. This entity will push nearby physical entites away for a few milliseconds.

  • DamageOnTouchProcessor
    (reacts on Touching & DamageOnTouch)
    Grabs the touched entity ID in Touching component and creates a new damaging entity with this entity as target. Later, the DamageProcessor will manage this new entity and, if the target has the Attrition component, it will apply damage to it.

The projectile itself don’t know anything about what happen to the effect it triggers. These effects are autonomous.

2 Likes

Sorry for bumping this.

@pspeed Just wanted to thank you for this great explanation. Every time I read this post I understand something new from it. :slightly_smiling_face:

2 Likes

Totally agree, I love all those ES Design discussion threads. I got the idea meanwhile though, but as you said, still find a gem here and there re-reading those threads. And sometimes understand something the first time, because before I lacked to much to even get what I or someone else was told to do :smiley: I sometimes just nodded and thought wtf :joy: because it sounded like totally obvious, but was not.

1 Like

Thanks. Glad these still resonate with folks.

I sometimes wish there was a list of all of my “good” ES advice so that I could reread them myself. :slight_smile: (Or consolidate them into a single document or something.)

1 Like