Pygame wall sprite collision - python

I am making a roguelike game, but I am beginner when it comes to coding. I already have my character moving, my wall and floor sprites, but there is some error in my code that allows the character to move through walls.
I used the block_path to choose between the floor and wall tile and I tried to use it then to recognize the wall but it didn't really work.
Next, you can see my code:
screenWidth = 800
screenHeight = 600
mapHeight = 30
mapWidth = 30
cellWidth = 32
cellHeight = 32
screen = pygame.display.set_mode((screenWidth, screenHeight))
walkRight = [pygame.image.load('model/r1.png'), pygame.image.load('model/r2.png'), pygame.image.load('model/r3.png'), pygame.image.load('model/r4.png'), pygame.image.load('model/r5.png'), pygame.image.load('model/r6.png')]
walkLeft = [pygame.image.load('model/l1.png'), pygame.image.load('model/l2.png'), pygame.image.load('model/l3.png'), pygame.image.load('model/l4.png'), pygame.image.load('model/l5.png'), pygame.image.load('model/l6.png')]
walkUp = [pygame.image.load('model/u1.png'), pygame.image.load('model/u2.png'), pygame.image.load('model/u3.png'), pygame.image.load('model/u4.png'), pygame.image.load('model/u5.png'), pygame.image.load('model/u6.png')]
Floor = pygame.image.load("map/floor.jpg")
wallRight = pygame.image.load("map/rightwall.png")
`
class struc_Tile():
def __init__(self,block_path):
self.block_path = block_path`
class player(object):
def __init__(self,x,y,width,height):
self.x = x
self.y = y
self.width = width
self.height = height
self.vel = 5
self.left = False
self.right = False
self.up = False
self.down = False
self.walkCount = 0
def draw(self,screen):
if self.walkCount + 1 >= 18:
self.walkCount = 0
elif self.left:
screen.blit(walkLeft[self.walkCount//3], (self.x,self.y))
self.walkCount += 1
elif self.right:
screen.blit(walkRight[self.walkCount//3], (self.x,self.y))
self.walkCount += 1
elif self.up:
screen.blit(walkUp[self.walkCount//3], (self.x,self.y))
self.walkCount += 1
elif self.down:
screen.blit(walkDown[self.walkCount//3], (self.x,self.y))
self.walkCount += 1
else:
screen.blit(Standing[self.walkCount//3], (self.x,self.y))
self.walkCount = 0
def move(self,dx,dy):
if gamemap[self.x + dx][self.y + dy].block_path == False:
self.x += dx
self.y += dy
def createmap():
newmap = [[struc_Tile(False) for y in range(0,mapHeight)] for x in range (0,mapWidth) ]
newmap[10][10].block_path = True
newmap[10][15].block_path = True
return newmap
def drawmap(maptodraw):
for x in range(0,mapWidth):
for y in range(0,mapHeight):
if maptodraw[x][y].block_path == True:
screen.blit(wallRight, (x*cellWidth, y*cellHeight))
else:
screen.blit(Floor, (x*cellWidth, y*cellHeight)
def redrawgamewindow():
screen.blit(bg, (0, 0))
drawmap(gamemap)
character.draw(screen)
pygame.display.update()
pygame.init()
gamemap = createmap()
clock = pygame.time.Clock()
character = player(0, 0, 32,32)
run = True
while run:
clock.tick(18)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] and character.x > character.vel:
character.x -= character.vel
character.left = True
character.right = False
character.up = False
character.down = False
character.standing = False
elif keys[pygame.K_RIGHT] and character.x < 800 -character.width - character.vel:
character.x += character.vel
character.left = False
character.right = True
character.up = False
character.down = False
character.standing = False
elif keys[pygame.K_UP] and character.y > character.vel:
character.y -= character.vel
character.left = False
character.right = False
character.up = True
character.down = False
character.standing = False
elif keys[pygame.K_DOWN] and character.y < 600 - character.height - character.vel:
character.y += character.vel
character.left = False
character.right = False
character.up = False
character.down = True
character.standing = False
else:
character.right = False
character.left = False
character.up = False
character.down = False
character.standing = True
redrawgamewindow()

Changing your createmap() function to something like this will create a 5 pixel buffer on the boreder of your map. The reason I made it 5 pixels is because that's what your character movement is.
I would suggest putting in some actual character collision to test whether you're out of bounds regardless of speed though.
EDIT: I've included the full code since the changes I've made are easier to understand if I show them.
I've changed your image lists to a dict of list since they're easier to call that way. The grid you asked for is 30x30. Since you showed a 32x32 cell width I changed the map to 960 x 960.
I've extended your move method to check for collision to see if it can move before it does. I've also removed your redraw() function since it was easier to just move your redraw down. If you wish, you can add it back in but for this example, I removed it.
import pygame
# Based on the paths in your image
# This should work for your image paths
image = pygame.image.load('model/standing1.png')
Standing = pygame.transform.scale(image, (32, 32))
image = pygame.image.load('map/floor.jpg')
Floor = pygame.transform.scale(image, (32, 32))
image = pygame.image.load('map/rightwall.png')
wallRight = pygame.transform.scale(image, (32, 32))
walkLeft = []
walkRight = []
walkUp = []
walkDown = []
for i in range(1, 19):
image = pygame.image.load('model/r' + str(i) + '.png')
walkRight.append(pygame.transform.scale(image, (32, 32)))
image = pygame.image.load('model/l' + str(i) + '.png')
walkLeft.append(pygame.transform.scale(image, (32, 32)))
image = pygame.image.load('model/u' + str(i) + '.png')
walkUp.append(pygame.transform.scale(image, (32, 32)))
image = pygame.image.load('model/d' + str(i) + '.png')
walkDown.append(pygame.transform.scale(image, (32, 32)))
class struc_Tile():
def __init__(self, block_path):
self.block_path = block_path
class player(object):
def __init__(self,x,y,width,height):
self.x = x
self.y = y
self.width = width
self.height = height
self.vel = 5
self.walkCount = 0
# do something like this with your images
# if you keep your lists in a dict you can avoid
# all your boolean direction variables and
# the lengthy if else statement
self.images = {}
self.images['walkleft'] = walkLeft[:]
self.images['walkright'] = walkRight[:]
self.images['walkup'] = walkUp[:]
self.images['walkdown'] = walkDown[:]
def draw(self, screen, direction):
if self.walkCount + 1 >= 18:
self.walkCount = 0
# since standing is not in your dict check for that first
if direction == 'standing':
screen.blit(Standing, (self.x,self.y))
self.walkCount = 0
else:
screen.blit(self.images[direction][self.walkCount], (self.x,self.y))
self.walkCount += 1
def can_move(self, dx, dy):
# with the buffer created around the border of the map
# you shouldn't have issues with
# index out of bounds exceptions
# EDIT: added better collision
new_x = self.x + dx
new_y = self.y + dy
if gamemap[new_x][new_y].block_path == False:
if gamemap[new_x + cellWidth][new_y].block_path == False:
if gamemap[new_x][new_y + cellHeight].block_path == False:
if gamemap[new_x + cellWidth][new_y + cellHeight].block_path == False:
self.x += dx
self.y += dy
return True
def createmap():
newmap = [[struc_Tile(False) for y in range(0, mapHeight)] for x in range (0,mapWidth)]
# give our upper/left borders a cell width buffer
# and our bottom/right borders a 2 cell width buffer
# since blit uses the upper left corner this should account
# for the sprite width
# EDIT: Fixed this to accommodate the better collision
for x in range(0, mapWidth):
for y in range (0, mapHeight):
if y < 32 or y + cellWidth >= mapHeight:
newmap[x][y].block_path = True
elif x < 32 or x + cellWidth >= mapWidth:
newmap[x][y].block_path = True
return newmap
def drawmap(maptodraw):
# only blit at cellwidth and height intervals
for x in range(0, mapWidth, cellWidth):
for y in range(0, mapHeight, cellHeight):
if maptodraw[x][y].block_path == True:
screen.blit(wallRight, (x, y))
else:
screen.blit(Floor, (x, y))
# Added this function which lets you block or unblock a cell
# simply call like gamemap = block_element(5, 10, gamemap)
# this will block the 6th cell on the x axis and 11th on the y axis
# to unblock a cell call it with block=False
def block_element(x, y, maptoblock, block=True):
x_cells = int(mapWidth / cellWidth)
y_cells = int(mapHeight / cellHeight)
start_x = int(x * cellWidth)
start_y = int(y * cellHeight)
end_x = start_x + cellWidth
end_y = start_y + cellHeight
print(start_x, end_x)
if x >= 0 and x < x_cells:
if y >= 0 and y < y_cells:
for x in range(start_x, end_x):
for y in range(start_y, end_y):
maptoblock[x][y].block_path = block
return maptoblock
pygame.init()
mapHeight = 960
mapWidth = 960
cellWidth = 32
cellHeight = 32
screen = pygame.display.set_mode((mapWidth, mapHeight))
gamemap = createmap()
# blocking/unblocking example
gamemap = block_element(5, 10, gamemap)
gamemap = block_element(0, 8, gamemap, block=False)
gamemap = block_element(0, 9, gamemap, block=False)
clock = pygame.time.Clock()
character = player(64, 64, 32, 32)
run = True
while run:
clock.tick(18)
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
# I just used fill since I didn't have a bg image handy
screen.fill((0, 0, 0))
drawmap(gamemap)
# handle the keypresses
# first check if the character can move that much
# then draw the movement
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] and character.can_move(0 - character.vel, 0):
character.draw(screen, 'walkleft')
elif keys[pygame.K_RIGHT] and character.can_move(character.vel, 0):
character.draw(screen, 'walkright')
elif keys[pygame.K_UP] and character.can_move(0, 0 - character.vel):
character.draw(screen, 'walkup')
elif keys[pygame.K_DOWN] and character.can_move(0, character.vel):
character.draw(screen, 'walkdown')
else:
character.draw(screen, 'standing')

Related

Blit is not detecting my Player classes self.pos

My code:
import pgzrun, pgzero, pygame, math, time, random, os
from pgzero.builtins import Actor, animate, keyboard
screen = pgzero.screen.Screen
# screen
WIDTH = 800
HEIGHT = 600
pygame.init()
pygame.mouse.set_visible(False)
os.chdir("c:/Users/carter.breshears/Documents/CSPVSC/Final/Images")
font = pygame.font.Font("Minecraft.ttf", 30)
# player images
playerIdle = pygame.image.load("playerIdle.png")
playerWalkImages = [pygame.image.load("playerRun1.png"), pygame.image.load("playerRun2.png"), pygame.image.load("playerRun3.png"), pygame.image.load("playerRun4.png")]
#variables
# gameloop
gameover = False
# classes
class Player:
def __init__(self, x, y):
self.x = x
self.y = y
self.animationCount = 0
self.movingRight = False
self.movingLeft = False
self.movingUp = False
self.movingDown = False
def main(self, screen):
if self.animationCount + 1 >= 24:
self.animationCount = 0
self.animationCount += 1
if self.movingRight or self.movingUp or self.movingDown:
screen.blit(pygame.transform.scale(playerWalkImages[self.animationCount//6], (40,74)), (self.x, self.y))
elif self.movingLeft:
screen.blit(pygame.transform.scale(pygame.transform.flip(playerWalkImages[self.animationCount//6], True, False), (40,74)), (self.x, self.y))
else:
screen.blit(pygame.transform.scale(playerIdle, (40,74)), (self.x, self.y)) ##### This line!
self.movingRight = False
self.movingLeft = False
self.movingUp = False
self.movingDown = False
class PlayerBullet:
def __init__(self, x, y, mouseX, mouseY):
self.x = x
self.y = y
self.mouseX = mouseX
self.mouseY = mouseY
speed = 6
self.angle = math.atan2(y - mouseY, x - mouseX)
self.x_vel = math.cos(self.angle) * speed
self.y_vel = math.sin(self.angle) * speed
def main(self, screen):
self.x -= int(self.x_vel)
self.y -= int(self.y_vel)
print("bang")
pygame.draw.circle(screen, "yellow", (self.x+16, self.y+16), 5)
class InvaderEnemy:
def __init__(self, x, y):
self.x = x
self.y = y
self.greenAnimationImages = [pygame.image.load("virus g.png"), pygame.image.load("virus g2.png")]
self.yellowAnimationImages = [pygame.image.load("virus y.png"), pygame.image.load("virus y2.png")]
self.blueAnimationImages = [pygame.image.load("virus b.png"), pygame.image.load("virus b2.png")]
self.redAnimationImages = [pygame.image.load("virus r.png"), pygame.image.load("virus r2.png")]
self.animationCount = 0
self.velocity = 1
self.lerpFactor = 0.05
def main(self, screen):
if self.animationCount + 1 == 8:
self.animationCount = 0
self.animationCount += 1
spawnPos = (random.randint(-1000, 1000), random.randint(-1000, 1000))
targetVector = pygame.math.Vector2(player.x, player.y)
enemyVector = pygame.math.Vector2(spawnPos.x, spawnPos.y)
newEnemyVector = pygame.math.Vector2(spawnPos.x, spawnPos.y)
distance = enemyVector.distance_to(targetVector)
minDistance = 25
maxDistance = 1000
if distance > minDistance:
directionVector = (targetVector - enemyVector)/2
self.minStep = max(0, distance - maxDistance)
self.maxStep = distance - minDistance
self.stepDistance = self.minStep + (self.maxStep - self.minStep) * self.lerpFactor
newEnemyVector = enemyVector + directionVector * self.stepDistance
return(newEnemyVector.x, newEnemyVector.y)
# player
player = Player(400,300)
displayScroll = [0,0]
# shooting
playerBullets = []
# invader
invaders = []
invaderEnemy = InvaderEnemy(100,100)
invaderSpawnRate = 1
# functions
def update():
# movement
keys = pygame.key.get_pressed()
if keys[pygame.K_a]:
displayScroll[0] -= 2
player.movingLeft = True
for bullet in playerBullets:
bullet.x += 2
if keys[pygame.K_d]:
displayScroll[0] += 2
player.movingRight = True
for bullet in playerBullets:
bullet.x -= 2
if keys[pygame.K_w]:
displayScroll[1] -= 2
player.movingUp = True
for bullet in playerBullets:
bullet.y += 2
if keys[pygame.K_s]:
displayScroll[1] += 2
player.movingDown = True
for bullet in playerBullets:
bullet.y -= 2
player.main(screen)
# shooting
mouseX, mouseY = pygame.mouse.get_pos()
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
playerBullets.append(PlayerBullet(player.x, player.y, mouseX, mouseY))
for bullet in playerBullets:
PlayerBullet.main(screen)
#enemies
global spawnPos
if time.perf_counter() - invaderSpawnRate > 1:
invaders.append(InvaderEnemy(spawnPos.x, spawnPos.y))
for invader in invaders:
InvaderEnemy.main(screen)
# run
pgzrun.go()
I've tried everything I know of, Im new to this. I know that it worked before because I tested this on my home computer but whenever I downloaded the file on my school computer it stopped working so something must have changed between python versions.

Pygame ValueError: subsurface rectangle outside surface area [duplicate]

This question already has answers here:
Subsurface ValueError: 'subsurface rectangle outside surface area'
(1 answer)
Subsurface rect outside of surface area when using get_clip() in Pygame?
(1 answer)
How can I crop an image with Pygame?
(4 answers)
Closed 2 months ago.
I am currently working on a game on Pygame in Python. It is a player vs player battle game. When the program is run the value error subsurface rectangle outside surface area is shown. I searched about the error but could not understand about it as I am new to Pygame.
My program-
import pygame
from Players import Characters
pygame.init()
#Game window
swidth = 1000
sheight= 600
s=pygame.display.set_mode((swidth, sheight))
pygame.display.set_caption("Brawler")
#Framerate
clock = pygame.time.Clock()
FPS = 60
#Game variables
count=3
last_count_update=pygame.time.get_ticks()
score = [0, 0]#player scores. [P1, P2]
round_over = False
round_cooldown = 2000
#Player variables
Knight_size = 278
Knight_scale = 4
Knight_offset = [72, 56]
Knight_data = [Knight_size, Knight_scale, Knight_offset]
Assassin_size = 288
Assassin_scale = 3
Assassin_offset = [112, 107]
Assassin_data =[ Assassin_size, Assassin_scale, Assassin_offset]
#load background image
bg_image = pygame.image.load("G1/Assets/Bg.png").convert_alpha()
#load spritesheets
Knight_sheet = pygame.image.load("G1/Assets/Knight/Sprites.png").convert_alpha()
Assassin_sheet = pygame.image.load("G1/Assets/Assassin/Sprites.png").convert_alpha()
#load vicory image
victory_img = pygame.image.load("G1/Assets/victory.png").convert_alpha()
#define number of steps in each animation
Knight_steps=[8, 8, 20, 8, 11, 19, 10, 6, 13]
Assassin_steps=[8, 8, 6, 6, 8, 18, 7, 6, 19]
#Font
count_font = pygame.font.Font("G1/assets/fonts/turok.ttf", 80)
score_font = pygame.font.Font("G1/assets/fonts/turok.ttf", 30)
#Text
def draw_text(text, font, text_col, x, y):
img = font.render(text, True, text_col)
s.blit(img, (x, y))
#Background
def draw_bg():
scaled_bg = pygame.transform.scale(bg_image, (swidth, sheight))
s.blit(scaled_bg, (0, 0))
#Health bars
red=(255, 0, 0)
yellow=(255, 255, 0)
white=(255, 255, 255)
def draw_health_bar(health, x, y):
ratio = health / 100
pygame.draw.rect(s, white, (x - 2, y - 2, 404, 34))
pygame.draw.rect(s, red, (x, y, 400, 30))
pygame.draw.rect(s, yellow, (x, y, 400 * ratio, 30))
#Player avatars
Knight=Characters(1, 200, 310, False, Knight_data, Knight_sheet, Knight_steps)
Assassin=Characters(2, 700, 310, True, Assassin_data, Assassin_sheet, Assassin_steps)
#game loop
run = True
while run:
clock.tick(FPS)
#Display background
draw_bg()
#Display health bar
draw_health_bar(Knight.health, 20, 20)
draw_health_bar(Assassin.health, 580, 20)
draw_text("P1: " + str(score[0]), score_font, red, 20, 60)
draw_text("P2: " + str(score[1]), score_font, red, 580, 60)
#countdown
if intro_count <= 0:
#Move fighters
Knight.move(swidth, sheight, s, Assassin, round_over)
Assassin.move(swidth, sheight, s, Knight, round_over)
else:
#Display count timer
draw_text(str(intro_count), count_font, red, swidth/2, sheight/3)
#update count timer
if (pygame.time.get_ticks()-last_count_update)>= 1000:
intro_count -= 1
last_count_update = pygame.time.get_ticks()
#Update fighters
Knight.update()
Assassin.update()
#Display fighters
Knight.draw(s)
Assassin.draw(s)
#check for player defeat
if round_over == False:
if Knight.alive == False:
score[1] += 1
round_over = True
round_over_time = pygame.time.get_ticks()
elif Assassin.alive == False:
score[0] += 1
round_over = True
round_over_time = pygame.time.get_ticks()
else:
#display victory image
s.blit(victory_img, (360, 150))
if pygame.time.get_ticks()-round_over_time>round_cooldown:
round_over = False
intro_count = 3
Knight= Knight(1, 200, 310, False, Knight_data, Knight_sheet, Knight_steps)
Assassin= Assassin(2, 700, 310, True, Assassin_data, Assassin_sheet, Assassin_steps)
#event handler
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
#update display
pygame.display.update()
#exit pygame
pygame.quit()`
My Module-
import pygame
class Characters():
def __init__(self, player, x, y, flip, data, sheet, steps):
self.player = player
self.size = data[0]
self.image_scale = data[1]
self.offset = data[2]
self.flip = flip
self.animation_list = self.load_images(sheet, steps)
self.action = 0#0:idle #1:run #2:jump #3:attack1 #4: attack2 #5:hit #6:death
self.frame_index = 0
self.image = self.animation_list[self.action][self.frame_index]
self.update_time = pygame.time.get_ticks()
self.rect = pygame.Rect((x, y, 80, 180))
self.vel_y = 0
self.running = False
self.jump = False
self.attacking = False
self.attack_type = 0
self.attack_cooldown = 0
self.hit = False
self.health = 100
self.alive = True
def load_images(self, sheet, steps):
#extract images from spritesheet
animation_list = []
for y, animation in enumerate(steps):
temp_img_list = []
for x in range(animation):
temp_img = sheet.subsurface(x * self.size, y * self.size, self.size, self.size)
temp_img_list.append(pygame.transform.scale(temp_img, (self.size * self.image_scale, self.size * self.image_scale)))
animation_list.append(temp_img_list)
return animation_list
def move(self, screen_width, screen_height, surface, target, round_over):
SPEED = 10
GRAVITY = 2
dx = 0
dy = 0
self.running = False
self.attack_type = 0
#get keypresses
key = pygame.key.get_pressed()
#can only perform other actions if not currently attacking
if self.attacking == False and self.alive == True and round_over == False:
#check player 1 controls
if self.player == 1:
#movement
if key[pygame.K_a]:
dx = -SPEED
self.running = True
if key[pygame.K_d]:
dx = SPEED
self.running = True
#jump
if key[pygame.K_w] and self.jump == False:
self.vel_y = -30
self.jump = True
#attack
if key[pygame.K_r] or key[pygame.K_t]:
self.attack(target)
#determine which attack type was used
if key[pygame.K_r]:
self.attack_type = 1
if key[pygame.K_t]:
self.attack_type = 2
#check player 2 controls
if self.player == 2:
#movement
if key[pygame.K_LEFT]:
dx = -SPEED
self.running = True
if key[pygame.K_RIGHT]:
dx = SPEED
self.running = True
#jump
if key[pygame.K_UP] and self.jump == False:
self.vel_y = -30
self.jump = True
#attack
if key[pygame.K_KP1] or key[pygame.K_KP2]:
self.attack(target)
#determine which attack type was used
if key[pygame.K_KP1]:
self.attack_type = 1
if key[pygame.K_KP2]:
self.attack_type = 2
#apply gravity
self.vel_y += GRAVITY
dy += self.vel_y
#ensure player stays on screen
if self.rect.left + dx < 0:
dx = -self.rect.left
if self.rect.right + dx > screen_width:
dx = screen_width - self.rect.right
if self.rect.bottom + dy > screen_height - 220:
self.vel_y = 0
self.jump = False
dy = screen_height - 220 - self.rect.bottom
#ensure players face each other
if target.rect.centerx > self.rect.centerx:
self.flip = False
else:
self.flip = True
#apply attack cooldown
if self.attack_cooldown > 0:
self.attack_cooldown -= 1
#update player position
self.rect.x += dx
self.rect.y += dy
#handle animation updates
def update(self):
#check what action the player is performing
if self.health <= 0:
self.health = 0
self.alive = False
self.update_action(6)#6:death
elif self.hit == True:
self.update_action(5)#5:hit
elif self.attacking == True:
if self.attack_type == 1:
self.update_action(3)#3:attack1
elif self.attack_type == 2:
self.update_action(4)#4:attack2
elif self.jump == True:
self.update_action(2)#2:jump
elif self.running == True:
self.update_action(1)#1:run
else:
self.update_action(0)#0:idle
animation_cooldown = 50
#update image
self.image = self.animation_list[self.action][self.frame_index]
#check if enough time has passed since the last update
if pygame.time.get_ticks() - self.update_time > animation_cooldown:
self.frame_index += 1
self.update_time = pygame.time.get_ticks()
#check if the animation has finished
if self.frame_index >= len(self.animation_list[self.action]):
#if the player is dead then end the animation
if self.alive == False:
self.frame_index = len(self.animation_list[self.action]) - 1
else:
self.frame_index = 0
#check if an attack was executed
if self.action == 3 or self.action == 4:
self.attacking = False
self.attack_cooldown = 20
#check if damage was taken
if self.action == 5:
self.hit = False
#if the player was in the middle of an attack, then the attack is stopped
self.attacking = False
self.attack_cooldown = 20
def attack(self, target):
if self.attack_cooldown == 0:
#execute attack
self.attacking = True
self.attack_sound.play()
attacking_rect = pygame.Rect(self.rect.centerx - (2 * self.rect.width * self.flip), self.rect.y, 2 * self.rect.width, self.rect.height)
if attacking_rect.colliderect(target.rect):
target.health -= 10
target.hit = True
def update_action(self, new_action):
#check if the new action is different to the previous one
if new_action != self.action:
self.action = new_action
#update the animation settings
self.frame_index = 0
self.update_time = pygame.time.get_ticks()
def draw(self, surface):
img = pygame.transform.flip(self.image, self.flip, False)
surface.blit(img, (self.rect.x - (self.offset[0] * self.image_scale), self.rect.y - (self.offset[1] * self.image_scale)))
The error occurred-
File "c:\Users\User\Desktop\G1\Battle game V1.py", line 73, in <module>
Knight=Characters(1, 200, 310, False, Knight_data, Knight_sheet, Knight_steps)
File "c:\Users\User\Desktop\G1\Players.py", line 10, in __init__
self.animation_list = self.load_images(sheet, steps)
File "c:\Users\User\Desktop\G1\Players.py", line 33, in load_images
temp_img = sheet.subsurface(x * self.size, y * self.size, self.size, self.size)
ValueError: subsurface rectangle outside surface area

Why does flipping my sprites cause it to move weird

I'm making a pyGame game and instead of getting sprites to both sides I decided to just flip it horizontally, the thing is it move perfect on the right side but on the left side it moves wrong like the center of the image changes every frame, is there anyway I can controll the images center.
for the record the function draw is being called from Main.py
This is my Player Class:
class Player(pygame.sprite.Sprite):
def __init__(self, char_type, x, y, scale, speed):
pygame.sprite.Sprite.__init__(self)
self.alive = True
self.char_type = char_type
self.speed = speed
self.direction = 1
self.vel_y = 0
self.jump = False
self.in_air = True
self.flip = False
self.attack = False
self.moving_left = False
self.moving_right = False
self.animation_list = []
self.frame_index = 0
self.action = 0
self.update_time = pygame.time.get_ticks()
#load all images for the players
animation_types = ['stand', 'walk', 'jump', 'attack1', 'attack2', 'attack3']
for animation in animation_types:
#reset temporary list of images
temp_list = []
#count number of files in the folder
num_of_frames = len(os.listdir(f'sprites/{self.char_type}/{animation}'))
for i in range(num_of_frames):
img = pygame.image.load(f'sprites/{self.char_type}/{animation}/{i}.png')
img = pygame.transform.scale(img, (int(img.get_width() * scale), int(img.get_height() * scale)))
temp_list.append(img)
self.animation_list.append(temp_list)
self.image = self.animation_list[self.action][self.frame_index]
self.rect = self.image.get_rect()
self.rect.center = (x, y)
def move(self, GRAVITY):
#reset movement variables
dx = 0
dy = 0
#assign movement variables if moving left or right
if self.moving_left:
dx = -self.speed
self.flip = True
self.direction = -1
if self.moving_right:
dx = self.speed
self.flip = False
self.direction = 1
# if self.attack:
# self.play_sound("attack")
#jump
if self.jump == True and self.in_air == False:
self.vel_y = -11
self.jump = False
self.in_air = True
# if self.attack == True:
# self.attack = False
#apply gravity
self.vel_y += GRAVITY
if self.vel_y > 10:
self.vel_y
dy += self.vel_y
#check collision with floor
if self.rect.bottom + dy > 450:
dy = 450 - self.rect.bottom
self.in_air = False
#update rectangle position
self.rect.x += dx
self.rect.y += dy
def update_animation(self):
#update animation
if self.action == 0:
animation_cooldown = 800
elif self.action == 1:
animation_cooldown = 170
elif self.action == 2:
animation_cooldown = 50
elif self.action == 3:
self.moving_left = False
self.moving_right = False
animation_cooldown = 250
# self.action = random.randint(3, 5)
#update image depending on current frame
self.image = self.animation_list[self.action][self.frame_index]
#check if enough time has passed since the last update
if pygame.time.get_ticks() - self.update_time > animation_cooldown:
self.update_time = pygame.time.get_ticks()
self.frame_index += 1
#if the animation has run out the reset back to the start
if self.frame_index >= len(self.animation_list[self.action]):
if self.action == 3:
self.attack = False
self.frame_index = 0
def update_action(self, new_action):
#check if the new action is different to the previous one
if new_action != self.action:
self.action = new_action
#update the animation settings
self.frame_index = 0
self.update_time = pygame.time.get_ticks()
def play_sound(self, sound):
soundObj = pygame.mixer.Sound('sprites/sounds/player/' + sound + '.mp3')
soundObj.play()
def draw(self, screen):
screen.blit(pygame.transform.flip(self.image, self.flip, False), self.rect)
You need to reverse the list for the flipped side. reverse the list in place:
temp_list.reverse()
self.animation_list.append(temp_list)
or append a reversed list:
self.animation_list.append(reversed(temp_list))
If your images are different sizes, you need to create images of the same size. Find the size of the largest image. Create a pygame.Surface for each image and blit the image onto the Surface:
for animation in animation_types:
max_image_size = [0, 0]
temp_list = []
num_of_frames = len(os.listdir(f'sprites/{self.char_type}/{animation}'))
for i in range(num_of_frames):
img = pygame.image.load(f'sprites/{self.char_type}/{animation}/{i}.png')
temp_list.append(img)
max_image_size[0] = max(max_image_size[0], img.get_width())
max_image_size[0] = max(max_image_size[1], img.get_height())
for i in range(num_of_frames):
img = pygame.Surface(max_image_size, pygame.SRCALPHA)
img.blit(temp_list[i], (0, 0))
temp_list[i] = pygame.transform.scale(img, (int(img.get_width() * scale), int(img.get_height() * scale)))
self.animation_list.append(temp_list)

Adding collision to maze walls

Can someone please help me adding collision points to my sprites. I had a past code where I layered a bitmap over images but the same code does not integrate well for physically drawing lines rather than detecting where the black/grey is on an image
import random
import pygame
pygame.init()
WHITE = (255,255,255)
GREY = (20,20,20)
BLACK = (0,0,0)
PURPLE = (100,0,100)
RED = (255,0,0)
size = (701,701)
screen = pygame.display.set_mode(size)
pygame.display.set_caption("Maze Generator")
done = False
clock = pygame.time.Clock()
width = 25
cols = int(size[0] / width)
rows = int(size[1] / width)
stack = []
pos = (0,0)
class Player(pygame.sprite.Sprite):
def __init__(self, image, pos, background):
super().__init__()
self.image = image
self.pos = pygame.Vector2(pos)
self.rect = self.image.get_rect(center=self.pos)
self.background = background
def update(self, events, dt):
pressed = pygame.key.get_pressed()
move = pygame.Vector2((0, 0))
if pressed[pygame.K_w]: move += (0, -1)
if pressed[pygame.K_a]: move += (-1, 0)
if pressed[pygame.K_s]: move += (0, 1)
if pressed[pygame.K_d]: move += (1, 0)
#if move.length() > 0: move.normalise_ip()
self.pos = self.pos + move*(dt/5)
self.rect.center = self.pos
if self.background:
new_rect.clamp_ip(self.background.get_rect())
new_pos = new_rect.center
hit_box = self.background.subsurface(new_rect)
for x in range(new_rect.width):
for y in range(new_rect.height):
if sum(hit_box.get_at((x, y))) < 500:
return
def load_background(filename=None):
name = filename if filename else "background.jpg"
background = pygame.image.load(name)
background = pygame.transform.rotate(background, -90)
background = pygame.transform.scale(background, (800,600))
return background
def load_player(background):
pimg = pygame.Surface((10, 10))
pimg.fill((200, 20, 20))
return Player(pimg, (25, 325), background)
class Cell():
def __init__(self,x,y):
global width
self.x = x * width
self.y = y * width
self.visited = False
self.current = False
self.walls = [True,True,True,True] # top , right , bottom , left
# neighbors
self.neighbors = []
self.top = 0
self.right = 0
self.bottom = 0
self.left = 0
self.next_cell = 0
def draw(self):
if self.current:
pygame.draw.rect(screen,RED,(self.x,self.y,width,width))
elif self.visited:
pygame.draw.rect(screen,WHITE,(self.x,self.y,width,width))
if self.walls[0]:
pygame.draw.line(screen,BLACK,(self.x,self.y),((self.x + width),self.y),1) # top
if self.walls[1]:
pygame.draw.line(screen,BLACK,((self.x + width),self.y),((self.x + width),(self.y + width)),1) # right
if self.walls[2]:
pygame.draw.line(screen,BLACK,((self.x + width),(self.y + width)),(self.x,(self.y + width)),1) # bottom
if self.walls[3]:
pygame.draw.line(screen,BLACK,(self.x,(self.y + width)),(self.x,self.y),1) # left
def checkNeighbors(self):
#print("Top; y: " + str(int(self.y / width)) + ", y - 1: " + str(int(self.y / width) - 1))
if int(self.y / width) - 1 >= 0:
self.top = grid[int(self.y / width) - 1][int(self.x / width)]
#print("Right; x: " + str(int(self.x / width)) + ", x + 1: " + str(int(self.x / width) + 1))
if int(self.x / width) + 1 <= cols - 1:
self.right = grid[int(self.y / width)][int(self.x / width) + 1]
#print("Bottom; y: " + str(int(self.y / width)) + ", y + 1: " + str(int(self.y / width) + 1))
if int(self.y / width) + 1 <= rows - 1:
self.bottom = grid[int(self.y / width) + 1][int(self.x / width)]
#print("Left; x: " + str(int(self.x / width)) + ", x - 1: " + str(int(self.x / width) - 1))
if int(self.x / width) - 1 >= 0:
self.left = grid[int(self.y / width)][int(self.x / width) - 1]
#print("--------------------")
if self.top != 0:
if self.top.visited == False:
self.neighbors.append(self.top)
if self.right != 0:
if self.right.visited == False:
self.neighbors.append(self.right)
if self.bottom != 0:
if self.bottom.visited == False:
self.neighbors.append(self.bottom)
if self.left != 0:
if self.left.visited == False:
self.neighbors.append(self.left)
if len(self.neighbors) > 0:
self.next_cell = self.neighbors[random.randrange(0,len(self.neighbors))]
return self.next_cell
else:
return False
def removeWalls(current_cell,next_cell):
x = int(current_cell.x / width) - int(next_cell.x / width)
y = int(current_cell.y / width) - int(next_cell.y / width)
if x == -1: # right of current
current_cell.walls[1] = False
next_cell.walls[3] = False
elif x == 1: # left of current
current_cell.walls[3] = False
next_cell.walls[1] = False
elif y == -1: # bottom of current
current_cell.walls[2] = False
next_cell.walls[0] = False
elif y == 1: # top of current
current_cell.walls[0] = False
next_cell.walls[2] = False
grid = []
for y in range(rows):
grid.append([])
for x in range(cols):
grid[y].append(Cell(x,y))
current_cell = grid[0][0]
next_cell = 0
# -------- Main Program Loop -----------
def main():
global current_cell
player = None
initialized = False
current_maze = None
dt = 0
screen_rect = screen.get_rect()
clock = pygame.time.Clock()
sprites = pygame.sprite.Group()
if not initialized:
#current_maze = 0
background = load_background()
background = None
player = load_player(background)
sprites.add(player)
initialized = True
play = False
while not done:
# --- Main event loop
for event in pygame.event.get():
if event.type == pygame.QUIT:
return
for y in range(rows):
for x in range(cols):
grid[y][x].draw()
if play == True:
pygame.init()
while not done:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
player_x = player.pos[0]
player_y = player.pos[1]
sprites.update(None, dt)
#screen.fill(pygame.Color('grey'))
#screen.blit(background, (0, 0))
sprites.draw(screen)
pygame.display.flip()
dt = clock.tick(60)
else:
current_cell.visited = True
current_cell.current = True
next_cell = current_cell.checkNeighbors()
if next_cell != False:
current_cell.neighbors = []
stack.append(current_cell)
removeWalls(current_cell,next_cell)
current_cell.current = False
current_cell = next_cell
elif len(stack) > 0:
current_cell.current = False
current_cell = stack.pop()
else:
play = True
pygame.display.flip()
main()
pygame.quit()
I would love if anyone could help or point me in the right direction.
end_rect = pygame.draw.rect(screen, RED, (800-width, 600-width, width, width))
if player.rect.colliderect(end_rect):
screen.fill(BLACK)
end = True
Ensure that the Player object is positioned in the center of cell of the grid. e.g. calculate a random position for the player:
def load_player(background):
pimg = pygame.Surface((10, 10))
pimg.fill((200, 20, 20))
px = random.randint(0, rows-1) * width + width//2
py = random.randint(0, cols-1) * width + width//2
return Player(pimg, (px, py), background)
To track the player, but highlight the player position, the current player position has to be tint in a different color, before the player is updated and is redrawn:
pygame.draw.rect(screen, (255, 164, 164), player.rect)
sprites.update(None, dt)
sprites.draw(screen)
For the collision test:
The current position of the player in the grid has to be identified.
The new possible new position of the player, dependent on the movement has to be identified.
If a wall is on the track from the current to the new position, then the movement has to be skipped
class Player(pygame.sprite.Sprite):
def __init__(self, image, pos, background):
super().__init__()
self.image = image
self.pos = pygame.Vector2(pos)
self.rect = self.image.get_rect(center=self.pos)
self.background = background
def update(self, events, dt):
pressed = pygame.key.get_pressed()
move = pygame.Vector2((0, 0))
# calculate maximum movement and current cell position
testdist = dt // 5 + 2
cellx = self.rect.centerx // width
celly = self.rect.centery // width
minx = self.rect.left // width
maxx = self.rect.right // width
miny = self.rect.top // width
maxy = self.rect.bottom // width
# test move up
if minx == maxx and pressed[pygame.K_w]:
nexty = (self.rect.top-testdist) // width
if celly == nexty or (nexty >= 0 and not grid[celly][cellx].walls[0]):
move += (0, -1)
# test move right
elif miny == maxy and pressed[pygame.K_d]:
nextx = (self.rect.right+testdist) // width
if cellx == nextx or (nextx < cols and not grid[celly][cellx].walls[1]):
move += (1, 0)
# test move down
elif minx == maxx and pressed[pygame.K_s]:
nexty = (self.rect.bottom+testdist) // width
if celly == nexty or (nexty < rows and not grid[celly][cellx].walls[2]):
move += (0, 1)
# test move left
elif miny == maxy and pressed[pygame.K_a]:
nextx = (self.rect.left-testdist) // width
if cellx == nextx or (nextx >= 0 and not grid[celly][cellx].walls[3]):
move += (-1, 0)
self.pos = self.pos + move*(dt/5)
self.rect.center = self.pos
If you want to restart the maze, the you've to get rid of the nested game loops. Use 1 game loop and a condition, which state if the game is in play state or initializing the maze.
To check if the player has reached the goal can be done by pygame.Rect.colliderect():
finished = pygame.Rect(0, 0, width, width).colliderect(player.rect)
To restart you've to reset the grid:
grid = []
for y in range(rows):
grid.append([])
for x in range(cols):
grid[y].append(Cell(x,y))
current_cell = grid[0][0]
Set the player to a new random position:
px = random.randint(0, rows-1) * width + width//2
py = random.randint(0, cols-1) * width + width//2
player.pos = pygame.Vector2(px, py)
player.rect = player.image.get_rect(center=player.pos)
Clear the background and reset the state play:
screen.fill(0)
play = False
Full main code:
def main():
global current_cell, grid
player = None
initialized = False
current_maze = None
dt = 0
screen_rect = screen.get_rect()
clock = pygame.time.Clock()
sprites = pygame.sprite.Group()
if not initialized:
#current_maze = 0
background = load_background()
background = None
player = load_player(background)
sprites.add(player)
initialized = True
play = False
while not done:
# --- Main event loop
for event in pygame.event.get():
if event.type == pygame.QUIT:
return
if play == True:
pygame.draw.rect(screen, (255, 164, 164), player.rect)
sprites.update(None, dt)
sprites.draw(screen)
dt = clock.tick(60)
finished = pygame.Rect(0, 0, width, width).colliderect(player.rect)
if finished:
# init new grid
grid = []
for y in range(rows):
grid.append([])
for x in range(cols):
grid[y].append(Cell(x,y))
current_cell = grid[0][0]
# create new random player positon
px = random.randint(0, rows-1) * width + width//2
py = random.randint(0, cols-1) * width + width//2
player.pos = pygame.Vector2(px, py)
player.rect = player.image.get_rect(center=player.pos)
# clear screen
screen.fill(0)
play = False
else:
current_cell.visited = True
current_cell.current = True
next_cell = current_cell.checkNeighbors()
if next_cell != False:
current_cell.neighbors = []
stack.append(current_cell)
removeWalls(current_cell,next_cell)
current_cell.current = False
current_cell = next_cell
elif len(stack) > 0:
current_cell.current = False
current_cell = stack.pop()
else:
play = True
for y in range(rows):
for x in range(cols):
grid[y][x].draw()
pygame.display.flip()
see also How do I prevent the player from moving through the walls in a maze?

Integrating sprite snippet into maze program

Im attempting intergrate this a code which auto generates a maze with my sprite code, but when I try and add my sprite code snippet the program keeps erroring.
Iv tried to remove and redefine variables and also aline the programs variables with my own snippet
import random
import pygame
pygame.init()
WHITE = (255,255,255)
GREY = (20,20,20)
BLACK = (0,0,0)
PURPLE = (100,0,100)
RED = (255,0,0)
size = (701,701)
screen = pygame.display.set_mode(size)
pygame.display.set_caption("Maze Generator")
done = False
clock = pygame.time.Clock()
width = 25
cols = int(size[0] / width)
rows = int(size[1] / width)
stack = []
pos = (0,0)
class Player(pygame.sprite.Sprite):
def __init__(self, image, pos, background):
super().__init__()
self.image = image
self.pos = pygame.Vector2(pos)
self.rect = self.image.get_rect(center=self.pos)
self.background = background
def update(self, events, dt):
pressed = pygame.key.get_pressed()
move = pygame.Vector2((0, 0))
if pressed[pygame.K_w]: move += (0, -1)
if pressed[pygame.K_a]: move += (-1, 0)
if pressed[pygame.K_s]: move += (0, 1)
if pressed[pygame.K_d]: move += (1, 0)
#if move.length() > 0: move.normalise_ip()
new_pos = self.pos + move*(dt/5)
new_rect = self.rect.copy()
new_rect.center = new_pos
new_rect.clamp_ip(self.background.get_rect())
new_pos = new_rect.center
hit_box = self.background.subsurface(new_rect)
for x in range(new_rect.width):
for y in range(new_rect.height):
if sum(hit_box.get_at((x, y))) < 500:
return
def load_background(filename):
background = (pygame.image.load("background.jpg"))
background = pygame.transform.rotate(background, -90)
background = pygame.transform.scale(background, (800,600))
return background
def load_player(background):
pimg = pygame.Surface((10, 10))
pimg.fill((200, 20, 20))
return Player(pimg, (25, 325), background)
class Cell():
def __init__(self,x,y):
global width
self.x = x * width
self.y = y * width
self.visited = False
self.current = False
self.walls = [True,True,True,True] # top , right , bottom , left
# neighbors
self.neighbors = []
self.top = 0
self.right = 0
self.bottom = 0
self.left = 0
self.next_cell = 0
def draw(self):
if self.current:
pygame.draw.rect(screen,RED,(self.x,self.y,width,width))
elif self.visited:
pygame.draw.rect(screen,WHITE,(self.x,self.y,width,width))
if self.walls[0]:
pygame.draw.line(screen,BLACK,(self.x,self.y),((self.x + width),self.y),1) # top
if self.walls[1]:
pygame.draw.line(screen,BLACK,((self.x + width),self.y),((self.x + width),(self.y + width)),1) # right
if self.walls[2]:
pygame.draw.line(screen,BLACK,((self.x + width),(self.y + width)),(self.x,(self.y + width)),1) # bottom
if self.walls[3]:
pygame.draw.line(screen,BLACK,(self.x,(self.y + width)),(self.x,self.y),1) # left
def checkNeighbors(self):
#print("Top; y: " + str(int(self.y / width)) + ", y - 1: " + str(int(self.y / width) - 1))
if int(self.y / width) - 1 >= 0:
self.top = grid[int(self.y / width) - 1][int(self.x / width)]
#print("Right; x: " + str(int(self.x / width)) + ", x + 1: " + str(int(self.x / width) + 1))
if int(self.x / width) + 1 <= cols - 1:
self.right = grid[int(self.y / width)][int(self.x / width) + 1]
#print("Bottom; y: " + str(int(self.y / width)) + ", y + 1: " + str(int(self.y / width) + 1))
if int(self.y / width) + 1 <= rows - 1:
self.bottom = grid[int(self.y / width) + 1][int(self.x / width)]
#print("Left; x: " + str(int(self.x / width)) + ", x - 1: " + str(int(self.x / width) - 1))
if int(self.x / width) - 1 >= 0:
self.left = grid[int(self.y / width)][int(self.x / width) - 1]
#print("--------------------")
if self.top != 0:
if self.top.visited == False:
self.neighbors.append(self.top)
if self.right != 0:
if self.right.visited == False:
self.neighbors.append(self.right)
if self.bottom != 0:
if self.bottom.visited == False:
self.neighbors.append(self.bottom)
if self.left != 0:
if self.left.visited == False:
self.neighbors.append(self.left)
if len(self.neighbors) > 0:
self.next_cell = self.neighbors[random.randrange(0,len(self.neighbors))]
return self.next_cell
else:
return False
def removeWalls(current_cell,next_cell):
x = int(current_cell.x / width) - int(next_cell.x / width)
y = int(current_cell.y / width) - int(next_cell.y / width)
if x == -1: # right of current
current_cell.walls[1] = False
next_cell.walls[3] = False
elif x == 1: # left of current
current_cell.walls[3] = False
next_cell.walls[1] = False
elif y == -1: # bottom of current
current_cell.walls[2] = False
next_cell.walls[0] = False
elif y == 1: # top of current
current_cell.walls[0] = False
next_cell.walls[2] = False
grid = []
for y in range(rows):
grid.append([])
for x in range(cols):
grid[y].append(Cell(x,y))
current_cell = grid[0][0]
next_cell = 0
# -------- Main Program Loop -----------
def main():
while not done:
# --- Main event loop
for event in pygame.event.get():
if event.type == pygame.QUIT:
return
global current_cell
play = False
current_cell.visited = True
current_cell.current = True
for y in range(rows):
for x in range(cols):
grid[y][x].draw()
next_cell = current_cell.checkNeighbors()
if next_cell != False:
current_cell.neighbors = []
stack.append(current_cell)
removeWalls(current_cell,next_cell)
current_cell.current = False
current_cell = next_cell
elif len(stack) > 0:
current_cell.current = False
current_cell = stack.pop()
play = True
"""
elif len(stack) == 0:
grid = []
for y in range(rows):
grid.append([])
for x in range(cols):
grid[y].append(Cell(x,y))
current_cell = grid[0][0]
next_cell = 0
"""
if play == True:
pygame.init()
screen_rect = screen.get_rect()
clock = pygame.time.Clock()
sprites = pygame.sprite.Group()
player = None
initialized = False
current_maze = None
dt = 0
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
if not initialized:
#current_maze = 0
background = load_background
player = load_player(background)
sprites.add(player)
initialized = True
player_x = player.pos[0]
player_y = player.pos[1]
if player_x >= 780 and 275 < player_y < 375:
current_maze += 1
# reset to first maze if all mazes were done
if current_maze >= mazes_len:
current_maze = 0
background = load_background(mazes[current_maze])
sprites.empty()
player = load_player(background)
sprites.add(player)
sprites.update(events, dt)
screen.fill(pygame.Color('grey'))
screen.blit(background, (0, 0))
sprites.draw(screen)
pygame.display.flip()
dt = clock.tick(60)
pygame.display.flip()
main()
pygame.quit()
It would be awesome if someone could help me find out what the actual issue is here as when i have scanned through the code I dont see any issues with the current variables listed as my code snippet doesnt share anything with the auto generating maze program.
The issue is the assignment of the function objectload_backgroundtobackground` in the main loop:
background = load_background
You have to call the function load_background and to assign the return value to background:
background = load_background()
I recommend to add a default argument value to the parameter of the function load_background:
def load_background(filename=None):
name = filename if filename else "background.jpg"
background = pygame.image.load(name)
background = pygame.transform.rotate(background, -90)
background = pygame.transform.scale(background, (800,600))
return background
If you want to discard the background optionally, then set
background = None
And check for if the background is present before it is draw or accessed:
if background:
screen.fill(pygame.Color('grey'))
screen.blit(background, (0, 0))
class Player(pygame.sprite.Sprite):
# [...]
def update(self, events, dt):
# [...]
if self.background:
new_rect.clamp_ip(self.background.get_rect())
new_pos = new_rect.center
hit_box = self.background.subsurface(new_rect)
for x in range(new_rect.width):
for y in range(new_rect.height):
if sum(hit_box.get_at((x, y))) < 500:
return
Avoid additional loops inside the main loop. The application should run in a single loop. In side the main loop there should be a single event loop.
the variable play is state, which indicates if the maze is set up or the game is in running state:
# initilization
# [...]
while not done:
# event loop
for event in pygame.event.get():
# [...]
if play:
# play
# [...]
else:
# create maze
# [...]
The state play has to be set when stack is empty:
if next_cell != False:
current_cell.neighbors = []
stack.append(current_cell)
removeWalls(current_cell,next_cell)
current_cell.current = False
current_cell = next_cell
elif len(stack) > 0:
current_cell.current = False
current_cell = stack.pop()
else:
play = True
The main function may look as follows:
def main():
global current_cell
player = None
initialized = False
current_maze = None
dt = 0
screen_rect = screen.get_rect()
clock = pygame.time.Clock()
sprites = pygame.sprite.Group()
if not initialized:
#current_maze = 0
background = load_background()
background = None
player = load_player(background)
sprites.add(player)
initialized = True
play = False
while not done:
# --- Main event loop
for event in pygame.event.get():
if event.type == pygame.QUIT:
return
for y in range(rows):
for x in range(cols):
grid[y][x].draw()
if play:
# [...]
sprites.update(None, dt)
# [...]
sprites.draw(screen)
pygame.display.flip()
dt = clock.tick(60)
else:
current_cell.visited = True
current_cell.current = True
next_cell = current_cell.checkNeighbors()
if next_cell != False:
current_cell.neighbors = []
stack.append(current_cell)
removeWalls(current_cell,next_cell)
current_cell.current = False
current_cell = next_cell
elif len(stack) > 0:
current_cell.current = False
current_cell = stack.pop()
else:
play = True
pygame.display.flip()
Finally the player won't move, because self.pos is never changed and self.rect is never set.
Set self.rect in Player.update:
def update(self, events, dt):
pressed = pygame.key.get_pressed()
move = pygame.Vector2((0, 0))
if pressed[pygame.K_w]: move += (0, -1)
if pressed[pygame.K_a]: move += (-1, 0)
if pressed[pygame.K_s]: move += (0, 1)
if pressed[pygame.K_d]: move += (1, 0)
self.pos = self.pos + move*(dt/5)
self.rect.center = self.pos

Categories