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()
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...