Warning: long question ahead, don't be afraid, I just tried to be as precise as possible about details for who wants them but many paragraphs as skipables if you already understood what I want ;).
Context:
I passed the last few weeks learning Rust (reading The Book, writing small apps and toying), and I'd like to start building a bit bigger application, so I thought writing a game (to also learn Rust's bindings for SDL2, as I've already worked with this fantastic library).
There aren't lots of up-to-date tutorials out there on how to do that, so I basically followed the documentation. That's when I found the specs crate, which looked very interesting as I read over Internet the ECS pattern is quite good, and very idiomatic for Rust. So, let's dive in! (previously I was writing games in python where ECS isn't much known – at least as far as I saw)
So, I create the basic components, such as Sprite
(for every entity that needs to be printed), Position
(self explanatory), Hitbox
, KeyboardControlled
, Mass
, Speed
…, setup some Keyboard
and Physics
systems (I don't include any code as everything up to here is very self explanatory, and my implementations are all three bugged, standard and very simple, and as so quite redundant with the name).
Ok, now I have a nice little sprite (a rectangle) which I can control with Left/Right. I would like to add some ground so it doesn't fall forever (there is some gravity), but I'm stuck in finding any appropriate way of doing so. I would like my whole ground/level as well as static objects to be somehow efficient (I don't want to check collisions against the whole world for each entity in order to see if it's falling).
The question: what is the “good” way to do so? I think that every part of my level should be entities (so at least I can load/drop them dynamical), but if so, how can I control how they are stored? I mean, suppose I had a marker component for walls Wall
or stuff like that, then I could try to match every entity that has a hitbox against walls, like that:
// data is (WriteStorage<Hitbox>, ReadStorage<Wall>)
for (entity,) in (&mut data.0,).join() {
for (wall,) in (&data.1,).join() {
// compute collision, eventually change push the hitbox out of the wall or what so ever
}
}
But this is so inefficient: O(m·n) where m is the number of hitboxed entities and n the number of walls, whereas in python I would probably do it with HashMap
s (~O(m·k/n) time to retrieve colliding objects where m is the number of hitboxed entities, k is the size of the hitbox and n the size of walls), or with any decent search tree, with witch I can get something like O(m·ln(n)), where n is the number of walls, and this is just applying standard databases (one can memoize data from one query to the following ones, and get super fast colliding algorithms for bunch of objects).
But algorithms are not really the concern here, my question is: what is the idiomatic way of doing it? Where/how should I store all of that? Should specs
do it for me, and if so?
All I could find in doc is to specify the type of collection to be used to store entities, and then to retrieve them when I do the computation, but I couldn't find many examples or explanations so I am unsure about this solution.
I specified I coded in Rust because, as a novice, I would enjoy any tip or reference to specific crates/docs, but feel free to answer the question on a more abstract level, even if you know few or nothing about Rust, but you are still used to ECS.