ES Design Question

Hi,

I am at a point where i need various timed events. Like cooldown for spells, time left for aura effects/ground effects and so on.

No my idea was to have:

Component TimeLeft{
  float timeLeft;
}

Component TargetEntity{
  EntityId target;
}

Now, after the time left is 0 a set of actions can happen. Removal of components, setting new components, creating/deleting entities for example.

Unfortunately i cannot see a clear way of how to store/implement what actions should be executed?

It seems the clean way would be add a TimeElapesd component, but that would require a large set of EntitySets just to keep waiting until the timer reached his end.

Is this the way to go? Adding logic to a component feels wrong. Like a

Component{
 ArrayList<Callable> onTimerElapsedActions;
}

For similar cases where more than pure ttl data is used, I actually use a timedEventQueue logic. (that is executed centrally by a system)

Upone time reaching it just fires an Event containing the id. So basically it just centralizes the time check logic, how the event is processed is than a matter for the subscribed systems.

This however is not anymore a pure es design

Walk us through an actual game level use-case.

Ok here is a usecase involving two timers.

A character casts a spell that requires cooldown time. After cooldown is over i would like to set the SpellCooldownReady flag.
The spell itself spawn an area of effect that increases weapon damage by 10% and stays for 10 seconds. After the time is over i want to delete the AOE entity.

Now it seems that i need an additional entityset processing routine for each action that should be executed. In the above example, one that sets the SpellCooldownReady flag, and one deleting entities such as the AOE or DamageOverTime “effects”.

Only speaking about gamelogic, it has absolutely nothing to do with visualisation

This part seems unnecessary to me. The spell is ready again at a certain time and that is all that’s needed to be tracked, right? Much less updating going on in that case.

The 'delete the AOE entity" is the classic case. I use a Decay component for this. It’s only job is to let the DecaySystem delete entities when their time is up.

I have yet to run across other time-to-live use-cases which is why I asked. Mostly, things get created. Things get deleted.

Any other use-cases?

Edit: alternately, how is the spell defined?

Personnaly I’ve created a cooldown component, and most of my ability systems get a set with an ability component and a cooldown component.

I would discourage the lambda approach, because your components should always be pure data for edition, serialisation, networing… purposes imo.

The spell casting object is not yet working. Beside the cooldown stuff i am missing another important part:

//Generic

Component Interactible{

}
//When a interactible gets triggered player adds a
Component InteractsWith{
 EntityId actor;
}

//A door for example is defined as
Component Door{
  boolean isOpen;
}

Component CollisionData{

}

Component Interactible{
}

//The system that control the door then simply can react to requested interaction

However i cannot find a way to do the same thing for spells, since it is not possible to know what actual spell-data should get created. (Unless processing a EntitySet for each available spell) That would seem like a lot of repeated code, which should not be necessary, or am i wrong?

Indeed thats why a asked here, the whole game state and interactio should be based purely on data

I’m sorry if I’m being dense but I’m still not understanding something about what a spell is.

To me a spell is an instance thing. It creates an effect in the game that may last a long time but that’s entirely up to the spell… which is code. Game logic. The exploding fireball spell may create a “this is all on fire” area of effect. It may also set something on the player (or one of its entities) that says the player can’t cast it again for a while. These are all separate things.

So, for example, let’s say you have a spell entity that is attached to the player. Essentially it’s there just like any other tool and has some ‘use’ logic associated with it. When the player invokes ‘use’ (could be Java code, could be a script) that ‘use’ code adds some entities to the world. Some of those entities may have a ‘time to live’. The spell is essentially done now, though.

As part of its ‘use’ logic, it may set a LastUsedAt component or something that it’s ‘use’ logic could check to see if it’s ready to be used again. (Spell icon displays could also use it to gray out the icon or something.)

Gamewise, I see no real difference between tools and spells and skills and so on. They will all share essentially the same working components.

I think maybe your mistake is trying to use components to do the actual ‘interaction’ logic. That’s game logic and not really ES stuff. Some things actually have to run code.

1 Like

You are right about the spell, it can be anything…

