I am trying to work out how to detect and resolve collisions between moving sprites in pygame, I followed the logic of this video: https://www.youtube.com/watch?v=VpSWuywFlC8
The basic idea is that I track the current rectangle position as well as the previous rectangle position for each sprite and use that to detect where collisions have come from.
For example, to check if the player has collided at the bottom I use: player.rect.bottom >= sprite.rect.top and player.old_rect.bottom < sprite.old_rect.top that way I know that the player's bottom is in the other sprite and that it was above it in the previous frame.
The problem I have is that the collision detection only works sporadically and thus also breaks the collision resolution. Although, if I only look at a collision on a single side it does work. I am just not sure what the problem is with the collision detection.
Code so far:
import pygame, sys
class StaticObstacle(pygame.sprite.Sprite):
def __init__(self,pos,size,groups):
super().__init__(groups)
self.image = pygame.Surface(size)
self.image.fill('yellow')
self.rect = self.image.get_rect(topleft = pos)
self.old_rect = self.rect
class MovingVerticalObstacle(StaticObstacle):
def __init__(self,pos,size,groups):
super().__init__(pos, size, groups)
self.image.fill('green')
self.direction = pygame.math.Vector2((0,1))
self.speed = 8
def update(self):
self.old_rect = self.rect.copy()
if self.rect.bottom >= 600 or self.rect.top <= 120:
self.direction.y *= -1
self.rect.y += self.direction.y * self.speed
class MovingHorizontalObstacle(StaticObstacle):
def __init__(self,pos,size,groups):
super().__init__(pos, size, groups)
self.image.fill('purple')
self.direction = pygame.math.Vector2((1,0))
self.speed = 6
def update(self):
self.old_rect = self.rect.copy()
if self.rect.right >= 1000 or self.rect.left <= 600:
self.direction.x *= -1
self.rect.x += self.direction.x * self.speed
class Player(pygame.sprite.Sprite):
def __init__(self,groups,obstacles):
super().__init__(groups)
# image
self.image = pygame.Surface((30,60))
self.image.fill('blue')
# position
self.rect = self.image.get_rect(topleft = (640,360))
self.old_rect = self.rect.copy()
# movement
self.direction = pygame.math.Vector2()
self.speed = 5
self.obstacles = obstacles
def input(self):
keys = pygame.key.get_pressed()
# movement input
if keys[pygame.K_UP]:
self.direction.y = -1
elif keys[pygame.K_DOWN]:
self.direction.y = 1
else:
self.direction.y = 0
if keys[pygame.K_RIGHT]:
self.direction.x = 1
elif keys[pygame.K_LEFT]:
self.direction.x = -1
else:
self.direction.x = 0
def collisions(self):
collision_sprites = pygame.sprite.spritecollide(self,self.obstacles,False)
if collision_sprites:
for sprite in collision_sprites:
if self.rect.top <= sprite.rect.bottom and self.old_rect.top > sprite.old_rect.bottom:
print('top collision')
self.rect.top = sprite.rect.bottom
elif self.rect.bottom >= sprite.rect.top and self.old_rect.bottom < sprite.old_rect.top:
print('bottom collision')
self.rect.bottom = sprite.rect.top
elif self.rect.right >= sprite.rect.left and self.old_rect.right < sprite.old_rect.left:
print('right collision')
self.rect.right = sprite.rect.left
elif self.rect.left >= sprite.rect.right and self.old_rect.left < sprite.old_rect.right:
print('left collision')
self.rect.left = sprite.rect.right
def update(self):
self.old_rect = self.rect.copy()
self.input()
self.rect.topleft += self.direction * self.speed
self.collisions()
# general setup
pygame.init()
screen = pygame.display.set_mode((1280,720))
clock = pygame.time.Clock()
# group setup
all_sprites = pygame.sprite.Group()
collision_sprites = pygame.sprite.Group()
# sprite setup
StaticObstacle((100,300),(100,50),[all_sprites,collision_sprites])
StaticObstacle((800,600),(100,200),[all_sprites,collision_sprites])
StaticObstacle((900,200),(200,10),[all_sprites,collision_sprites])
MovingVerticalObstacle((200,300),(200,60),[all_sprites,collision_sprites])
MovingHorizontalObstacle((850,350),(100,100),[all_sprites,collision_sprites])
Player(all_sprites,collision_sprites)
# loop
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
screen.fill('black')
all_sprites.update()
all_sprites.draw(screen)
pygame.display.update()
clock.tick(60)