Click detection happening multiple times per click
Question:
Whenever a user clicks on a point in the map, the click is registered more than once. Obviously this is not ideal as it could lead to multiple inputs not being registered the correct amount of times.
I have a put a variable "clicked" in the logic, which works in other programs, but here it does not work and it still registers the click more than once.
Main Code:
import pygame
from world import World
pygame.init()
screen_width = 1500
screen_height = 650
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption('Grid Wars INDEV')
world = World(screen)
tile_size = 50
lineAmount = 1300/50
def draw_grid():
for line in range(1,25):
pygame.draw.line(screen, (218,165,32), (200, line * tile_size), (1350, line * tile_size), 2)
keyline = line + 3
pygame.draw.line(screen, (218,165,32), (keyline * tile_size, 0), (keyline * tile_size, 650), 2)
run = True
while run:
screen.fill("red")
world.draw()
draw_grid()
pygame.draw.line(screen, (218,165,32), (200,5), (1350,5), 10)
pygame.draw.line(screen, (218,165,32), (200,645), (1350, 645), 10)
pygame.draw.line(screen, (218,165,32), (200,0), (200, 650), 10)
pygame.draw.line(screen, (218,165,32), (1350,0), (1350, 650), 10)
action = world.isClicked()
if action[0] == True:
print('Hello World')
print(action[1])
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
pygame.display.update()
pygame.quit()
Class Code:
import pygame
from button import Button
class World:
def __init__(self, screen) -> None:
self.name_list = []
self.screen = screen
self.clicked = False
self.action = False
map_data = [
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1],
[1,0,0,0,0,1,0,1,1,1,0,1,1,0,0,0,0,0,0,0,0,0,1],
[1,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1],
[1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[0,1,1,1,1,1,0,0,0,1,1,0,0,1,1,1,1,0,0,0,0,0,1],
[1,1,1,1,1,1,1,0,1,1,1,0,0,1,1,1,1,1,1,1,0,1,1]
]
ocean_img = pygame.image.load('ocean.png')
grass_img = pygame.image.load('grass.png')
row_count = 0
for row in map_data:
column_count = 0
for tile in row:
if tile == 1:
img = pygame.transform.scale(ocean_img, (50,50))
self.img_rect = img.get_rect()
self.img_rect.x = column_count * 50 + 200
self.img_rect.y = row_count * 50
tile = (img, self.img_rect,'ocean')
self.name_list.append(tile)
if tile == 0:
img = pygame.transform.scale(grass_img, (50,50))
self.img_rect = img.get_rect()
self.img_rect.x = column_count * 50 + 200
self.img_rect.y = row_count * 50
tile = (img, self.img_rect,'grass')
self.name_list.append(tile)
column_count += 1
row_count += 1
def draw(self):
for tile in self.name_list:
self.screen.blit(tile[0], tile[1])
def isClicked(self):
self.clicked = False
pos = pygame.mouse.get_pos()
for tile in self.name_list:
if tile[1].collidepoint(pos):
if pygame.mouse.get_pressed()[0] == 1 and self.clicked == False:
self.clicked = True
self.action = True
if pygame.mouse.get_pressed()[0] == 0:
self.clicked = False
self.action = False
return self.action, tile[2]
Answers:
Actually you don’t detect clicks, but you detect when the mouse button is held down and the mouse hovers over a tile. Use the MOUSEBUTTONDOWN
event to detect clicks (see Pygame mouse clicking detection). Note that if pygame.event.get()
is called in multiple event loops, only one loop receives the events, but never all loops receive all events. As a result, some events appear to be missed. You have to get the events once per frame and use them in multiple loops or pass the list of events to functions and methods where they are handled.
Also, you must break the loop or return the function if you detect a click because you want to get the result of the clicked tile, not the last tile in the loop:
class World:
# [...]
def isClicked(self, event_list):
for event in event_list:
if event.type == pygame.MOUSEBUTTONDOWN:
for tile in self.name_list:
if tile[1].collidepoint(event.pos):
if event.button == pygame.BUTTON_LEFT:
return True, tile[2]
if event.button == pygame.BUTTON_RIGHT:
return False, tile[2]
return False, None
while run:
event_list = pygame.event.get()
for event in event_list :
if event.type == pygame.QUIT:
run = False
screen.fill("red")
world.draw()
draw_grid()
pygame.draw.line(screen, (218,165,32), (200,5), (1350,5), 10)
pygame.draw.line(screen, (218,165,32), (200,645), (1350, 645), 10)
pygame.draw.line(screen, (218,165,32), (200,0), (200, 650), 10)
pygame.draw.line(screen, (218,165,32), (1350,0), (1350, 650), 10)
action = world.isClicked(event_list)
if action[0] == True:
print('Hello World')
print(action[1])
pygame.display.update()
pygame.quit()
Whenever a user clicks on a point in the map, the click is registered more than once. Obviously this is not ideal as it could lead to multiple inputs not being registered the correct amount of times.
I have a put a variable "clicked" in the logic, which works in other programs, but here it does not work and it still registers the click more than once.
Main Code:
import pygame
from world import World
pygame.init()
screen_width = 1500
screen_height = 650
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption('Grid Wars INDEV')
world = World(screen)
tile_size = 50
lineAmount = 1300/50
def draw_grid():
for line in range(1,25):
pygame.draw.line(screen, (218,165,32), (200, line * tile_size), (1350, line * tile_size), 2)
keyline = line + 3
pygame.draw.line(screen, (218,165,32), (keyline * tile_size, 0), (keyline * tile_size, 650), 2)
run = True
while run:
screen.fill("red")
world.draw()
draw_grid()
pygame.draw.line(screen, (218,165,32), (200,5), (1350,5), 10)
pygame.draw.line(screen, (218,165,32), (200,645), (1350, 645), 10)
pygame.draw.line(screen, (218,165,32), (200,0), (200, 650), 10)
pygame.draw.line(screen, (218,165,32), (1350,0), (1350, 650), 10)
action = world.isClicked()
if action[0] == True:
print('Hello World')
print(action[1])
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
pygame.display.update()
pygame.quit()
Class Code:
import pygame
from button import Button
class World:
def __init__(self, screen) -> None:
self.name_list = []
self.screen = screen
self.clicked = False
self.action = False
map_data = [
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1],
[1,0,0,0,0,1,0,1,1,1,0,1,1,0,0,0,0,0,0,0,0,0,1],
[1,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1],
[1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[0,1,1,1,1,1,0,0,0,1,1,0,0,1,1,1,1,0,0,0,0,0,1],
[1,1,1,1,1,1,1,0,1,1,1,0,0,1,1,1,1,1,1,1,0,1,1]
]
ocean_img = pygame.image.load('ocean.png')
grass_img = pygame.image.load('grass.png')
row_count = 0
for row in map_data:
column_count = 0
for tile in row:
if tile == 1:
img = pygame.transform.scale(ocean_img, (50,50))
self.img_rect = img.get_rect()
self.img_rect.x = column_count * 50 + 200
self.img_rect.y = row_count * 50
tile = (img, self.img_rect,'ocean')
self.name_list.append(tile)
if tile == 0:
img = pygame.transform.scale(grass_img, (50,50))
self.img_rect = img.get_rect()
self.img_rect.x = column_count * 50 + 200
self.img_rect.y = row_count * 50
tile = (img, self.img_rect,'grass')
self.name_list.append(tile)
column_count += 1
row_count += 1
def draw(self):
for tile in self.name_list:
self.screen.blit(tile[0], tile[1])
def isClicked(self):
self.clicked = False
pos = pygame.mouse.get_pos()
for tile in self.name_list:
if tile[1].collidepoint(pos):
if pygame.mouse.get_pressed()[0] == 1 and self.clicked == False:
self.clicked = True
self.action = True
if pygame.mouse.get_pressed()[0] == 0:
self.clicked = False
self.action = False
return self.action, tile[2]
Actually you don’t detect clicks, but you detect when the mouse button is held down and the mouse hovers over a tile. Use the MOUSEBUTTONDOWN
event to detect clicks (see Pygame mouse clicking detection). Note that if pygame.event.get()
is called in multiple event loops, only one loop receives the events, but never all loops receive all events. As a result, some events appear to be missed. You have to get the events once per frame and use them in multiple loops or pass the list of events to functions and methods where they are handled.
Also, you must break the loop or return the function if you detect a click because you want to get the result of the clicked tile, not the last tile in the loop:
class World:
# [...]
def isClicked(self, event_list):
for event in event_list:
if event.type == pygame.MOUSEBUTTONDOWN:
for tile in self.name_list:
if tile[1].collidepoint(event.pos):
if event.button == pygame.BUTTON_LEFT:
return True, tile[2]
if event.button == pygame.BUTTON_RIGHT:
return False, tile[2]
return False, None
while run:
event_list = pygame.event.get()
for event in event_list :
if event.type == pygame.QUIT:
run = False
screen.fill("red")
world.draw()
draw_grid()
pygame.draw.line(screen, (218,165,32), (200,5), (1350,5), 10)
pygame.draw.line(screen, (218,165,32), (200,645), (1350, 645), 10)
pygame.draw.line(screen, (218,165,32), (200,0), (200, 650), 10)
pygame.draw.line(screen, (218,165,32), (1350,0), (1350, 650), 10)
action = world.isClicked(event_list)
if action[0] == True:
print('Hello World')
print(action[1])
pygame.display.update()
pygame.quit()