Implement Map with Clickable Polygons/Hexagons, Interactive Camera2D, and VBoxContainer of Buttons?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By kestrel256

Hi all, I am a Godot newbie. I’d like to use Godot to implement an old SPI wargame. The battlefield is a hex map, and can be seen at the end of this PDF. This is just a passion project on my bucket list.

Rather than reconstruct the game map in Godot using a tileset, I’d like to display the original game map as an image. The problem then becomes how to tell which hex in the map the player has selected. I am thus in a very similar situation to this question posted by Mike Whittemore last year (2020).

Mike W. thought that one approach might be to create a “grid with a bunch of invisible 2D polygons (hexagons) for each location that can intercept mouse clicks, have center points, etc.” I realize there are alternative approaches, but I would like to go this route. In addition to having clickable hexagons in the map, I’d also like to be able to zoom and pan around the map.

Using the code implementing clickable polygons here as well as the code implementing an interactive Camera2D here, I have been able to get a bare-bones prototype to work; example code is here. Hexagons are displayed in a translucent light green color. Note that when you click on a hexagon, the prototype can identify the index number of the clicked hexagon. This is very cool! (I’m guessing that the battlefield map is still protected under copyright, so in the example code I am instead using a different map made publicly available under a free license for personal use. It is a very nice hex map designed by Dyson Logos.)

But a problem arises when I try to add a vertical bank of buttons to the left of the interactive map: although the hexagons are still visible, they are no longer clickable. As a result, we can’t identify which hexagon the user has clicked on. The updated code in which the vertical bank of buttons (actually only a Label and a Button, for illustration) has been added is here. Interestingly, the Camera2D zooming and panning functionality still works. Note that I’ve placed the TextureRect (which contains the map) and the Camera2D within a Node2D, which in turn is the child of a ViewPort.

Updated Scene Tree

Is the problem that the ViewPort blocks signals from getting to the hexagonal polygons, so I need to use something besides a ViewPort? (But somehow the Camera2D is still able to receive events?!) Or, have I improperly set the Mouse Filter property for some of the parent nodes? Or, something else?

How do I fix the updated code? I’d like to retain:

  1. The general layout of the interface (i.e., an HSplitContainer containing a vertical bank of buttons to the left, and the battlefield map on the right)
  2. The interactivity of the Camera2D (zoom and drag)
  3. The identifiability of clicked hexagons

Thanks very much in advance for any advice!

Maybe this can help you

There i posted some code to mathematically find the corresponding hex for a coordinate.

klaas | 2021-07-06 16:06

Hi klaas, thanks very much for your advice!

I have come to the conclusion that your approach is indeed the way to go. The ViewPort seems to prevent the polygons/hexagons from being clickable – but it is still possible to obtain a selected (clicked) XY coordinate within the ViewPort, e.g., something like this::

func _input(event):
    if event is InputEventMouseButton and event.pressed:
        print("Mouse Click at: ", event.position)

The corresponding hexagon can then be computed from the XY coordinates using a bit of math.

Mike W. hinted at this approach in his 2020 post (“would you do this with pure math, for example, based on the x/y coordinate calculate where on the map image to place unit sprites, whether the user clicked in a particular hex”). The mathematics involved is worked out here. And it seems to be the approach used in this Godot tutorial.

Again, many thanks.

kestrel256 | 2021-07-09 17:15

For those who have been following along, I have updated my code to implement the suggestion that klaas made. Now you can click on the map in the ViewPort, and under the hood the code will convert the screen XY coordinates (which might reflect zooming and panning) to raw unzoomed and unpanned map XY coordinates. From the raw XY coordinates, the code figures out which hexagon has been selected, and displays this information in the console. Click on the Quit button to exit.

My solution is on GitHub here.

kestrel256 | 2021-07-11 00:19

Take a look at last comment here:

viewport consuming mouse events can be rectified by enable Viewport property Physics->Object Picking to “On”. Also Mouse filter to pass or ignore.

Rmstar | 2022-09-24 17:37