Pygame character shaking for collisions

Question:

I’m practising at pygame and I was trying to implement block id detection in my code, but after I finished it I noticed that my character is shaking. For seeing if block id detection was the problem I temporarely removed it, but nothing changed. I checked what type of collisions is the character facing and saw that he detects whatever collision type only in 1 of 2 frames. So how can I fix it ?

import pygame 
from pygame.locals import *
import os

pygame.init()
pygame.mixer.pre_init(4410, -16, 2, 512)


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

def move(rect, movement, tiles):
    collision_types = {"top" : False, "bottom" : False, "right" : False, "left" : False}
    rect.x += movement[0]
    hit_list = check_collision(rect, tiles)
    for hit in hit_list:
        if movement[0] > 0:
            collision_types["right"] = True
            rect.right = hit.left
        elif movement[0] < 0:
            collision_types["left"] = True
            rect.left = hit.right

    rect.y += movement[1]
    hit_dict = check_collision(rect, tiles)
    for hit in hit_list:
        if movement[1] > 0:
            collision_types["bottom"] = True
            rect.bottom = hit.top
        elif movement[1] < 0:
            collision_types["top"] = True
            rect.top = hit.bottom

    return rect, collision_types

def load_map(path):
    map_file = open(path, "r")
    content = map_file.read()
    map_file.close()
    content = content.split("n")
    game_map = []
    for row in content:
        game_map.append(list(row))
    return game_map

def check_walk_frame(image_list, walking_counter, current):
    if walking_counter%60 <= 20 and walking_counter%60 != 0:
        current = image_list[0]
    elif walking_counter%60 > 20 and walking_counter%60 <= 40:
        current = image_list[1]
    elif walking_counter%60 > 40 and walking_counter%60 < 60:
        current = image_list[2]
    elif walking_counter == 60:
        walking_counter = 1
    else:
        current = image_list[1]
    return current, walking_counter


game_map = load_map("game_map.txt")

class Player(object):
    def __init__(self, x, y, image):
        self.image = image
        self.x = x
        self.y = y
        self.width = self.image.get_width()
        self.height = self.image.get_height()
        self.hitbox = pygame.Rect(self.x, self.y, self.width, self.height)
        self.momentum = 0
        self.speed = 6
        self.moving_left = False
        self.moving_right = False


WINDOW_WIDTH, WINDOW_HEIGHT = 1300, 650
FPS = 60
TILE_SIZE = 100

def main():

    window = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
    display = pygame.Surface((1200, 600))

Slid_animations = [pygame.transform.scale(pygame.image.load(os.path.join("Slid_anim\Slid_anim2.png")).convert_alpha(), (82, 128)),
                   pygame.transform.scale(pygame.image.load(os.path.join("Slid_anim\Slid_anim1.png")).convert_alpha(), (82, 128)),
                   pygame.transform.scale(pygame.image.load(os.path.join("Slid_anim\Slid_anim3.png")).convert_alpha(), (82, 128))]
Slid_Current_Image = Slid_animations[1]
Dirt = pygame.image.load(os.path.join("dirt.png")).convert_alpha()
Grass = pygame.image.load(os.path.join("grass.png")).convert_alpha()
Dirt_image = pygame.transform.scale(Dirt, (TILE_SIZE, TILE_SIZE))
Grass_image = pygame.transform.scale(Grass, (TILE_SIZE, TILE_SIZE))
Sounds = {"grass_1" : pygame.mixer.Sound(os.path.join("grass_1.mp3")),
          "grass_2" : pygame.mixer.Sound(os.path.join("grass_2.mp3")),
          "jump" : pygame.mixer.Sound(os.path.join("jump.wav"))}


float_scroll = [0, 0]
scroll = [0, 0]
Walking_Counter = 0
Slid = Player(0, 100, Slid_Current_Image)


Cloud_Layer_1 = [pygame.Rect(200, 50, 150, 90), pygame.Rect(800, 150, 100, 60)]
Cloud_Layer_2 = [pygame.Rect(100, 80, 230, 110)]

