I am creating a game with pygame & python 3.10.0 using VSCode everything is working fine but this if statement stops working after sometime and the font doesn't draw but I can't pinpoint the problem for that , for the if statement it usually runs till 10 or 11 score but it can be quicker like 2:
if player.rect.colliderect(food):
pygame.sprite.Sprite.kill(food)
food.rect.x = random.randrange(20, 1700)
food.rect.y = random.randrange(20, 860)
all_sprites_list.add(food)
score += 1
print(score)
whole code:
import pygame
import sys
import time
import random
import ctypes
from ctypes import wintypes
from pygame import sprite
from pygame.draw import rect
from pygame.event import pump, wait
from pygame import font
pygame.font.init()
myappid = 'elementalstudios.snake.Alpha 0_1' # arbitrary string
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid)
#------ Initialize Variables------#
# Player
player_width = 20
player_height = 20
# Food
food_width = 10
food_height = 10
# Colours
seafoam_gr = (159, 226, 191)
black = (0, 0, 0)
blue = (0, 0, 255)
red = (255, 0, 0)
green = (0, 255, 0)
white = (255, 255, 255)
# Score
score = 0
#------ Score Font Initialize ------#
font = pygame.font.Font(None, 50)
text = font.render("Score:", False, white, None)
#------Sprites Class------#
class Sprite(pygame.sprite.Sprite):
def __init__(self, color, height, width):
super().__init__()
self.image = pygame.Surface([width, height])
self.image.fill(seafoam_gr)
self.image.set_colorkey(black)
pygame.draw.rect(self.image,
color,
pygame.Rect(0, 0, width, height))
self.rect = self.image.get_rect()
def moveRight(self, pixels):
self.rect.x += pixels
def moveLeft(self, pixels):
self.rect.x -= pixels
def moveUp(self, speed):
self.rect.y -= speed * speed / 5
def moveDown(self, speed):
self.rect.y += speed * speed / 5
#------ Font Class ------#
def create_font(t,s = 24, colour_font = white, bold = False, italic = False):
font = pygame.font.Font("prstart.ttf", s, bold, italic)
text = font.render(t, True, colour_font)
return text
#------ Initialize Pygame and Window------#
pygame.init()
icon = pygame.image.load('Icon.ico')
pygame.display.set_icon(icon)
gameDisplay = pygame.display.set_mode((1920,1080), pygame.FULLSCREEN)
#rect = pygame.Rect( * gameDisplay.get_rect().center, 0, 0).inflate(100, 100)
pygame.display.set_caption("Blocky")
gameDisplay.fill(black)
clock = pygame.time.Clock()
running = True
#------Initialize Sprites------#
all_sprites_list = pygame.sprite.Group()
player = Sprite(seafoam_gr, player_height, player_width)
food = Sprite(red, food_height, food_width)
player.rect.x = 960
player.rect.y = 540
food.rect.x = 20 #random.randrange(20, 1800)
food.rect.y = 30 #random.randrange(20, 1050)
#------Add Sprites to sprite list------#
all_sprites_list.add(player)
all_sprites_list.add(food)
clock = pygame.time.Clock()
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
#------Eating------#
if player.rect.colliderect(food):
pygame.sprite.Sprite.kill(food)
food.rect.x = random.randrange(20, 1700)
food.rect.y = random.randrange(20, 860)
all_sprites_list.add(food)
score += 1
print(score)
#------ Score Draw ------#
text_rect = text.get_rect()
text_rect.center = (100, 150)
gameDisplay.blit(text, text_rect)
#------Key Movement------#
keys = pygame.key.get_pressed()
# Move Left
if keys[pygame.K_a]:
player.moveLeft(3)
if keys[pygame.K_LCTRL]:
player.moveLeft(5)
if keys[pygame.K_LEFT]:
player.moveLeft(3)
if keys[pygame.K_LCTRL]:
player.moveLeft(5)
# Move Right
if keys[pygame.K_d]:
player.moveRight(5)
if keys[pygame.K_LCTRL]:
player.moveRight(5)
if keys[pygame.K_RIGHT]:
player.moveRight(3)
if keys[pygame.K_LCTRL]:
player.moveRight(5)
# Move Down
if keys[pygame.K_s]:
player.moveDown(3)
if keys[pygame.K_LCTRL]:
player.moveDown(5)
if keys[pygame.K_DOWN]:
player.moveDown(3)
if keys[pygame.K_LCTRL]:
player.moveDown(5)
# Move Up
if keys[pygame.K_w]:
player.moveUp(3)
if keys[pygame.K_LCTRL]:
player.moveUp(5)
if keys[pygame.K_UP]:
player.moveUp(3)
if keys[pygame.K_LCTRL]:
player.moveUp(5)
all_sprites_list.update()
gameDisplay.fill(black)
all_sprites_list.draw(gameDisplay)
pygame.display.flip()
clock.tick(60)
pygame.quit()
quit()
It is not necessary to kill the food object. It is sufficient to change the position of the food. pygame.sprite.Sprite.kill just removes the Sprite object from all Gorups. So there is no point in calling kill and then adding the object back to the Group.
The text does not show up, because you draw it before you clear the display. pygame.Surface.fill fills the Surface with a solid color. Everything that was previously drawn will be cleared. blit the text after fill.
You will have to render the text Surface again if the score has changed.
I recommend simplifying the code that moves the player. See How can I make a sprite move when key is held down.
class Sprite(pygame.sprite.Sprite):
def __init__(self, color, x, y, height, width):
super().__init__()
self.image = pygame.Surface([width, height])
self.image.fill(color)
self.image.set_colorkey(black)
self.rect = self.image.get_rect(topleft = (x, y))
self.x, self.y = self.rect.x, self.rect.y
def move(self, move_x, move_y, speed_up):
if move_x or move_y:
direction = pygame.math.Vector2(move_x, move_y)
direction.scale_to_length(5 if speed_up else 3)
self.x += direction.x
self.y += direction.y
self.rect.x, self.rect.y = round(self.x), round(self.y)
player = Sprite(seafoam_gr, 400, 300, player_height, player_width)
food = Sprite(red, 20, 30, food_height, food_width)
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
if player.rect.colliderect(food):
food.rect.x = random.randrange(20, 780)
food.rect.y = random.randrange(20, 580)
score += 1
# render text
text = font.render("Score: " + str(score), False, white, None)
keys = pygame.key.get_pressed()
move_x = ((keys[pygame.K_d] or keys[pygame.K_RIGHT]) - (keys[pygame.K_a] or keys[pygame.K_LEFT]))
move_y = ((keys[pygame.K_s] or keys[pygame.K_DOWN]) - (keys[pygame.K_w] or keys[pygame.K_UP]))
player.move(move_x, move_y, keys[pygame.K_LCTRL])
all_sprites_list.update()
gameDisplay.fill(black)
# draw text
gameDisplay.blit(text, text.get_rect(center = (100, 150)))
all_sprites_list.draw(gameDisplay)
pygame.display.flip()
clock.tick(60)
pygame.quit()
quit()
I am trying to make a maze game in Pygame but am unable to achieve collision for the 1 (maze wall) in the array. I tried to put the collision detection in the loop creating the map but it is not working. I also put the collision detection in the main loop but only the top left rect detected the collision, not all the 1 rects. How would I go about fixing this? Thank you!
import pygame
pygame.init()
screen = pygame.display.set_mode((700,700))
pygame.display.set_caption("Game")
speed = 20
x = 200
y = 600
def game_map():
global rect_one
surface = pygame.Surface((100, 100), pygame.SRCALPHA)
rect_one = pygame.draw.rect(surface, (0, 0, 255), (0, 0, 50, 50))
global rect_two
surface_one = pygame.Surface((80, 80), pygame.SRCALPHA)
rect_two = pygame.draw.rect(surface_one, (255, 255, 255), (0, 0, 50, 50))
tileX = 0
tileY = 0
global tile_list
tile_list = []
map = [
[1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,0,0,0,1,0,1,0,0,0,0,0,1],
[1,0,1,0,0,0,1,0,1,1,1,0,1],
[1,0,0,0,1,1,1,0,0,0,0,0,1],
[1,0,1,0,0,0,0,0,1,1,1,0,1],
[1,0,1,0,1,1,1,0,1,0,0,0,1],
[1,0,1,0,1,0,0,0,1,1,1,0,1],
[1,0,1,0,1,1,1,0,1,0,1,0,1],
[1,0,0,0,0,0,0,0,0,0,1,0,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1]
]
for y, row in enumerate(map):
tileX = 0
for x, cell in enumerate(row):
image = surface if cell == 1 else surface_one
screen.blit(image, [x*50, y*50])
tile_list.append(rect_one)
pygame.display.update()
def player():
player = pygame.draw.rect(screen, (255,0,0), (x, y, 20, 20))
for i in tile_list:
if player.colliderect(i):
print("hello")
loop = True
while loop:
pygame.time.delay(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
loop = False
#player controls
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
x -= speed
if keys[pygame.K_RIGHT]:
x += speed
if keys[pygame.K_UP]:
y -= speed
if keys[pygame.K_DOWN]:
y += speed
screen.fill((255,255,255))
game_map()
player()
pygame.display.update()
pygame.quit()
Your tile_list only contains one Rect multiple times.
I simplified your code a little bit and use a Rect with the correct coordinates for each 1 in your map. Also note the comments:
import pygame
pygame.init()
screen = pygame.display.set_mode((700,700))
pygame.display.set_caption("Game")
speed = 10
player_x = 200
player_y = 600
# Use a constant. There's not need to make big Surfaces and then draw a smaller rect on them to create the map.
TILESIZE = 50
tile_list = []
map = [
[1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,0,0,0,1,0,1,0,0,0,0,0,1],
[1,0,1,0,0,0,1,0,1,1,1,0,1],
[1,0,0,0,1,1,1,0,0,0,0,0,1],
[1,0,1,0,0,0,0,0,1,1,1,0,1],
[1,0,1,0,1,1,1,0,1,0,0,0,1],
[1,0,1,0,1,0,0,0,1,1,1,0,1],
[1,0,1,0,1,1,1,0,1,0,1,0,1],
[1,0,0,0,0,0,0,0,0,0,1,0,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1]
]
# let's create a single Surface for the map and reuse that
grid = pygame.Surface((len(map[0]) * TILESIZE, len(map) * TILESIZE), pygame.SRCALPHA)
for y, row in enumerate(map):
for x, cell in enumerate(row):
# if we want a wall, we draw it on the new Surface
# also, we store the Rect in the tile_list so collision detection works
if cell:
rect = pygame.draw.rect(grid, 'blue', (x*TILESIZE, y*TILESIZE, TILESIZE, TILESIZE))
tile_list.append(rect)
loop = True
clock = pygame.time.Clock()
while loop:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
loop = False
#player controls
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
player_x -= speed
if keys[pygame.K_RIGHT]:
player_x += speed
if keys[pygame.K_UP]:
player_y -= speed
if keys[pygame.K_DOWN]:
player_y += speed
screen.fill((255,255,255))
# draw the map surface to the screen
screen.blit(grid, (0 ,0))
player = pygame.draw.rect(screen, (255,0,0), (player_x, player_y, 20, 20))
# now collision detection works because for each 1 in the map
# there's a Rect in tile_list with the correct coordinates
for i in tile_list:
if player.colliderect(i):
print("colliding")
break
else:
print('not colliding')
pygame.display.update()
pygame.quit()
Save the position of the player before moving it:
pos = x, y
Compute the row and column after the player has moved:
row = y // 50
column = x // 50
Reset the player's position if the new position is on a wall:
if map[row][column] == 1:
x, y = pos
Additionally you have to move the map variable to global namespace. The speed should a integral divider of the tile size. Change the starting position to a position in the grid:
speed = 25
x = 50
y = 50
Complete code:
import pygame
pygame.init()
screen = pygame.display.set_mode((700,700))
pygame.display.set_caption("Game")
speed = 25
x = 50
y = 50
map = [
[1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,0,0,0,1,0,1,0,0,0,0,0,1],
[1,0,1,0,0,0,1,0,1,1,1,0,1],
[1,0,0,0,1,1,1,0,0,0,0,0,1],
[1,0,1,0,0,0,0,0,1,1,1,0,1],
[1,0,1,0,1,1,1,0,1,0,0,0,1],
[1,0,1,0,1,0,0,0,1,1,1,0,1],
[1,0,1,0,1,1,1,0,1,0,1,0,1],
[1,0,0,0,0,0,0,0,0,0,1,0,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1]
]
def game_map():
global rect_one
surface = pygame.Surface((100, 100), pygame.SRCALPHA)
rect_one = pygame.draw.rect(surface, (0, 0, 255), (0, 0, 50, 50))
global rect_two
surface_one = pygame.Surface((80, 80), pygame.SRCALPHA)
rect_two = pygame.draw.rect(surface_one, (255, 255, 255), (0, 0, 50, 50))
tileX = 0
tileY = 0
for y, row in enumerate(map):
tileX = 0
for x, cell in enumerate(row):
image = surface if cell == 1 else surface_one
screen.blit(image, [x*50, y*50])
pygame.display.update()
def player():
player = pygame.draw.rect(screen, (255,0,0), (x, y, 25, 25))
loop = True
while loop:
pygame.time.delay(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
loop = False
#player controls
keys = pygame.key.get_pressed()
pos = x, y
if keys[pygame.K_LEFT]:
x -= speed
if keys[pygame.K_RIGHT]:
x += speed
if keys[pygame.K_UP]:
y -= speed
if keys[pygame.K_DOWN]:
y += speed
row = y // 50
column = x // 50
if map[row][column] == 1:
x, y = pos
screen.fill((255,255,255))
game_map()
player()
pygame.display.update()
pygame.quit()