I was thinking the last hour about the problem, and it seems one of my biggest problem was that i wanted to create the whole required component set in the same piece of code. But it turns out that if i split up the creation, it allows to reuse each piece of code for various effects…

Have to implement some features to see the next limitations :smile:

I really enjoy these discussions, by the way. They either tickle some new area I hadn’t thought of or re-enforce patterns of things that turn out to be more straight forward once unraveled.

Plus, I will hit 99% of these same design choices myself. :slight_smile:

1 Like

Careful with the dark side of the granularity. In my implementation, I’ve splitted components and processors so much that there is no processor that take more than two pages of code, and most likely only one. I discovered it was too much and now, it’s a mess. I have to merge things on.

But I still think it’s a good idea to get into a lot of splitting, and then come back. This way you understand better what you really need at the end.

2 Likes

:smile:

Yes, splitting is always the problems for a Component-based architecture.

I think the “spell” along with “interval” problem are some how a gotcha which cause head-each to everyone use ES, it’s there and will suddenly happen. So again, how i translate the problem here is the need of an Event system that can play well with Entity processing system.

Locally, the Interval or the TimeLeft component of an Entity tell how much (no suprise) timeLeft for that Entity to be process by one “ActionSystem” or “SpellSystem” or “CoolDownSystem”. I say locally because the Entity shouldn’t take that info personally, but should also let those System who care about it know about info. Or the system should aware of that info… The system that need this characteristic can be implemented by an Event-base system with an interval. Down this road, there is no free thread interchangable states! So you have to decide what system are in this category and carefully build it. I build mine but using RxJava!

ThreadExecutorForEntitySystemTimer{
// Build your working threads here
// Let RxJava take care of rigging the threads and pub-sub between theme
// I use Thead and also Actor for some case, because our system involve transaction of money and commercial stuffs that already built in Actors.
}

SpellEventSystem implements EventIntervalSystem {
// Bunch of observables that represent infos and bananas
// And the intervals
}

AwsomeRandomThingIntervalComponent {
// Floats…
}

So the main part is to write the appropriate schedulers for your core systems that run those EntitySystem… I know the different mind-set between each Java developer when come to solve concurrent problem. I’m in group of developers who prefer to use libraries instead of writing one. I just go kick some shit up with RxJava to test out a lot of stragegy which resulted in best performance and easier to understand. I don’t freaking try to design first :slight_smile:

@methusalah
I love how clean ES make a big code base revolute to after migrating to ES. A bunch of tangled lines become very long long straigh lines. And hey, we should also have tools for the job: creating, grouping and spliting Component in the name of Java language, hope that the Editor of @methusalah will become one editor like that. Remember I once told you in one topic about my BluePrint system I did for my Editor :slight_smile: Not really open source but I told you the idea behind it.

The thing about splitting is that it should be for the right reasons. In general, DRY (don’t repeat yourself) is not a good reason on its own… though it’s one that gets software developers in trouble in lots of ways.

To me, it’s important to remember the main features of ES as your driving motivators. The main one I keep coming back to in design is: easily add new ‘systems’ without affecting other systems.

This implies a lot of things. One, your systems should be as independent as possible. If you require a lot of interaction between 3 different systems and it’s not in the form of waterfall side-effects then chances are that those aren’t really different systems. Whenever I see threads about “Some event framework to let my systems communicate” I always want to drill into that because it’s a ‘bad smell’ from an ES perspective. Likely, those are one system that has been erroneously split out of some non-ES pseudo-need.

Also, I like to think of the entity systems as managing the state (and low level rules) of the world… not necessarily the game logic. The fact that there is a fire and it’s burning everyone standing in it is part of the game world state and low level game world rules. That casting a fireball level 8 spell created this fire, deducted mana, took spell ingredients, etc… that’s game logic. That’s the part you code in more traditional ways.

So maybe let’s tease out the spell example a little bit and see

Let’s say we have a player entity and some kind of ‘tool/spell/usable thing’ entities attached to that player. What might they look like? I’m going to spitball for a second so hopefully I don’t design myself into a corner. (I’m going to use a sort of pseudo-groovy style for representing components to try to be clear without having to define them all.)

fireballSpell = [ActionFor(actor:player, enabled:true), Name(“Fireball”)]

