Search code examples
pythonpygamecollision-detectionpygame-surface

How to make a collision system in pygame?


How can i make a collision system in the simplest way in pygame, without using classes. I've been trying for a long time and none of the ways works, in my last attempt the code was like this: (Sorry if it's wrongly written, I'm a beginner in all this)

import pygame
from pygame.locals import *
from sys import exit

pygame.init()

G = 9.807 # GRAVITY

screen = pygame.display.set_mode((400, 400))
time = pygame.time.Clock()

y_speed = 0
playersize = [20, 20]
playerpos = [0, 0]

def move(dx):
    playerpos[0] += dx

    if player.colliderect(block):
        if dx > 0:
            playerpos[0] -= player.right - block.left
        if dx < 0:
            playerpos[0] -= player.left - block.right
               
while 1:
    time.tick(30)
    screen.fill((255,255,255))
    player = pygame.draw.rect(screen, (255,0,0), (playerpos[0], playerpos[1], playersize[0], playersize[1]))
    block = pygame.draw.rect(screen, (0,0,0), (100, 380, 20, 20))

    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            exit()
        if event.type == KEYDOWN:
            if event.key == K_SPACE or event.key == K_UP or event.key == K_w:
                if playerpos[1] == 380:
                    y_speed = -5
                    time = pygame.time.Clock()
                else:
                    pass
    
    T = time.get_time() / 1000
    F = G * T
    y_speed += F
    playerpos[1] += y_speed
    
    if playerpos[1] >= 380:
        playerpos[1] = 380
        y_speed = 0
        time = pygame.time.Clock()
    else:
        pass
    
    keys = pygame.key.get_pressed()
    if keys[K_a] or keys[K_LEFT]:
        move(-5)
    if keys[K_d] or keys[K_RIGHT]:
        move(5)
        
    pygame.display.update()

Solution

  • You must create a pygame.Rect object with the current position of the player before the collision test. If the player hits the block from the left, the player`s right must be set by the left of the block. If the player hits the block from the right, the player's right must be set by the left of the block:

    def move(dx):
        playerpos[0] += dx
    
        player = pygame.Rect((playerpos[0], playerpos[1], playersize[0], playersize[1]))
        if player.colliderect(block):
            if dx > 0:
                player.right = block.left
                playerpos[0] = player.left
            if dx < 0:
                player.left = block.right
                playerpos[0] = player.left
    

    You can add the collision for the y-direction in a similar way. However, you must perform the collision detection in y direction before the collision detection in x direction:

    def move(dx, dy):
    
        playerpos[1] += dy
        player = pygame.Rect((playerpos[0], playerpos[1], playersize[0], playersize[1]))
        if player.colliderect(block):
            if dy > 0:
                player.bottom = block.top
                playerpos[1] = player.top
    
        playerpos[0] += dx
        player = pygame.Rect((playerpos[0], playerpos[1], playersize[0], playersize[1]))
        if player.colliderect(block):
            if dx > 0:
                player.right = block.left
                playerpos[0] = player.left
            if dx < 0:
                player.left = block.right
                playerpos[0] = player.left
    

    Complete and working example:

    import pygame
    from pygame.locals import *
    from sys import exit
    
    pygame.init()
    
    G = 9.807 # GRAVITY
    
    screen = pygame.display.set_mode((400, 400))
    time = pygame.time.Clock()
    
    y_speed = 0
    playersize = [20, 20]
    playerpos = [0, 0]
    
    def move(dx, dy):
    
        playerpos[1] += dy
        player = pygame.Rect((playerpos[0], playerpos[1], playersize[0], playersize[1]))
        if player.colliderect(block):
            if dy > 0:
                player.bottom = block.top
                playerpos[1] = player.top
    
        playerpos[0] += dx
        player = pygame.Rect((playerpos[0], playerpos[1], playersize[0], playersize[1]))
        if player.colliderect(block):
            if dx > 0:
                player.right = block.left
                playerpos[0] = player.left
            if dx < 0:
                player.left = block.right
                playerpos[0] = player.left
    
    block = pygame.Rect(150, 330, 20, 20)
    ground_y = 350
    
    run = True            
    while run:
        time.tick(30)
        for event in pygame.event.get():
            if event.type == QUIT:
                run = False
            if event.type == KEYDOWN:#
                if event.key == K_SPACE or event.key == K_UP or event.key == K_w:
                    if playerpos[1] == ground_y - playersize[1]:
                        y_speed = -5
    
        keys = pygame.key.get_pressed()
        move_left = keys[K_a] or keys[K_LEFT]
        move_right = keys[K_d] or keys[K_RIGHT]
        x_speed = (move_right - move_left) * 5
        
        T = time.get_time() / 1000
        F = G * T
        y_speed += F
        move(x_speed, y_speed)
        if playerpos[1] >= ground_y - playersize[1]:
            playerpos[1] = ground_y - playersize[1]
            y_speed = 0
            
        screen.fill((255,255,255))
        pygame.draw.rect(screen, "gray", (0, ground_y, 400, 400-ground_y))
        pygame.draw.rect(screen, (255,0,0), (playerpos[0], playerpos[1], playersize[0], playersize[1]))
        pygame.draw.rect(screen, (0,0,0), block)
        pygame.display.update()
    
    pygame.quit()
    exit()