boskid wrote: Thu Jul 16, 2026 6:26 pm
If i am reading this correctly, you are assuming that all entities do collision checks with anything else all the time.
Oh no, no-no, of course not! What I meant by that is when, say, a tank moves across terrain, each tick you perform a check if an entity collides with anything. As you said, "when entity is moving". This means that current state of things is
"entity moved -> collision checked" --
this is what I meant by "
already happening anyway". So far so good, right?
The thing that I do
not know, though, is if you're performing collision check once for every collision layer of the moved entity, or is this somehow optimised? What I mean is: a tank can collide with a biter, with water, an assembler, a player, or another tank, and since water tile isn't an entity, it's probably not returned in filtered entities list?... So I assume you're performing a separate check for each layer?.. Or at least two checks: one for entities, and one for terrain collisions, like water (again, I'm guessing here)?...
Now, you said, "collision check will cancel movement and may trigger entity damage" -- and here's where my suggestion fits in: when collision detected on a special layer (
just like water, but invisible, virtual layer) instead of cancelling movement, fire an event. Just for collisions that happen on that layer. That's it. Really. Sounds simple enough?..
boskid wrote: Thu Jul 16, 2026 6:26 pm
Collision checks have roughly the same performance cost as running `surface.find_entities_filtered` because collision checks are searching for other entities that occupy the same space while having matching collision layers.
Yet collision checks happen at the engine level, which is by definition faster than calling `surface.find_entities_filtered` on the LUA level, is it not?
Oh, and water tile isn't an entity (at least I hope it isn't

), so just as a side note: you don't really need to retrieve a whole entity that matches the intersection box from the registry, just to know if collision have happened. You just need to know if there are any overlapping coord ranges, or even a bitmask (or, rather, thirty two 32-bit-sized bitmasks), as an early bailout. And only retrieve entity if there actually
is something to retrieve at these. But I digress. You said:
boskid wrote: Thu Jul 16, 2026 6:26 pm
Opposite would be that entity would register only on a small area but the turret would be doing search over large area, which would have to be an area 64x64 which is also expensive if it was to happen each tick.
That's actually what I thought was happening after reading this thread:
viewtopic.php?t=101153!

Though I admit, that was pretty silly of me to think it stayed that way for this long.
But still, I saw that a mine checks for entities every 10 ticks and I thought "huh?.. but why??.." - in my defence, it not clear if mines check for entities every 10 ticks all the time? Or only if there is a hostile force within 1-chunk radius? Or only if there is a hostile entity within `trigger_collision_mask`? Does the mine scans even if there is nobody within the `trigger_collision_mask`? How does `trigger_collision_mask` even works then?.. And what's it for? So many questions, so little answers...
All that makes for a huge difference, and it isn't clear from that conversation what's actually _is_ happening under the hood, especially considering the age of that thread.
So, as I said, with the lack of knowledge about inner workings all we can do is guess...
boskid wrote: Thu Jul 16, 2026 6:26 pm
If a turret was expected to check for enemies nearby using collisions, if the range was 32 tiles, then unit movement would have to register its presence on an area about 64x64, and this would be happening every tick entity moves.
Is that's the current implementation of collision detection? That's... rough. Ok, at this point perhaps it's better if I explain in more detail what I
thought is happening during entity movement and collision detection process.
I
thought it's pretty straightforward: I thought there is a bigger registry of boxes for static objects (infrequent updates, lots of data), and a smaller registry for entities that can move (frequently updates, smaller dataset). When entity moves, it's own collision box is checked against both registries. There are many different ways to organise it, of course: some are slow to write but fast to read, some are fast to write but slow to check, some are balanced, some are just plain O(n2) bananas. In any case, I assumed this check is simple and fast. Now, reading your answer with "if the range was 32 tiles, then unit movement would have to register its presence on an area about 64x64, and this would be happening every tick entity moves", this doesn't look like simple. This looks very opposite of simple, tbh. Maybe it's blazing fast, I don't know the implementation details, but it definitely doesn't look simple.
So, what I thought
would happen in the context of my suggestion about trigger collision box/layer:
Let's say there is a turret belonging to player A and a tank belonging to player B that is moving towards the turret.
Tank has a trigger collision box of 3x3 tiles (I don't remember the exact values, so let's assume 3x3 merely for example purposes).
Turret has a trigger collision box of 64x64 tiles.
Tank moves towards the turret.
Tank checks if it's own trigger collision box intersects with any other trigger collision box.
Finds none.
Updates own box info in the registry.
Tick.
Tank moves closer.
Tank checks if it's own trigger collision box intersects with any other trigger collision box.
Finds none.
Updates own box info in the registry.
Tick.
Tank moves more closer.
Tank checks if it's own trigger collision box intersects with any other trigger collision box.
Check detects that there is, indeed, an intersection detected. No object info yet, just a boolean confirmation that
some intersection exists.
Only then tank asks for the actual object whose trigger collision box intersects with tank's one, updates own box info in the registry, notifies that object via event: "Hey, I'm here!", and notifies the LUA subscribers for the corresponding event with appropriate message and payload.
Turret activates and starts scanning the surrounding area (because, well, boxes are rectangular and turret radius is circular).
As for the collision registry itself, again, I don't know the details of the current implementation, but this is something I've used in the past, and it proved to be extremely efficient for both writes and reads (sub-ns read/write and mere <20mb of memory for a 10k*10k map with thousands of units): so, the turret's box spans over multiple chunks. Each chunk is a grid of 32x32 tiles. This grid can be represented as a list of 32 bitmasks, each bitmask is 32 bits long. Each bit is a tile. I know that actual collision boxes (masks) are float-based, but for the purpose of "check fast, bail early" it can be denormalized and stored in the form of bitmask as merely an indicator of "is there
anything at all in that tile worth checking?". There are 3 registries: smaller one for moving objects that updates more frequently (a plain spatial hash with tile int coords as keys and array of objects as values), same but bigger one for static objects, and another bigger one for static objects but bitmask-based. Yes, this adds an extra step to the "registration" process (I assume that by that you mean "changing the collision box entry of the entity in the collision boxes registry" or something like that?...), but for static objects it happens only when they are placed. And it also makes it significantly cheaper to check if there is
anything to collide with in the first place. Only when there
is, we get a list of colliding candidates from the spatial hash, and then compare their collision boxes at a float level -
which happens orders of magnitude less frequently than a basic anything/nothing check. With all that said, when turret is placed, quickcheck-bitmask-map is populated with "1" for tiles where the turret's trigger collision mask reaches, and the rest kept as "0" (or whatever it was before turret placement). When tank moves, tank's trigger collision box is converted to a set of bitmasks too, then checked against the surrounding quickcheck-bitmask-map lines. This should typically compile to a single CPU bitwise AND instruction followed by a zero test for every 32-bit long bitmask. 3 rows (tank's size), 6 CPU instructions. Slightly more, if tank's coords span over multiple chunks. Sounds fast enough?

. If & returned `0` - then you already know you didn't collide! If not, only
then we get the object from spatial hash map, and compare it's intersection between the trigger collision box of the tank and trigger collision box of the turret - for float-level precision.
Anyway, I hope I made my suggestion at least a little bit clearer

With all that said, do you still think my idea is not beneficial in any way?

(
though I do see at least one benefit in any case: it would finally give modders a reasonable way to tell when an entity arrived on a specific location! 
)