This can be used to present a list of ‘things the player can do’ to the player… either as icons or whatever. It’s up to the game logic to decide what to do when it’s enabled and executed. That’s a UI level system and a player-actor level system depending on whether single or multiplayer.

In this case, let’s pretend that the fireball spell is an instant effect and just plops a fire entity down somewhere.

fire = [Position(spot on the ground), Range(some radius), Damage(type:Fire, power:10)]

We have a few options here. We could have a special system that watched just position + range and created linking entities between them. The the damage system could watch for these… but it’s an extra level of indirection that feeds our sense of DRY without actually feeding the needs of the ES.

Instead, we could have a AOE damage system that keeps track of two entity sets:

  1. entities with Position, HitPoints… no reason to apply damage to things that can’t be damaged.
  2. entities with Position, Range, Damage

The system does all of the work of either spatial indexing or not based on the scale of the game. Let’s pretend we’ll only ever have a few hundred AOEs at a time so we can just track them in the standard way.

Whenever something from set (1) moves we see if it’s entered or exited one of our AOEs. If it (say the player) enters, we attach a Damage entity to it:
fireDamange = [TargetTo(target:player), Damage(type:Fire, power:10)]

If any of the entities in the second set are removed from the set then we remove any damage objects we’ve attached to entities in that AOE. (Or set them to decay.) This is the same as if the entity exited the AOE.

So if an entity disappears from set (1) or leaves the range of an entity in set (2) or is in the range of something in that is removed from set(2)… we add a decay component to the damage entity we created before.
fireDamage += Decay(0.1) or whatever… or we just delete the entity.

Some other damage applier system is responsible for taking all damage entities and applying them to object hitpoints. The principle here is that we only want one thing modifying hitpoints… so all hitpoint adjustments whether positive or negative should be attached to the player as either damage or healing entities. So the HealthDamageSystem or whatever name has at least one entity set so far in this discussion (health application is left as an exercise for the reader)

  1. entities with TargetTo and Damage.

At an interval that the game decides (probably not once a frame but maybe) the system loops over all of its entities, grabs the entity specified in TargetTo, and applies the damage. At this time, it can keep track of any buffs the player might have, etc. against fire damage, magic damage, immunity to heat, whatever. Every interval it will keep applying damage until the TargetTo, Damage entity has disappeared from its set.

Here we have represented most of the game-level ‘rules’ for AOE spells. Lots of other systems could be added that deal with visualization and sound. All of the information is there for them. They just have to watch the proper entity sets.

Furthermore, we can damage any object in any way we choose just by attaching a damage entity to it. We don’t have to worry about how buffs are handled, whether healing is applied before damage, etc… there is some system that has already codified that set of world rules.

If we want to add a poison gas AOE then we add another damage type. Maybe poison gas also decreases visibility or something and that’s a brand new feature that can now be easily added without touching any of the existing systems. Just have a system that watches for Range and Damage of type:PoisonGas.

This approach also nicely damages any object that can be damaged. If wooden chairs and tables are in the fire AOE then they can be burned if they have hitpoints. NPCs, MOBs, whatever all handled.

“What about spell cool down?” (the original issue)
This could be done in two ways… either add another AvailableAtTime() component or create a whole other ‘entity attached to the spell’ that can be part of a system that watches those entities and enables/disables the target entity based on cool down time. The first is simpler… always go with simpler first. :slight_smile:

“What about NPCs? Do I have to keep an active spell entity set per NPC?”
Nope. Part of AI (which will be a big system on its own) will be first setting up the world state for the AI ‘brains’ at the beginning of every AI interval. So you can run through ALL of the enabled spell entities in the whole game and simply mark them as usable in the local memory for that AI entity. This is all internal to the AI system and is part of any of the other half-dozen world state gathering entity sets you might have for the AI actors. Else for a relatively low number of actors, you could just query it when needed.

Ok… that’s enough of my rambling. I’m not going to check the spelling or coherence because I’ve already wasted one can of soda on this. :slight_smile:

4 Likes

Well I have actually at least one case where I frequently need events:

Some system tries to send a status to the spaceship computers:
→ Generater just used a fuelcell
->Ammo empty for turret 00432 Ballistic SizeS

