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')