clock = pygame.time.Clock()
run = True
while run:
    clock.tick(FPS)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False

    display.fill((145, 255, 255))


    
    cloud_1 = pygame.Rect(200-scroll[0]*0.8, 50-scroll[1]*0.5, 150, 90)
    cloud_2 = pygame.Rect(100-scroll[0]*0.6, 80-scroll[1]*0.3, 230, 110)
    cloud_3 = pygame.Rect(800-scroll[0]*0.8, 150-scroll[1]*0.5, 100, 60)
    pygame.draw.rect(display, (50, 200, 200), cloud_1)
    pygame.draw.rect(display, (0, 200, 200), cloud_2)
    pygame.draw.rect(display, (50, 200, 200), cloud_3)



    float_scroll[0] += (Slid.hitbox.x-scroll[0]-600) / 20
    float_scroll[1] += (Slid.hitbox.y-scroll[1]-300) / 20
    scroll = float_scroll.copy()
    scroll[0] = int(scroll[0])
    scroll[1] = int(scroll[1])


    Slid_Current_Image, Walking_Counter = check_walk_frame(Slid_animations, Walking_Counter, Slid_Current_Image)
    display.blit(Slid_Current_Image, (Slid.hitbox.x - scroll[0], Slid.hitbox.y - scroll[1]))


    Tile_rects = []



    y = 0
    for tile_row in game_map:
        x = 0
        for tile in tile_row:
            if tile == "1":
                display.blit(Dirt_image, (x * TILE_SIZE - scroll[0], y * TILE_SIZE - scroll[1]))
                Tile_rects.append(pygame.Rect(x * TILE_SIZE, y * TILE_SIZE, TILE_SIZE, TILE_SIZE))
            elif tile == "2":
                display.blit(Grass_image, (x * TILE_SIZE - scroll[0], y * TILE_SIZE - scroll[1]))
                Tile_rects.append(pygame.Rect(x * TILE_SIZE, y * TILE_SIZE, TILE_SIZE, TILE_SIZE))
            x += 1
        y += 1

    Slid_Movement = [0, 0]

    if Slid.moving_left:
        Slid_Movement[0] = -Slid.speed
    if Slid.moving_right:
        Slid_Movement[0] = Slid.speed
    Slid_Movement[1] += Slid.momentum
    Slid.momentum += 0.4
    if Slid.momentum > 5:
        Slid.momentum = 5

    Slid.hitbox, collisions = move(Slid.hitbox, Slid_Movement, Tile_rects)

    keys_pressed = pygame.key.get_pressed()

    if keys_pressed[K_a]:
        Slid.moving_left = True
        Walking_Counter += 1    
    else:

        Slid.moving_left = False
    if keys_pressed[K_d]:
        Slid.moving_right = True
        Walking_Counter += 1
    else:
        Slid.moving_right = False

    if not Slid.moving_right and not Slid.moving_left:
        Walking_Counter = 0

    if keys_pressed[K_SPACE] and collisions["bottom"]:
        Slid.momentum = -15
        Sounds["jump"].play()

    if collisions["top"]:
        Slid.momentum = 0

    
    surface = pygame.transform.scale(display, (WINDOW_WIDTH, WINDOW_HEIGHT))
    window.blit(surface, (0, 0))

    print(collisions)

    pygame.display.update()

pygame.quit()

if __name__ == "__main__":
    main()
Asked By: Sukurima

||

Answers:

The problem is actually the collision system in your move function. You somehow wrote hit_dict = check_collision instead of hit_list = check_collision but you never used hit_dict (which isn’t a dict). The collision system works by first moving the player on the x-axis and checking for any collisions, then it does the same for the y-axis. It’s important to split the movement for both axes, otherwise it won’t know where to go when the player is in a block – e.g. move to the left or to the top of the block? Therefore you also have to update your hit_list for both axis.

def move(rect, movement, tiles):
    collision_types = {"top" : False, "bottom" : False, "right" : False, "left" : False}
    rect.x += movement[0]
    hit_list = check_collision(rect, tiles)
    for hit in hit_list:
        if movement[0] > 0:
            collision_types["right"] = True
            rect.right = hit.left
        elif movement[0] < 0:
            collision_types["left"] = True
            rect.left = hit.right

    rect.y += movement[1]
    hit_list = check_collision(rect, tiles) # hit_list not hit_dict
    for hit in hit_list:
        if movement[1] > 0:
            collision_types["bottom"] = True
            rect.bottom = hit.top
        elif movement[1] < 0:
            collision_types["top"] = True
            rect.top = hit.bottom

    return rect, collision_types

I’m pretty sure you copied that collision system from somewhere…

Answered By: Jerry