Trying to detect mouse position that is within a large building tile

Godot Version

4.4.stable

Question

I am trying to create a “demolish building” mechanic, but I had limited success. The building does get removed, but only when I click on the center of the tile. So if I click within the building but away from the center, the building doesn’t get removed. Below is the video showcasing my issue (notice that when I click around the center of the building, nothing happens, but the building gets removed only when I click on the center):

This is the code I have (all buildings are “built” in the buildings tilemaplayer):

	if Input.is_action_pressed("click"):
		buildings.set_cell(mouse_position, source_id, building_vector)
		if source_id == -1:
			buildings.erase_cell(mouse_position)

The gscript is attached to a different tilemaplayer. I was able to change the shape of the hitbox in the physics layer:


Is it possible to detect if the mouse position is within the hitbox? If so, how? And if a player clicks within the hitbox, how do I use that signal to remove the building? I’m not sure the “erase’-cell” would work, since it needs the exact tile coordinates of the building.

I would also like to know if it’s possible to track how many of each specific building is “built”. As in, track how many solar power plants, how many wind turbines, how many oil power plants, etc are built. If so, how would I go about tracking all this?

Hi, for what I can see on your video the tiles on the map are very tiny compared to the sprite. The sprite is only placed on 1 tile on the center even if the graphic overlap multiples tiles, that’s why you need to click on the center tile.

For the question about the building count I would simply create a count var in my global script and add 1 each time a building is created and remove 1 when destroyed.

If you are not familiar with global scripts, I would highly recommend you search for this topic : global script or autoload. It is a script accessible from any other script where you can set variables and functions etc.

Understood, but if I want a building to be placed over multiple tiles, then how do I resolve my issue? The buildings I wish to have in my game will be in a variety of sizes.

I dont know enough of tilemap scripts to help you with that but I’m very curious about this problem. You should show us the code to put the building on the map so someone else can help.

My guess is you would need to use the hitbox of the moving node instead of the mouse position for this, only use the mouse position for the moving node to follow.

If you’re using a TileMapLayer, have a look at the local_to_map() and map_to_local() methods. You can use those to convert a mouse click on the tilemap to tile coordinates.

You could do one of several things here:

  • do a “within radius of building” check on clicks
  • keep a map of tiles to buildings
  • keep a per-building list of the tiles it covers
  • &c.

Personally, I’d probably keep a parallel array of every cell on the map and what (if any) building it contains. If someone clicks on a tile, you can use local_to_map() to figure out the cell coords, look that up in your coverage array, and it should spit out a building id.

Thank you. I actually did try to get a radius around a mouse made and would store the tiles of teach building placed in a dictionary. If the range of the mouse is within the tile of a built building, remove the building. However, another error came up. I have the code here:

Your code:

var used_tiles = {}

[...]

    for k in range (used_tiles.size()):
        if used_tiles[k] == mouse_range: # <- BANG!
            [...]
  • You’ve got used_tiles as a dictionary.
  • You’re iterating k over the number of entries in used_tiles, so it’s going to be an integer between 0 and however many dict entries you have.
  • Your dictionary is keyed with Vector2i, so when you do used_tiles[k] it expands to used_tiles[0], the dictionary returns null because there’s nothing there.
  • You compare that null to mouse_range, things go pear-shaped.

What you probably want is:

    for key in used_tiles.keys():
        if used_tile[key] == mouse_range:
            [...]

That gets an array containing all the keys in the dictionary, and iterates over it.

As an aside:

for x in range(value)

is equivalent to the less verbose:

for x in value:

The only reason to include range() is if you want to start iterating somewhere other than zero.

Thank you. I found another solution to my issue actually: redesign the buildings so they are all the same sized tile. Then resize the tile of the “buildings” TileMapLayer. This results in needed less code, keeping it simple.

1 Like