Last active
October 15, 2025 21:37
-
-
Save nitori/59b3c2d6a9e6f1f36e9334ea4da0f17c to your computer and use it in GitHub Desktop.
simple collision example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import pygame | |
| # direction normals | |
| LEFT = pygame.Vector2(-1, 0) | |
| RIGHT = pygame.Vector2(1, 0) | |
| UP = pygame.Vector2(0, -1) | |
| DOWN = pygame.Vector2(0, 1) | |
| ZERO = pygame.Vector2(0, 0) | |
| class Player: | |
| def __init__(self, pos, speed=500): | |
| self.image = pygame.Surface((16, 32)) | |
| self.image.fill('green') | |
| self.speed = speed | |
| self.rect = pygame.FRect(self.image.get_rect(center=pos)) | |
| def update(self, delta: float): | |
| keys = pygame.key.get_pressed() | |
| direction = pygame.Vector2( | |
| keys[pygame.K_d] - keys[pygame.K_a], | |
| keys[pygame.K_s] - keys[pygame.K_w], | |
| ) | |
| if direction.length_squared() > 0: | |
| direction.normalize_ip() | |
| movement = direction * self.speed * delta | |
| return movement | |
| return ZERO | |
| def apply_x(self, movement): | |
| self.rect.x += movement.x | |
| def apply_y(self, movement): | |
| self.rect.y += movement.y | |
| def check_collision(rect, other_rects): | |
| collision = None | |
| for other_rect in other_rects: | |
| if other_rect is rect: | |
| continue # don't collide with self | |
| if rect.colliderect(other_rect): | |
| # the distances between the two centers x and y | |
| dx = other_rect.centerx - rect.centerx | |
| dy = other_rect.centery - rect.centery | |
| # the sum of the half widths/heights of both rects. | |
| hw = (other_rect.width + rect.width) / 2 | |
| hh = (other_rect.height + rect.height) / 2 | |
| # calculate the overlap in both directions | |
| overlap_x = hw - abs(dx) | |
| overlap_y = hh - abs(dy) | |
| # we pick the smaller overlap for this wall, to move as little as possible to get out of it. | |
| if overlap_x < overlap_y: | |
| normal = LEFT if dx > 0 else RIGHT | |
| depth = overlap_x | |
| else: | |
| normal = UP if dy > 0 else DOWN | |
| depth = overlap_y | |
| # if we collide with more than one wall, we take the larger overlap. | |
| if not collision or depth > collision[1]: | |
| collision = normal, depth | |
| return collision | |
| def main(): | |
| pygame.init() | |
| screen = pygame.display.set_mode((800, 600)) | |
| clock = pygame.Clock() | |
| player = Player((400, 300)) | |
| WALLS = [ | |
| pygame.Rect(10, 10, 50, screen.height - 20), | |
| ] | |
| while True: | |
| for event in pygame.event.get(): | |
| if event.type == pygame.QUIT: | |
| pygame.quit() | |
| return | |
| delta = clock.tick(60) / 1000 | |
| proposal = player.update(delta) | |
| # first try x | |
| player.apply_x(proposal) | |
| if hit := check_collision(player.rect, WALLS): | |
| move_by = hit[0] * hit[1] | |
| player.rect.x += move_by.x | |
| # then try y | |
| player.apply_y(proposal) | |
| if hit := check_collision(player.rect, WALLS): | |
| move_by = hit[0] * hit[1] | |
| player.rect.y += move_by.y | |
| screen.fill('black') | |
| for wall in WALLS: | |
| pygame.draw.rect(screen, 'white', wall) | |
| screen.blit(player.image, player.rect) | |
| pygame.display.flip() | |
| if __name__ == '__main__': | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment