Player collision with tiles not working, instead falling through them

Question:

Basically I’m learning pygame. and I’m learning from a tutorial, I’m not doing 100% the same but mostly, now I can’t figure out why the tutorial works and mine doesn’t, in terms of colliding with tiles. Here’s my code:

import sys

import pygame

clock = pygame.time.Clock()  # initiate clock
from pygame.locals import *

pygame.init()  # initiate pygame


WINDOW = (600, 400)
screen = pygame.display.set_mode(WINDOW, 0, 32)
pygame.display.set_caption("window")  # set window name
display = pygame.Surface(
    (300, 200)
)  # create display surface, basically makes another image, since all images are surfaces.. kinda
# you can render one surface onto anoher surface


player_image = pygame.image.load(
    "imagesme1ns.png"
).convert_alpha()  # load player image, degine player image as variable
player_y_momentum = 0

grass_image = pygame.image.load("imagesgrass.png").convert()
dirt_image = pygame.image.load("imagesdirt.png").convert()
game_map = [
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2],
    [1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
]
TILE_SIZE = 16  # size of each tile


def collsion_test(rect, tiles):
    hit_col = []
    for tile in tiles:
        if rect.colliderect(tile):
            hit_col.append(tile)
        return hit_col
    # tile collision test, returns a list of tiles that collided with the rect


def move(
    rect, movement, tiles
):  # thing that's being moved, how it's moved, and what tiles it's colliding with
    rect.x += movement[0]  # move on the x axis
    collsions = collsion_test(
        rect, tiles
    )  # returns a list of tiles that collided with the rect
    for tile in collsions:
        if movement[0] > 0:
            rect.right = tile.left
        elif movement[0] < 0:
            rect.left = tile.right
        # if if right, place on left, if left palce on right
    rect.y += movement[1]  # move on the x axis
    collsions = collsion_test(
        rect, tiles
    )  # returns a list of tiles that collided with the rect
    for tile in collsions:
        if movement[1] > 0:
            rect.bottom = tile.top
        elif movement[1] < 0:
            rect.top = tile.bottom
        # if up place on bottom and vice versa
    return rect


# colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
GRAY = (128, 128, 128)
LBLUE = (146, 244, 255)

moving_right = False
moving_left = False
player_rect = pygame.Rect(50, 50, player_image.get_width(), player_image.get_height())
# getting the player rect(collision), x,y,w,h
# when defining a rect, use pygame.Rect(x,y,w,h), not use pygame.rect(x,y,w,h)
while True:
    display.fill(LBLUE)  # fills screen with color, put images after this
    tile_rects = []
    y = 0
    for row in game_map:
        x = 0
        for tile in row:
            if tile == 2:
                display.blit(grass_image, (x * TILE_SIZE, y * TILE_SIZE))
            if tile == 1:
                display.blit(dirt_image, (x * TILE_SIZE, y * TILE_SIZE))
            if tile != 0:
                tile_rects.append(
                    pygame.Rect(x * TILE_SIZE, y * TILE_SIZE, TILE_SIZE, TILE_SIZE)
                )
            x += 1
        y += 1
    # displays tile map, x and y are the location of the tile. We blit based on what the number is, tile is the individual number in map
    # multiple by tile size to get true full size.
    # we make a list of all the tiles in the map, then make a rect for each tile, base on the position of the tile, and ofc TILE_SIZE, TILE_SIZE

    # update player collision location
    # colliderect checks if someting is collidign with something else

    # movement
    player_movement = [0, 0]
    if moving_right:
        player_movement[0] += 2
    if moving_left:
        player_movement[0] -= 2
    player_movement[1] += player_y_momentum
    player_y_momentum += 0.2
    if player_y_momentum > 3:
        player_y_momentum = 3

    player_rect = move(player_rect, player_movement, tile_rects)
    # moves the player, does collision testing, and returns the new rect.

    display.blit(player_image, (player_rect.x, player_rect.y))
    # puts player image on the screen, at specific location
    # by having it so that you make change, and then check, you can hold and still move

    for event in pygame.event.get():  # main loop for pygame events
        if event.type == QUIT:
            pygame.quit()
            print(tile_rects)
            sys.exit()
        if event.type == KEYDOWN:  # loop for key pressed down
            if event.key == K_RIGHT:
                moving_right = True
            if event.key == K_LEFT:
                moving_left = True
        if event.type == KEYUP:  # checks if key is pressed up
            if event.key == K_RIGHT:
                moving_right = False
            if event.key == K_LEFT:
                moving_left = False
    # changes the size of one thing to another
    screen.blit(pygame.transform.scale(display, (600, 400)), (0, 0))
    # what to blit and where
    pygame.display.update()  # updates the screen
    clock.tick(60)  # 60 fps, has game running at 60 fps

I expect to not fall through the ground and istead just be on top of it. I think I understand how things work, not 100% though.

Asked By: Dzbice

||

Answers:

You appear to have an "early return" statement that you mistakenly put inside the loop, so only the first tile is looked at within this loop:

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

Try changing to this (note the corrected un-indentation of the return statement:

def collsion_test(rect, tiles):
    hit_col = []
    for tile in tiles:
        if rect.colliderect(tile):
            hit_col.append(tile)
    return hit_col
Answered By: AirSquid
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.