As the computer touches very many things, I opted against it polling the state itself, as this would create excessive state checking load, while the event is basically free, as I have the logic at that place anyway.

Sure, I too have events. I even have an event bus for such purposes. (For example, in Mythruna a block change is an event.)

…but these general exist entirely outside the entity system. ie: not a system → system event. That would be strange as the components are generally already enough of an “event”.

My damage system works exaclty as you described beside that it still holds a EnumMap<DamageType,Float> since some spells might deal physical and fire damage at the same time. (Might change that if it turns out to be bad)

It seems i have it running by now, well i will know it hopefully soon, when i added a more complex spell with influencing AOE effects. But at least in my mind it is working already :smile:

Any general hints left?

Is it “allowed” to use EntityData.getComponent(Id,Class) for some checks, or is that also a sign of bad design. For example

Is it a “bad smell” for one ES Implementation that have event processing along with it?

As you said, Entity is basicly enough to represent Event and Component also can represent a lot of kind of Event.
I also do that “proper way” in games and don’t “need” to communicate a lot between things.

For those who “need” communicate between “system” because “system” can be anything and be designed by a lot of people who don’t really read an ES cookbook first… or because they think it’s simplier to code that way; or simplier to adapt with editors, or to explain to non-developer that concept… well, i only have one choice that to do what ever work with less effort for the whole team.

In our game there is not too much systems but a lot of types of components. We have to limit those types to below 300. Because having too much types will make a team with more then ten devs lost: don’t know to find what Component, to do what with this System. The entities are composed with few ways but usually a Prefab somewhere and we just drag it in the level. Some entity procedure signal (they have Signal component), some receive signal (like AI stuff). Here are some stories I’ve meet over years, and it not only related to Java ES:

Our designer once mistake to add an a Signal component to a Rock Prefab instead of a LightSwitch Prefab, we have a lot of Signal what didn’t mean anything and drive our event processing crazy. AI depends on signals, result as they gets a lot of them and go very slow when it come close to specific area.

Another example is formation of troops, a bunch of entity have Signal that they get hurt and need to retreat personally. As we design one system to add “GetHurtComponent” when a character get attacked, one system that need to decide 50% of the troops are hurt to retreat the whole army. But it’s always lost in counting, because the signals fly in different threads with no overall order… That’s freaking hard to debug to find out why, and our team later decide that composing signal and trigger as Component in level design is way to hard to control.

So we build an (no suprise) EventSystem that we can: design, test, throw away, control it … and of course it can corporate with Entity and Component. Like 4 year ago, it’s started with concepts like Scope, Actor, Event, Message, Zone to wrap around entities and spatial infos. Much later after playing with ES a lot and using Java day by day I find out a way to compose “Observable” like what the Rx pattern suggest. And it’s translate directly into language of the tools, visually. The team can understand it. They know that component don’t need to be split because they procedure different smell (signal, event, message, whatever) with different interval and order… they just need to be add in Entity that subcribe to specific routine (so call scheduler system). And with tools, yes, even a non-dev can easily make a complex level with triggers, AIs and entities. That’s the story. Beyond that I still believe I have more energy for this topic. :smile:

I really WANT to find a better way. You know!

Then that’s two different damage entities (or AOE entities).

Happens all the time.

Though if you find yourself doing that once a frame for every entity in a set then it’s a sign there is a missing set. :slight_smile:

1 Like

Well, in that vein, just some random things I will say as a purist. Maybe they tickle some more discussion as it’s hard to consult on someone’s design with only a partial view.

300 components implies about 300 systems. That seems high.

It sounds from your event discussion that you are illustrating my point… again hard to say without more specifics. Systems sending other systems events implies that two systems are really one system. I guess if it’s the world sending events to the AI then maybe that’s ok. If it’s one AI system sending events to another AI system then I’d question the need to have split those. I generally see AI as one system, personally… and then only a system like the player is a system. Just another user of the world.

In the end I guess you have to do what your team supports. Harder still to stay “pure” if you all are still bumping along the learning curve as you go. I just see a lot of people make things more complicated instead of less… and with an ES that’s sometimes a bad smell, too.