I am making a sandbox(with sand) and I stumbled on an error in this line:
for grain in sand:
grain.update()
The sand variable is a dictionary, and I don’t know what went wrong. If you need further clarity, comment.
The full program is:
import pygame
import random
WIDTH, HEIGHT = 640, 360
FPS = 60
WHITE = (255, 255, 255)
SAND = (194, 178, 128)
GRAVITY = 0.5 # increased gravity
def main_program():
global sand
pygame.init()
game_display = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Sandbox")
is_crashed = False
clock = pygame.time.Clock()
font = pygame.font.SysFont("Arial", 32)
sand_grain = pygame.Surface((4, 4))
pygame.draw.rect(sand_grain, SAND, (0, 0, 4, 4))
sand = {}
sand_id = 0
while not is_crashed:
mouse_x, mouse_y = pygame.mouse.get_pos()
mouse_held = pygame.mouse.get_pressed()[0]
if mouse_held:
sand[str(sand_id)] = Grain(mouse_x, mouse_y)
sand_id += 1
for grain in sand:
grain.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
is_crashed = True
# handle mouse button down event
if event.type == pygame.MOUSEBUTTONDOWN:
# get the position and button of the mouse event
pos = event.pos
button = event.button
# get a list of all grains that are under the mouse cursor
clicked_grains = [g for g in sand.values() if g.rect.collidepoint(pos)]
# if there are any grains clicked
if clicked_grains:
# get the topmost grain
top_grain = clicked_grains[-1]
# if the left mouse button is pressed
if button == 1:
# set the dragged attribute of the grain to True
top_grain.dragged = True
# handle mouse button up event
if event.type == pygame.MOUSEBUTTONUP:
# get the position and button of the mouse event
pos = event.pos
button = event.button
# get a list of all grains that are under the mouse cursor
clicked_grains = [g for g in sand.values() if g.rect.collidepoint(pos)]
# if there are any grains clicked
if clicked_grains:
# get the topmost grain
top_grain = clicked_grains[-1]
# if the left mouse button is released
if button == 1:
# set the dragged attribute of the grain to False
top_grain.dragged = False
game_display.fill(WHITE)
for grain in sand.values():
grain.draw(game_display, sand_grain)
pygame.display.flip()
clock.tick(FPS)
pygame.quit()
class Grain:
def __init__(self, x, y):
self.x = x
self.y = y
self.yv = 0
self.xv = random.randint(-2, 2) # added horizontal velocity
self.rect = pygame.Rect(self.x, self.y, 4, 4)
self.dragged = False # added dragged attribute
def update(self):
global sand
if self.y > HEIGHT - 4: # check for bottom edge collision
self.y = HEIGHT - 4
self.yv = 0
elif self.y < 0: # check for top edge collision
self.y = 0
self.yv = 0
else:
self.yv += GRAVITY
if self.x > WIDTH - 4: # check for right edge collision
self.x = WIDTH - 4
self.xv = 0
elif self.x < 0: # check for left edge collision
self.x = 0
self.xv = 0
else:
self.xv += random.randint(-1, 1) # add some randomness to horizontal movement
self.x += self.xv
self.y += self.yv
self.rect = pygame.Rect(self.x, self.y, 4, 4)
for other in sand.values(): # check for collisions with other grains
if other is not self and self.rect.colliderect(other.rect): # use pygame.Rect.colliderect method
self.yv = 0
self.xv = 0
if self.y < other.y:
self.y = other.y - 4
elif self.y > other.y:
self.y = other.y + 4
if self.x < other.x:
self.x = other.x - 4
elif self.x > other.x:
self.x = other.x + 4
break
# check if the grain is being dragged by the mouse
if self.dragged:
# get the current mouse position and movement
mouse_x, mouse_y = pygame.mouse.get_pos()
mouse_rel_x, mouse_rel_y = pygame.mouse.get_rel()
# set the position and velocity of the grain to match the mouse
self.x = mouse_x
self.y = mouse_y
self.xv = mouse_rel_x
self.yv = mouse_rel_y
# update the rect of the grain
self.rect = pygame.Rect(self.x, self.y, 4, 4)
def draw(self, display, grain):
display.blit(grain, self.rect)
if __name__ == "__main__":
main_program()
>Solution :
Try this
for grain in sand.values():
grain.update()
I think with sand you are accessing the keys and with sand.values you will access the correct instances