Here's what I described in the comment. Check if the sprites are colliding (I use spritecollide
and the pygame.sprite.collide_mask
functions here), and then use the normalized negative velocity vector to move the player backwards until it doesn't collide with the obstacle anymore.
import pygame as pg
from pygame.math import Vector2
pg.init()
screen = pg.display.set_mode((800, 600))
GRAY = pg.Color('gray12')
CIRCLE_BLUE = pg.Surface((70, 70), pg.SRCALPHA)
pg.draw.circle(CIRCLE_BLUE, (0, 0, 230), (35, 35), 35)
CIRCLE_RED = pg.Surface((170, 170), pg.SRCALPHA)
pg.draw.circle(CIRCLE_RED, (230, 0, 0), (85, 85), 85)
class Player(pg.sprite.Sprite):
def __init__(self, pos, key_left, key_right, key_up, key_down):
super().__init__()
self.image = CIRCLE_BLUE
self.mask = pg.mask.from_surface(self.image)
self.rect = self.image.get_rect(topleft=pos)
self.vel = Vector2(0, 0)
self.pos = Vector2(self.rect.topleft)
self.dt = 0.03
self.key_left = key_left
self.key_right = key_right
self.key_up = key_up
self.key_down = key_down
def handle_event(self, event):
if event.type == pg.KEYDOWN:
if event.key == self.key_left:
self.vel.x = -230
elif event.key == self.key_right:
self.vel.x = 230
elif event.key == self.key_up:
self.vel.y = -230
elif event.key == self.key_down:
self.vel.y = 230
elif event.type == pg.KEYUP:
if event.key == self.key_left and self.vel.x < 0:
self.vel.x = 0
elif event.key == self.key_right and self.vel.x > 0:
self.vel.x = 0
elif event.key == self.key_down and self.vel.y > 0:
self.vel.y = 0
elif event.key == self.key_up and self.vel.y < 0:
self.vel.y = 0
def update(self, dt):
self.pos += self.vel * dt
self.rect.center = self.pos
class Obstacle(pg.sprite.Sprite):
def __init__(self, pos):
super().__init__()
self.image = CIRCLE_RED
self.mask = pg.mask.from_surface(self.image)
self.rect = self.image.get_rect(topleft=pos)
class Game:
def __init__(self):
self.done = False
self.clock = pg.time.Clock()
self.screen = screen
self.player = Player((100, 50), pg.K_a, pg.K_d, pg.K_w, pg.K_s)
obstacle = Obstacle((300, 240))
self.all_sprites = pg.sprite.Group(self.player, obstacle)
self.obstacles = pg.sprite.Group(obstacle)
def run(self):
while not self.done:
self.dt = self.clock.tick(60) / 1000
self.handle_events()
self.run_logic()
self.draw()
pg.quit()
def handle_events(self):
for event in pg.event.get():
if event.type == pg.QUIT:
self.done = True
elif event.type == pg.MOUSEBUTTONDOWN:
if event.button == 2:
print(BACKGROUND.get_at(event.pos))
self.player.handle_event(event)
def run_logic(self):
self.all_sprites.update(self.dt)
collided_sprites = pg.sprite.spritecollide(
self.player, self.obstacles, False, pg.sprite.collide_mask)
for obstacle in collided_sprites:
# The length of the velocity vector tells us how many steps we need.
for _ in range(int(self.player.vel.length())+1):
# Move back. Use the normalized velocity vector.
self.player.pos -= self.player.vel.normalize()
self.player.rect.center = self.player.pos
# Break out of the loop when the masks aren't touching anymore.
if not pg.sprite.collide_mask(self.player, obstacle):
break
def draw(self):
self.screen.fill(GRAY)
self.all_sprites.draw(self.screen)
pg.display.flip()
if __name__ == '__main__':
Game().run()