EnTT: Gaming meets Modern C++
github.com
github.com
EnTT has an eventing system as well. These can be triggered in various ways (entity created, entity deleted, component assigned/removed, etc). I've used eventing to control fine-grained compilation pipelines. For example a recent scheme compiler I wrote used events to simplify inlining (both define-macro/define-syntax and straight procedure inlining) and quote/quasi-quote/unquote/unquote-splicing translation.
If you spend a lot of time writing compilers, I find an ECS like EnTT can give you extra flexibility during the design/prototype phase. Try it out on a simple prototype and see if you like it.
With that said, if there is interest, I'd enjoy building a clean room compiler for an existing language using EnTT and other techniques I've developed over the years. Thoughts? While I absolutely adore TinyCC, it might be worthwhile giving that space another go?
(slightly off-topic, but I'm excited to have a venue to ramble a bit about this:) ECS is really cool, and I'm excited about using it more for my games. The "cheap" (de)serialization by moving all state into pure, data-only components is really fascinating - I've been playing around with building networked multiplayer games for a while now, and I'm currently experimenting with rollback netcode.
A key part of rollback is saving your state every frame so you can load it if you need to roll back, and ECS makes saving/loading easier to reason about, since you can "just" grab the components and their associated entity IDs and load if needed (EnTT, for the record, has an API for this[1], though they leave the actual (de)serialization up to you). Of course, JS's lack of a memcpy equivalent makes this much harder than what you could do in C++, which has lead me to experiment with immer[2] in my ECS, which uses structural sharing to avoid mutation, so you can get a "copy" of your state by just keeping a reference to it, as future updates will make new objects. This, of course, theoretically could make a ton of garbage (e.g. updating your position every frame would create a new Position object every frame), which is not great for high performance games. I'm not sure how bad this will be in practice - JS GC is relatively smart and fast these days, but I haven't tried doing much beyond little pong or platformer demos yet. I'd also imagine that, like, doing a deep clone of my state tree every frame (or doing the whole JSON.stringify/parse dance if I stick to primitive values) probably generates just as much garbage. Maybe if I could integrate immer with some kind of object pool it'd avoid these issues, but I have no idea how useful object pooling is in practice in JS...
[1] https://github.com/skypjack/entt/wiki/Crash-Course:-entity-c... [2] https://github.com/immerjs/immer
I'm pretty sure most JavaScript games use object pooling extensively. GC is definitely an issue if you want to hit 60fps. Even for non-games actually.
You don't have a lot of ms
2) Is it really necessary for anything beyond a competitive shooter with bullets flying around? Outside of the bullets, fighting games are as or more precise than the average shooter and 60fps continues to be the standard there and I don't think >60 tick rate would bring any big value to a fighting game. A 60fps game with no buffer can already yield situations that are close to or beyond human execution (see: Melee)
I know Ecsy[1] is using it, which makes sense because that's targeted at apps building for WebVR which really cannot afford GC pauses, so I guess it must have some merit.
I guess I could try monkey-patching immer to have it pull from a pool when creating new objects? Theoretically if it just used nominal typing (re: classes) to pick objects out of the pool and overwrite all the fields on it, as I assume it normally would do with a brand-new object, this could work out okay. Could just do pooling on the top-level component objects and deal with GC on any nested objects to simplify things (especially because nested objects probably wouldn't have classes associated with them).
[1] https://ecsy.io/docs/#/manual/Architecture?id=components-poo...
[1] https://photonstorm.github.io/phaser3-docs/Phaser.GameObject...
Some engines I used to work with would assert() on malloc/free if it happened outside certain safe regions of execution.
https://github.com/andyhall/ent-comp
In mine I store each entity's state for a given component as an object (e.g. `{ mass:1, velocity:[0,0,0] }`, so the internal storage of the ECS for a given component is an array of such objects. To really optimize for the "cache and rollback" kind of behavior you're talking about I guess it would be ideal for the internal storage to be flat arrays of numbers, but at first blush it seems like that would make the implementation of the ECS itself kind of hairy.
Not a lot of technical details, but this video has a great general overview https://www.youtube.com/watch?v=W3aieHjyNvw
Feel free to ask if I triggered you and you want more details. ;) Reach me out here, on gitter, discord, by mail, whatever...
// p={(x0,y0),(x1,y1),...} and v={(vx0,vy0),(vx1,vy1),...}
for(int j=0;j<2*SIZE;j++)p[j]+=v[j];
vs. // pv={(x0,y0,vx0,vy0),(x1,y1,vx1,vy1),...}
for(int j=0;j<4*SIZE;j+=4)
pv[i+0]+=pv[i+2],pv[i+1]+=pv[i+3];