Python Forum
can't get collision detection to work in platform game
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
can't get collision detection to work in platform game
#1
the code isnt producing any errors, but the player just falls through the ground and platforms i set up, im not sure what the problem is!! any help would be super appreciated Angel

import pygame, sys  # importing the library pygame and sys
import time  # imports time library
from pygame.locals import *  # import all pygames modules


windowWidth = 960  # 64 * 15
windowHeight = 640  # 64 * 10
fps = 60  # frames per second/ updates and redraws per second
main = True  # main game loop is always true

playerimg1 = pygame.image.load("idleghost.png")
playerx = 0
playery = 512
playerspeed = 5
playerrect = pygame.Rect(playerx, playery, 64, 64)
rightmovement = False  # variables for player movement
leftmovement = False
jump = False
player_y_momentum = 0

tilesize = 64
mapList = [['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a'],
           ['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a'],
           ['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a'],
           ['a', 'a', 'a', 'a', 'a', 'a', 'p', 'p', 'p', 'a', 'a', 'a', 'a', 'a', 'a'],
           ['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'p', 'p', 'a'],
           ['a', 'a', 'p', 'p', 'p', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a'],
           ['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a'],
           ['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a'],
           ['a', 'a', 'a', 'a', 'a', 'a', 'p', 'p', 'a', 'a', 'a', 'a', 'a', 'a', 'a'],  
           ['g', 'g', 'g', 'g', 'g', 'g', 'g', 'g', 'g', 'g', 'g', 'g', 'g', 'g', 'g']]

background = pygame.image.load("wall.png")
floor = pygame.image.load("floorboardtile.png")
shelf = pygame.image.load("bookshelf.png")

clock = pygame.time.Clock()  # setting up the clock

pygame.init()  # initializing pygame
window = pygame.display.set_mode((windowWidth, windowHeight))  # initializing the window/screen

pygame.display.set_caption("Platform Game")  # name of window


def collision_test(rect, tiles):
    hit_list = []
    for tile in tiles:
        if rect.colliderect(tile):
            hit_list.append(tile)
    return hit_list


def move(rect, playerx, playery, tiles):
    collision_types = {'top': False, 'bottom': False, 'right': False, 'left': False}
    rect.x += playerx
    hit_list = collision_test(rect, tiles)
    for tile in hit_list:
        if rightmovement == True:
            rect.right = tile.left
            collision_types['right'] = True
        if leftmovement == True:
            rect.left = tile.right
            collision_types['left'] = True
    rect.y += playery
    hit_list = collision_test(rect, tiles)
    for tile in hit_list:
        if jump == False:
            rect.bottom = tile.top
            collision_types['bottom'] = True
        if jump == True:
            rect.top = tile.bottom
            collision_types['top'] = True
    return rect, collision_types


while main == True:  # main game loop
    window.blit(background,
                (0, 0))  # renders the background image, blit is used to place one surface upon another
    playerrect = pygame.Rect(playerx, playery, 60, 60)
    tile_rects = []  # sets up list of tile rects
    y = 0  # keeps track of x,y coordinates in tile map
    for row in mapList:  # renders the tiles in the map list / for each row in the map list
        x = 0
        for tile in row:  # for each tile in each row
            if tile == 'g':  # if a tile is the ground
                window.blit(floor,
                            (x * tilesize, y * tilesize))  # render the floor image in the current co-ords

            if tile == 'p':  # if a tile is a platform
                window.blit(shelf, (x * tilesize, y * tilesize))
            if tile != 'a':
                tile_rects.append(pygame.Rect(x * tilesize, y * tilesize, tilesize,
                                              tilesize))  # add this tile to the tile rects list
            x += 1  # increase the x co-ord by 1
        y += 1  # increase the y co-ord by 1
    playery += player_y_momentum  # add the players vertical momentum to their y position
    player_y_momentum += 0.2  # in every frame add 0.2 to the vertical momentum
    if player_y_momentum > 3:  # stops the players vertical momentum being larger than 3
        player_y_momentum = 3
    playerrect, collisions_type = move(playerrect, playerx, playery, tile_rects)

    if collisions_type['bottom']:
        player_y_momentum = 0
    window.blit(playerimg1, (playerx, playery))  # renders players image

    for event in pygame.event.get():  # the event loop
        if event.type == pygame.QUIT:  # checks if the window is being closed
            pygame.quit()  # stops pygame
            sys.exit()  # stops the script running
        if event.type == KEYDOWN:
            if event.key == K_RIGHT or event.key == K_d:
                rightmovement = True

            if event.key == K_LEFT or event.key == K_a:
                leftmovement = True
            if event.key == K_UP or event.key == K_w:
                player_y_momentum = -5

        if event.type == KEYUP:
            if event.key == K_RIGHT or event.key == K_d:
                rightmovement = False
            if event.key == K_LEFT or event.key == K_a:
                leftmovement = False

    if rightmovement == True:
        playerx = playerx + playerspeed
    if leftmovement == True:
        playerx = playerx - playerspeed


    pygame.display.update()  # updates what is being displayed on screen
    clock.tick(fps)  # maintains the frame rate (in this case 60)
Reply
#2
You collision detection fails because you pass playerx and playery to move() when you should pass player_speed and player_y_momentum.

move() should look like this:
def move(rect, xstep, ystep, tiles):
    collisions = {'top': False, 'bottom': False, 'right': False, 'left': False}
    # Move in x and check for collisions
    rect.x += xstep
    for tile in tiles:
        if rect.colliderect(tile):
            if xstep > 0:
                collisions['right'] = True
            elif xstep < 0:
                collisions['left'] = True
            break

    # Move in y and check for collisions
    rect.x -= xstep
    rect.y += ystep
    for tile in tiles:
        if ystep > 0:
            collisions['bottom'] = True
        elif ystep < 0:
            collisions['top'] = True
        break
    return collisions
You weren't using the returned rect value, so don't return it. After checking for x collisions you want to move rect back (rect.x -= xstep) otherwise an x collision is also reported as a y collision.

You'll also want to change the code that calls move()
    playery += player_y_momentum
    player_y_momentum = min(3, player_y_momentum + 0.2)
    collisions_type = move(playerrect, player_speed, player_y_momentum , tile_rects)
Reply
#3
thank you!!! this was so helpful with the collisions stuff, it does now actually detect the ground! although it has made it so that once the player jumps, they do not fall back down, sorry this might be another error in the rest of my code, i've tried to solve it but just cant figure it out??
Reply
#4
Jumping should give the player a negative momentum. Over time the integration of momentum (player_y_momentum = min(3, player_y_momentum + 0.2)) will bring momentum to zero (the apex of the jump) and then the player will begin to fall. It works when I press the up arrow. Look for code that changes playeer_y_momentum.

I have an error in the move() code I posted. I forgot to check for y collisions. The function should look like this:
def move(rect, xstep, ystep, tiles):
    collisions = {'top': False, 'bottom': False, 'right': False, 'left': False}
    # Move in x and check for collisions
    rect.x += xstep
    for tile in tiles:
        if rect.colliderect(tile):
            if xstep > 0:
                collisions['right'] = True
            elif xstep < 0:
                collisions['left'] = True
            break
 
    # Move in y and check for collisions
    rect.x -= xstep
    rect.y += ystep
    for tile in tiles:
        if rect.colliderect(tile):  # Was missing this very important line.
            if ystep > 0:
                collisions['bottom'] = True
            elif ystep < 0:
                collisions['top'] = True
            break
    return collisions
Reply
#5
thank you so much for your help!!! Heart
Reply
#6
i've managed to fix the jumping problem but now ive added the collisions for the sides of the platforms and i just cant get it to work Huh
when the player is supposed to stop after hitting the side of a platform they just continue through it, and when they hit the bottom of a floating platform they get stuck at that height!!
sorry for all this, im just so confused lol
ill reattach my code with the edits ive made:
import pygame, sys  # importing the library pygame and sys
import time  # imports time library
from pygame.locals import *  # import all pygames modules

windowWidth = 960  # 64 * 15
windowHeight = 640  # 64 * 10
fps = 60  # frames per second/ updates and redraws per second
main = True  # main game loop is always true

playerimg1 = pygame.image.load("idleghost.png")
playerx = 0
playery = 512
playerspeed = 5
playerrect = pygame.Rect(playerx, playery, 64, 64)
rightmovement = False  # variables for player movement
leftmovement = False
jump = False
player_y_momentum = 0

tilesize = 64
mapList = [['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a'],
           ['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a'],
           ['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a'],
           ['a', 'a', 'a', 'a', 'a', 'a', 'p', 'p', 'p', 'a', 'a', 'a', 'a', 'a', 'a'],
           ['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'p', 'p', 'a'],
           ['a', 'a', 'p', 'p', 'p', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a'],
           ['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a'],
           ['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a'],
           ['a', 'a', 'a', 'a', 'a', 'a', 'p', 'p', 'a', 'a', 'a', 'a', 'a', 'a', 'a'],  # y=576
           ['g', 'g', 'g', 'g', 'g', 'g', 'g', 'g', 'g', 'g', 'g', 'g', 'g', 'g', 'g']]

background = pygame.image.load("bloodywall.png")
floor = pygame.image.load("floorboardtile.png")
shelf = pygame.image.load("bookshelf.png")

clock = pygame.time.Clock()  # setting up the clock

pygame.init()  # initializing pygame
window = pygame.display.set_mode((windowWidth, windowHeight))  # initializing the window/screen

pygame.display.set_caption("Platform Game")  # name of window


def collision_test(rect, tiles):
    hit_list = []
    for tile in tiles:
        if rect.colliderect(tile):
            hit_list.append(tile)
    return hit_list


def move(rect, xstep, ystep, tiles):
    collisions = {'top': False, 'bottom': False, 'right': False, 'left': False}
    hit_list = collision_test(rect, tiles)
    # Move in x and check for collisions
    rect.x += xstep
    for tiles in hit_list:
        if rect.colliderect(tiles):
            if xstep > 0:
                collisions['right'] = True
            elif xstep < 0:
                collisions['left'] = True
            break

    # Move in y and check for collisions
    rect.x -= xstep
    rect.y += ystep
    for tiles in hit_list:
        if rect.colliderect(tiles):  # Was missing this very important line.
            if ystep > 0:
                collisions['bottom'] = True
            elif ystep < 0:
                collisions['top'] = True
            break
    return collisions


while main == True:  # main game loop
    window.blit(background,
                (0, 0))  # renders the background image, blit is used to place one surface upon another
    playerrect = pygame.Rect(playerx, playery, playerimg1.get_width(), playerimg1.get_height())
    tile_rects = []  # sets up list of tile rects
    y = 0  # keeps track of x,y coordinates in tile map
    for row in mapList:  # renders the tiles in the map list / for each row in the map list
        x = 0
        for tile in row:  # for each tile in each row
            if tile == 'g':  # if a tile is the ground
                window.blit(floor,
                            (x * tilesize, y * tilesize))  # render the floor image in the current co-ords
                tile_rects.append(pygame.Rect(x * tilesize, y * tilesize, tilesize,
                                              tilesize))

            if tile == 'p':  # if a tile is a platform
                window.blit(shelf, (x * tilesize, y * tilesize))
                tile_rects.append(pygame.Rect(x * tilesize, y * tilesize, tilesize,
                                              tilesize))

            x += 1  # increase the x co-ord by 1
        y += 1  # increase the y co-ord by 1
    playery += player_y_momentum
    player_y_momentum = min(3, player_y_momentum + 0.2)  # add the players vertical momentum to their y position

    collisions_type = move(playerrect, playerspeed, player_y_momentum, tile_rects)

    if collisions_type['bottom']:
        player_y_momentum = 0
    if collisions_type['top']:
        player_y_momentum = 5
    if collisions_type['right']:
        playerspeed = 0
    else:
        playerspeed = 5
    if collisions_type['left']:
        playerspeed = 0
    else:
        playerspeed = 5


    window.blit(playerimg1, (playerx, playery))  # renders players image

    for event in pygame.event.get():  # the event loop
        if event.type == pygame.QUIT:  # checks if the window is being closed
            pygame.quit()  # stops pygame
            sys.exit()  # stops the script running
        if event.type == KEYDOWN:
            if event.key == K_RIGHT or event.key == K_d:
                rightmovement = True

            if event.key == K_LEFT or event.key == K_a:
                leftmovement = True
            if event.key == K_UP or event.key == K_w:
                player_y_momentum = -5

        if event.type == KEYUP:
            if event.key == K_RIGHT or event.key == K_d:
                rightmovement = False
            if event.key == K_LEFT or event.key == K_a:
                leftmovement = False
            if event.key == K_UP or event.key == K_w:
                player_y_momentum = player_y_momentum + 0.2
                if player_y_momentum > 3:
                    player_y_momentum = 3

    if rightmovement == True:
        playerx = playerx + playerspeed
    if leftmovement == True:
        playerx = playerx - playerspeed

    pygame.display.update()  # updates what is being displayed on screen
    clock.tick(fps)  # maintains the frame rate (in this case 60)
Reply
#7
Think about 1 problem at a time. I would start with stopping when running into a wall. If you can't figure that out by yourself I suggest you watch a few videos or do a tutorial. These are very basic components. If you don't understand them well you are just going to get frustrated.
Reply
#8
(Aug-17-2022, 07:22 PM)chairmanme0wme0w Wrote: i've managed to fix the jumping problem but now ive added the collisions for the sides of the platforms and i just cant get it to work Huh
when the player is supposed to stop after hitting the side of a platform they just continue through it,

As for this point, when you add the momentum and check collision, you zero the momentum but don't subtract the movement you just added. They are still inside the block at the end of the turn.

rect.x += xstep
    for tiles in hit_list:
        if rect.colliderect(tiles):
            rect.x -= xstep
better would be something to the effect
rect.x += xstep
    for tile in hit_list:
        if rect.colliderect(tile):
            # if moving right
            rect.right = tile.rect.left
Reply
#9
Your problem with left/right is you control player speed like this
    if rightmovement == True:
        playerx = playerx + playerspeed
    if leftmovement == True:
        playerx = playerx - playerspeed
But if you have a collision you don't set leftmovement or rightmovement false. Detecting collisions is useless if you don't take appropriate action. I would get rid of the left/right movement and work directly with player_speed. When the right key is pressed set player speed = 5. When the key is released set player speed = 0. When there is a x direction collision set player speed = 0. Do the same for the left key, except set the player speed to -5. This same sort of thing is why the player sticks when they jump up into a barrier.
Reply
#10
i've made these changes, but i think the problem is moreso rooted in the actual collisions, as i made the tiles turn red upon being collided with, and that has suggested that even when a left/right collision isnt taking place, its registered as a collision if the player is touching the floor. does that make sense? thanks for all the help and advice!
rect.x += xstep
    for tile in tiles:
        if rect.colliderect(tile):

            if xstep > 0:

                collisions['right'] = True
                pygame.draw.rect(window, (255, 0, 0), tile)

            elif xstep < 0:
                collisions['left'] = True

            break
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
Question [PyGame] Problem with collision of player and enemy Nekotrooper 1 700 Dec-08-2023, 03:29 PM
Last Post: deanhystad
  [PyGame] Players not falling from the platform, and some other errors. urmom33 1 1,638 Jan-23-2023, 10:28 PM
Last Post: deanhystad
  [PyGame] Collision in not happening onizuka 3 3,426 Sep-07-2020, 11:30 AM
Last Post: metulburr
  [PyGame] No collision detection onizuka 6 3,677 Aug-18-2020, 01:29 PM
Last Post: onizuka
  Problem with collision detection... michael1789 4 3,282 Nov-12-2019, 07:49 PM
Last Post: michael1789
  Arcade Collision Problem randor 0 2,694 Oct-28-2019, 11:17 PM
Last Post: randor
  Multiple wall collision in pacman rustyjoe 4 4,119 Aug-11-2019, 08:08 AM
Last Post: rustyjoe
  drawing, moving, and collision problems (pygame) SheeppOSU 26 14,762 Apr-22-2019, 03:09 AM
Last Post: SheeppOSU
  [PyGame] Game doesn't work after pause Owenix 2 3,184 Sep-20-2018, 08:44 PM
Last Post: nilamo

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020