Reputation: 281
I am programming a Bomberman game, right now I don't have any sprites but I use rectangles for it. This is my code:
import pygame
pygame.init()
WinWidth = 800
WinHeight = 608
p1x = 32
p1y = 32
vel = 1
clock = pygame.time.Clock()
win = pygame.display.set_mode((WinWidth, WinHeight))
def player():
pygame.draw.rect(win, (0, 200, 200), (p1x, p1y, 32, 32))
player_rect = pygame.Rect(p1x, p1y, tlw, tlh)
class Wall:
def __init__(self, x, y):
self.x = x
self.y = y
pygame.draw.rect(win, (50, 50, 50), (x, y, 32, 32))
class Breakable:
def __init__(self, x, y):
self.x = x
self.y = y
pygame.draw.rect(win, (200, 150, 100), (x, y, 32, 32))
game_map1 = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 10, 10, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 10, 10, 0],
[0, 10, 0, 2, 0, 2, 0, 2, 0, 0, 0, 2, 0, 2, 0, 2, 0, 10, 0],
[0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0],
[0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0],
[0, 2, 2, 2, 2, 10, 10, 2, 2, 10, 2, 2, 10, 10, 2, 2, 2, 2, 0],
[0, 2, 0, 2, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 2, 0, 2, 0],
[0, 2, 2, 2, 2, 2, 0, 2, 2, 10, 2, 2, 0, 2, 2, 2, 2, 2, 0],
[0, 2, 0, 2, 0, 2, 0, 2, 10, 10, 10, 2, 0, 2, 0, 2, 0, 2, 0],
[0, 0, 0, 2, 2, 10, 10, 10, 10, 10, 10, 10, 10, 10, 2, 2, 0, 0, 0],
[0, 2, 0, 2, 0, 2, 0, 2, 10, 10, 10, 2, 0, 2, 0, 2, 0, 2, 0],
[0, 2, 2, 2, 2, 2, 0, 2, 2, 10, 2, 2, 0, 2, 2, 2, 2, 2, 0],
[0, 2, 0, 2, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 2, 0, 2, 0],
[0, 2, 2, 2, 2, 10, 10, 2, 2, 10, 2, 2, 10, 10, 2, 2, 2, 2, 0],
[0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0],
[0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0],
[0, 10, 0, 2, 0, 2, 0, 2, 0, 0, 0, 2, 0, 2, 0, 2, 0, 10, 0],
[0, 10, 10, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 10, 10, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
run = True
while run:
pygame.init()
clock.tick(100)
win.fill((0, 255, 200))
y = 0
for layer in game_map1:
x = 0
for tile in layer:
if tile == 0:
Wall(x * 32, y * 32)
wall_rect = pygame.Rect(x * 32, y * 32, tlw, tlh)
if tile == 10:
pygame.Rect(x * 32, y * 32, 32, 32)
if tile == 2:
Breakable(x * 32, y * 32)
x += 1
y += 1
player()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
run = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_d:
if player_rect.colliderect(wall_rect):
p1x *= -1
p1y *= -1
else:
p1x += vel
elif event.key == pygame.K_a:
if player_rect.colliderect(wall_rect):
p1x *= -1
p1y *= -1
else:
p1x -= vel
if event.key == pygame.K_s:
if player_rect.colliderect(wall_rect):
p1x *= -1
p1y *= -1
else:
p1y += vel
elif event.key == pygame.K_w:
if player_rect.colliderect(wall_rect):
p1x *= -1
p1y *= -1
else:
p1y -= vel
pygame.display.update()
The x
and y
are at the top-left of the player rectangle - They are p1x
and p1y
.
Width and Height are tlw
and tlh
.
The Wall and Breakable are using tlw
and tlh
, too and they do not have their specific coordinates like the player has.
sooo... I watched a tutorial (https://www.youtube.com/watch?v=HCWI2f7tQnY&t=2s) and even tried to make a collision detection on my own. But I just can't do it. I don't have coordinates for the rectangles in the tilemap and there are very much rectangles in the tilemap, too. How could you make a collision detection with a tilemap with no coordinates and many rectangles? And yeah, the game doesn't have much stuff going on for now... I tried it with the pygame.Rect's player_rect
and wall_rect
and then using (colliderect)
but it didn't work and how can you make it the player_rect collides with ALL wall_rect's and not only with one.
My question is: How do I make a collision detection with the tilemaps with colliderect?
Upvotes: 1
Views: 161
Reputation:
I have refactored the entire code because the code you posted throws an error and i couldn't test my code. The way we have implemented collision detection here works but is not really effective. If you want to know which side is colliding so you can implement the game logic, then colliderect
is quite pointless. I suggest you ask a new question on how to build custom collision detection, since it's an entirely new topic but the following code solves the original question of how to implement collision detection using colliderect. The following example also shows how draw functions should be a separate method, which is the better way to do it. Btw, if tile == 10
you had put pygame.Rect(x * 32, y * 32, 32, 32)
in your question which basically does nothing, so i put pass
. If you want it to be a new type of wall, you can make a new class similar to Breakable
and Wall
. Final answer, hope it helped :).
import pygame
pygame.init()
WinWidth = 800
WinHeight = 608
clock = pygame.time.Clock()
win = pygame.display.set_mode((WinWidth, WinHeight))
class Player:
def __init__(self):
self.x = 32
self.y = 32
self.width = 20
self.height = 20
self.vel = 0.1
def draw(self):
pygame.draw.rect(win, (0, 200, 200), (self.x, self.y, 32, 32))
def getRect(self):
return pygame.Rect(self.x, self.y, self.width, self.height)
class Wall:
def __init__(self, rect):
self.rect = rect
self.x = rect.x
self.y = rect.y
def draw(self):
pygame.draw.rect(win, (50, 50, 50), self.rect)
def getRect(self):
return pygame.Rect(self.x, self.y, 32, 32)
class Breakable:
def __init__(self, rect):
self.rect = rect
self.x = rect.x
self.y = rect.y
def draw(self):
pygame.draw.rect(win, (200, 150, 100), self.rect)
def getRect(self):
return pygame.Rect(self.x, self.y, 32, 32)
game_map1 = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 10, 10, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 10, 10, 0],
[0, 10, 0, 2, 0, 2, 0, 2, 0, 0, 0, 2, 0, 2, 0, 2, 0, 10, 0],
[0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0],
[0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0],
[0, 2, 2, 2, 2, 10, 10, 2, 2, 10, 2, 2, 10, 10, 2, 2, 2, 2, 0],
[0, 2, 0, 2, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 2, 0, 2, 0],
[0, 2, 2, 2, 2, 2, 0, 2, 2, 10, 2, 2, 0, 2, 2, 2, 2, 2, 0],
[0, 2, 0, 2, 0, 2, 0, 2, 10, 10, 10, 2, 0, 2, 0, 2, 0, 2, 0],
[0, 0, 0, 2, 2, 10, 10, 10, 10, 10, 10, 10, 10, 10, 2, 2, 0, 0, 0],
[0, 2, 0, 2, 0, 2, 0, 2, 10, 10, 10, 2, 0, 2, 0, 2, 0, 2, 0],
[0, 2, 2, 2, 2, 2, 0, 2, 2, 10, 2, 2, 0, 2, 2, 2, 2, 2, 0],
[0, 2, 0, 2, 0, 10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 2, 0, 2, 0],
[0, 2, 2, 2, 2, 10, 10, 2, 2, 10, 2, 2, 10, 10, 2, 2, 2, 2, 0],
[0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0],
[0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0],
[0, 10, 0, 2, 0, 2, 0, 2, 0, 0, 0, 2, 0, 2, 0, 2, 0, 10, 0],
[0, 10, 10, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 10, 10, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
walls = []
y = 0
for layer in game_map1:
x = 0
for tile in layer:
if tile == 0:
walls.append(Wall(pygame.Rect([x*32, y*32, 32, 32])))
if tile == 10:
pass
if tile == 2:
walls.append(Breakable(pygame.Rect([x*32, y*32, 32, 32])))
x += 1
y += 1
player = Player()
pygame.init()
run = True
while run:
clock.tick(100)
win.fill((0, 255, 200))
for i in range(len(walls)):
walls[i].draw()
player.draw()
for wall in walls:
if player.getRect().colliderect(wall.getRect()): #getRect is the method we added to wall class earlier
print("they are colliding")
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
run = False
if event.type == pygame.KEYDOWN:
for wall in walls:
wall_rect = wall.getRect()
if event.key == pygame.K_d:
if player.getRect().colliderect(wall_rect):
player.x *= -1
player.y *= -1
else:
player.x += player.vel
elif event.key == pygame.K_a:
if player.getRect().colliderect(wall_rect):
player.x *= -1
player.y *= -1
else:
player.x -= player.vel
if event.key == pygame.K_s:
if player.getRect().colliderect(wall_rect):
player.x *= -1
player.y *= -1
else:
player.y += player.vel
elif event.key == pygame.K_w:
if player.getRect().colliderect(wall_rect):
player.x *= -1
player.y *= -1
else:
player.y -= player.vel
pygame.display.update()
Regarding your last question:
#Checks if right side of rect1 is colliding with left side of rect2
def rightCheck(rect1, rect2):
if rect1.x + rect1.width > rect2.x:
if (rect1.y > rect2.y and rect1.y < rect2.y + rect2.height) or (rect1.y + rect1.height < rect2.y + rect2.height and rect1.y + rect1.height> rect2.y):
if rect1.x < rect2.x:
return True
return False
#Checks if top side of rect1 is colliding with bottom side of rect2
def topCheck(rect1, rect2):
if rect1.y < rect2.y + rect2.height:
if (rect1.x > rect2.x and rect1.x < rect2.x + rect2.width) or (rect1.x + rect1.width > rect2.x and rect1.x + rect1.width < rect2.x + rect2.width):
if rect1.y > rect2.y:
return True
return False
#Checks if bottom side of rect1 is colliding with top side of rect2
def botCheck(rect1, rect2):
if rect1.y + rect1.height > rect2.y:
if (rect1.x > rect2.x and rect1.x < rect2.x + rect2.width) or (rect1.x + rect1.width > rect2.x and rect1.x + rect1.width < rect2.x + rect2.width):
if rect1.y < rect2.y:
return True
return False
These are custom collision detection functions you can use. I haven't got the one for left side of the player (rect1) but i will try to make it later or you can try and make one as well. You can try to improve the ones i have provided to fit your game as well. Using these you can figure out exactly which side is colliding, and add useful logic. Example:
#have different velocities for every side
player_right_speed = 2
player_left_speed = 2
# Say that your player moves according to the following key presses
key = pygame.key.get_pressed()
if key[pygame.K_RIGHT]:
playerx += player_right_speed
if key[pygame.K_LEFT]:
playerx -= player_left_speed
#you can call the collision detection functions to check if they are colliding and if they are set the speed in that direction to 0
#notice these functions take pygame.Rect as an argument
right_colliding = rightCheck(playerRect, tileRect)
if right_colliding:
player_right_speed = 0
left_colliding = leftCheck(playerRect, tileRect)
if left_colliding:
player_left_speed = 0
Upvotes: 2