Currently I've been spending around half an hour trying to figure out how to implement this script better. I've tried a few methods, but they didn't quite work.
When running the script, the player glitches a bit, and sometimes the player can only move in the blocks once it comes in contact.
x = 64*3
y = 0
xvel = 0
yvel = 0
grounded = True
playerRect = pygame.Rect ((x, y, 64, 64))
collidelist = []
level = ["#=======##=========================",
"#=======#==========================",
"#==###############=========###=====",
"#===============#####==============",
"#==================================",
"###################################"]
def makelevel (level):
x = y = 0
def checkline (line, x, y):
for character in line:
if character == "#":
block = pygame.draw.rect (screen, (50, 50, 255), (x * 64, y * 64, 64, 64))
collidelist.append (block)
x += 1
for line in level:
checkline (line, x, y)
y += 1
def move (xvel, yvel):
global x
global y
global playerRect
global collideList
x += xvel
y += yvel
for block in collidelist:
if playerRect.colliderect(block):
x += -xvel * 2
y += -yvel * 2
break
makelevel (level)
while True:
screen.fill ([0, 0, 0])
makelevel (level)
playerRect = pygame.Rect ((x, y, 64, 64))
pygame.draw.rect (screen, (255, 255, 255), playerRect)
for event in pygame.event.get ():
if event.type == pygame.QUIT:
pygame.quit ()
sys.exit ()
exit ()
pressed = pygame.key.get_pressed ()
if pressed [pygame.K_RIGHT]:
move (5, 0)
if pressed [pygame.K_LEFT]:
move (-5, 0)
if pressed [pygame.K_UP]:
move (0, -5)
if pressed [pygame.K_DOWN]:
move (0, 5)
pygame.display.update ()
To handle collisions with walls, the easiest solution is to move the player rect or sprite along the x-axis first, check if it collides with a wall and then set its rect.right = block.left
if we're moving to the right or rect.left = block.right
if we're moving to the left. Afterwards you do the same with the y-axis. We have to do it separately, otherwise we wouldn't know the direction and how to reset the position of the rect.
Here's an example:
import sys
import pygame
def makelevel(level):
collidelist = []
for y, line in enumerate(level):
for x, character in enumerate(line):
if character == "#":
block = pygame.Rect(x*64, y*64, 64, 64)
collidelist.append(block)
return collidelist
def move(xvel, yvel, player_rect, collideList):
# Move the rect along the x-axis first.
player_rect.x += xvel
# Check if it collides with a block.
for block in collidelist:
if player_rect.colliderect(block):
if xvel < 0: # We're moving to the left.
# Move the player out of the block.
player_rect.left = block.right
elif xvel > 0: # We're moving to the right.
player_rect.right = block.left
break
# Now do the same for the y-axis.
player_rect.y += yvel
for block in collidelist:
if player_rect.colliderect(block):
if yvel < 0:
player_rect.top = block.bottom
elif yvel > 0:
player_rect.bottom = block.top
break
pygame.init()
screen = pygame.display.set_mode((800, 600))
BLOCK_COLOR = (50, 50, 255)
BG_COLOR = (0, 0, 0)
level = ["#=======##=========================",
"#=======#==========================",
"#==###############=========###=====",
"#===============#####==============",
"#==================================",
"###################################",]
collidelist = makelevel(level)
player_rect = pygame.Rect((64*3, 0, 64, 64))
# A clock to limit the frame rate.
clock = pygame.time.Clock()
while True:
for event in pygame.event.get ():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
pressed = pygame.key.get_pressed()
if pressed[pygame.K_RIGHT]:
move(5, 0, player_rect, collidelist)
if pressed[pygame.K_LEFT]:
move(-5, 0, player_rect, collidelist)
if pressed[pygame.K_UP]:
move(0, -5, player_rect, collidelist)
if pressed[pygame.K_DOWN]:
move(0, 5, player_rect, collidelist)
# Draw everything.
screen.fill(BG_COLOR)
for rect in collidelist:
pygame.draw.rect(screen, BLOCK_COLOR, rect)
pygame.draw.rect(screen, (255, 255, 255), player_rect)
pygame.display.update()
clock.tick(30) # Limit frame rate to 30 fps.
There were some other issues with your code that I need to address (even though codereview is offtopic (check out http://codereview.stackexchange.com/)):
The most severe problem is that you call makelevel
in your main loop, so you append more and more rects to the collidelist
until you run out of memory. Create the list inside this function and then return and assign it to a variable above the while loop and just reuse it.
It's also not necessary to define the checkline
function inside of makelevel
.
Global variables can have a bad effect on the comprehensibility and maintainability of the code. Rather pass the needed variables to the functions that need them and return the results.
Use enumerate
instead of incrementing y
and x
by 1 in makelevel
.