Ecs and Timers

Posted on
ecs

Entity Component Systems are great

but, how do you make timers in them? First, a plug: https://github.com/Leopotam/ecs Great C# ECS library there. No generator, fast, small (if I was smarter, it feels possible to read the code and understand it in an hour or so). Back to timers.

What I did that I thought was good

Back then I came across someone talking about using a many to one architecture for timers. Timers would be an entity with a TimerComponent. The TimerSystem would tick down the remaining time field within the TimerComponent, then when it’s done; the timer would do something to the targeted entity. It sounds pretty good; great, actually. Let’s see what this looks like with systems and components.

TimerComponent/TimerSystem - Ticks down the elapsed time and figures out when to do whatever the timer is supposed to do.

How do we store what the timer is supposed to do when it’s complete? Well, I guess you could just have a function be data. Is it data? Can I just put a fatty Action in there? I think if I took this route I would think that this approach was cleaner. Except, I think having to debug this turns into a shit storm. (I’m not too sure.) So, I decided for the other choice: have a component and a system for each thing that the timer can do.

CooldownTimerComponent/CooldownTimerCompleteSystem - Now, the CooldownTimerComponent would be added to the timer entity also. The CooldownTimerCompleteSystem would tick on entities which have CooldownTimerComponent and — Oh, I forgot about the new challenger:

TimerCompletedComponent - Added to the Timer entity by the TimerSystem when the timer has reached 0.

Ok, so that guy gets added to the entity, then the system we were talking about a few lines up ticks on these two. The tick ends up doing whatever it needs to do to the entity that it targets.

Now, what if the entity is destroyed before the timer completes? We have to handle this situation. Since id’s are pooled, I can’t just use world.IsEntityExists since it’s possible that the entity could die then come back.

I ended up adding an AttachedTimersComponent with a HashSet<int> that would contain any timer aimed at it. Then, of course, there is an AttachedTimersDestroyedSystem that would tick on AttachedTimersComponent and DestroyComponent and iterate over the hash set adding DestroyComponent to every timer.

Ok. It works. Now to create one of these timers. Let’s see. Add an AttachedTimersComponent to the entity if it doesn’t already have one. Create an entity with a TimerComponent and a CooldownTimerComponent. Add the entity to the AttachedTimersComponent hash set. We’re good to go.

What. It works, why don’t I use that anymore?

I’ve reverted to abandoning DRY in this case and just having timers in components. For example, the gun has a cooldown field. The GunSystem or GunCooldownSystem will tick the timer away. Every system will handle ticking its own timer fields. That’s it.

Is there anything better?

Is there? I think there is. It feels like there should be. I think this is one of those cases where I don’t know enough about programming / the language that I don’t know what tools are available to make this experience better. It almost seems like I should be able to do things like target what component to remove upon the timer being elapsed by being able to store the component type to be removed in the field of the TimerComponent. This way, I could add a CoolingDownComponent that doesn’t allow the gun to shoot. A ReloadingComponent that also doesn’t allow the gun to shoot, but also doesn’t allow it to reload. My solution would be a switch statement and an enum in the timer component, but that’s disgusting for obvious reasons (That should tell you how shitty I am.)

Conclusion

Hm. Maybe I should have tried to be cool and put actual code in there to give a taste of the Leopotam.ECS api. I’m still not sure what the purpose of writing these things down is. It’s a break from coding or an excuse to say I did something gamedev related.