I am programming a strategy game that takes place on an Hexagonal grid (yes, a civ clone) and want to implement a system, which allows me to display some properties of the tile by hovering over it with my mouse. My current implementation is to create a rect for every Tile (1), this rect has the same dimensions and coordinates as the sprite of the Tile. Then, I use pygame.Rect.collidepoint (2) to detect collision. The problem with this is that the rects of the different Tiles overlap at the edges, causing multiple collisions to be detected (3)
(1)
for i in range (0, 100):
Tiles_rects.append (pygame.Rect(Tiles_pos[i], (TILE_WIDTH, TILE_HEIGHT )))
(2)
for i in range (0, 100):
# Takes one tile at a time and determines if mouse is on top of it
mouse_over_tile = pygame.Rect.collidepoint( Tiles_rects[i] , mouse_pos )
if mouse_over_tile:
print (i) #placeholder
I tried to make the colliders (rects) smaller and adjusted their position to be centered. While this does fix the overlap, it also makes a considerable part of the hexagon not react when hovered over. A circle collider instead of a rect collider would likely not be of much more use, as my hexagons are somewhat streched on the x axis.
You can use the fact that the slope on the sides of the hexagon is 60°. If the y-distance to the center of the bounding rectangle is less than the x-distance from the outside of the bounding rectangle multiplied by the tangent of 60°, the point is inside the hexagon:
def collideHexagon(bounding_rect, position):
px, py = position
if bounding_rect.collidepoint((px, py)):
dx = min(px - bounding_rect.left, bounding_rect.right - px)
dy = abs(py - bounding_rect.centery)
if dy < dx * math.tan(math.radians(60)):
return True
return False
Minimal example:
import pygame, math
pygame.init()
screen = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
len = 100
sin_len = math.sin(math.radians(60)) * len
cos_len = math.cos(math.radians(60)) * len
pts = [
(len, 0), (len-cos_len, sin_len), (cos_len-len, sin_len),
(-len, 0), (cos_len-len, -sin_len), (len-cos_len, -sin_len)]
pts = [(x + 200, y + 200) for x, y in pts]
tile_heihgt = sin_len * 2
tile_width = len * 2
tiel_rect = pygame.Rect(0, 0, tile_width, tile_heihgt)
tiel_rect.center = (200, 200)
def collideHexagon(bounding_rect, position):
px, py = position
if bounding_rect.collidepoint((px, py)):
dx = min(px - bounding_rect.left, bounding_rect.right - px)
dy = abs(py - bounding_rect.centery)
if dy < dx * math.tan(math.radians(60)):
return True
return False
run = True
while run:
clock.tick(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
color = "white"
if collideHexagon(tiel_rect, pygame.mouse.get_pos()):
color = "red"
screen.fill(0)
pygame.draw.polygon(screen, color, pts)
pygame.display.flip()
pygame.quit()
exit()