Do you think there's a better way of doing building placement than this @xyz or have I just managed to code myself into a corner because it's an RTS I'm looking at and they're just inherently quite tricky because of the way you have to grab data? I feel like if I switched to 3D now instead and just got some drag select code I'd probably be done in five seconds but I'm stubborn because I like 2D RTS'.
Having Area2D overlap with a specific tile cell ID?
- Edited
Lethn This is fine for placement. There is manual iteration through tiles but it's one off and I guess there's not that much tiles a building can occupy. Despite it looking complicated at first sight, it's actually quite straightforward.
Btw doing drag-selection in 2d with tilemaps is much simpler than doing it in 3d. The code I posted is in fact all you need.
- Edited
Right, so as I understand it, you're getting the tiles using a rect within a rect that goes across all the tiles? That is a fairly straightforward concept but I am being so triggered by the code you have to do to achieve it.
Lethn There's nothing to it really. It's just a couple of transformations from one coordinate system to another. I probably should have written it in more educational form . Like this:
func get_tiles_in_rect(rect: Rect2) -> Rect2i:
# rect is our input world-space rectangle
# reinterpret rect as min/max (topleft/bottomright) points
var pmin = rect.position
var pmax = rect.position + rect.size
# transform min/max points to tilemap local space
var pmin_local = to_local(pmin)
var pmax_local = to_local(pmax)
# transform min/max points from tilemap local space to tile index space
var pmin_tile = local_to_map(pmin_local)
var pmax_tile = local_to_map(pmax_local)
# reinterpret min/max points as rect
var pos = pmin_tile
var size = pmax_tile - pmin_tile + Vector2i(1,1)
# return the rect
return Rect2i(pos, size)
LOL thanks I think I mainly just need to do the programmer thing of breaking it all down individually, what I need to do is study the TileMap functions mainly because it all reads like gibberish to me even by programmer standards. I think I can explain better now I've cooled off why it annoys me so much, I'm seeing these functions and looking at them based on standard programming knowledge but they do something completely different to what I'm expecting. local_to_map as an example I was thinking of local co-ordinates, but they are in fact telling you it's something else in the documentation. Same deal with the whole get cell ID thing, it doesn't just blindly grab the ID of the tile collided with like I was expecting it to, you have to declare the layer and the co-ordinates of the tile first but I'm slowly working through that in my head now that I don't have a headache and I've got more patience.
- Edited
Lethn Take your time. I too think TileMap'a api is kinda jumbled. It's hard to design a simple class interface for such a complex object though.
Just to address the specific things you mentioned.
local_to_map()
does exactly what the name says. It converts a point in local coordinate system into tile "indices". If the TileMap object has zero transforms its local coordinate space will in fact be the same as the global coordinate space.
Second thing, if you call get_used_cells()
from within body_shape_entered
handler, you're still calling it on the whole TileMap object. Receiving the reference to that object as an argument doesn't create any special context. It doesn't isolate collided cells only, becuse that's not what get_used_cells()
do. No matter where you call it from - you'll always get all of the used cells.
- Edited
Now looking at your code this is why I was convinced that as it turns out data layers may well be the better option, the reason being is it allows you to identify individual layers without the absolute mess of code potentially, I may explore that since you've explained to me why get_used_cells() isn't working how I'm expecting it to. My thinking is having the Area2D react according to the data layers it collides with and I can use a simple boolean check for that, then if there's anything in there that you can't build on have the outline turn red and you can't place the building. Will poke at this idea and see what happens, if not I'll default to your idea with using the rects.
Unless I'm misinterpreting the video I posted badly, it seems that this is the sort of thing that data layers was specifically designed to address. What I'm thinking is instead of using the tilemap functions, I can also grab the individual cells through the appropriate data layer then do a for loop check for anything that can't be built on and that can also deal with the problem I've run into.
- Edited
Not sure custom data layers can help you there because they have nothing to do with collisions. If you want only the specific tiles to generate collision signals you can try the following:
- create a new physics layer for the tileset
- assign an unused collision layer number to it and set its collision mask to match the collision layer of the area
- create collision shapes on that new collision layer for tiles you want to generate the signals
- add tile's collision layer to area's collision mask
Now the area will emit entered/exited signal only for tiles that have collision shapes in that physics layer.
More reeeeeeeing from me.
- Edited
It may be more difficult to do the initial maths of box selection and such in 3D but if it means I don't have to deal with the silly TileMap nonsense I am seriously considering it lol. At least until the back end devs maybe patch up the TileMaps to make them easier to use for individual cell interaction.
- Edited
This is possibly the most hacky solution ever devised but another thing I've thought of which I could do is maybe create a rectangle shape for where the tilemap can be built on and use the tilemap purely for basic navigation and physics collisions. Which after all this nonsense is probably the better solution than trapping myself into dealing with individual cell interaction. The only issue is it relies on the tilemap being static but I think it will work for what I have in mind.
I have to wonder if this in fact how the older RTS games ended up doing it but I'm not sure. I do know after some extensive testing as an example of playing a round with their AI they take into account starting positions etc. rather than reading off individual tiles and I think they do distance checks on individual units too. For things like slopes I could define some custom collisions as well and have them set things like unit height that way.
That's an interesting method, will need to experiment.