I'm creating an endless runner/ platformer game using pygame and am working on collision detection between the player rect and the rect of any object in the foreground list, as they will be randomly generated. I've tried using help from other questions, friends and even chatGPT but it still wont work, upon jumping the player will fall through the object they are meant to land on in the fgs list and leave the screen as they fall. I've tried everything i can think of but nothing works.
One thing i am positive of is the loop i have to generate the position of the building correctly captures the width, height, X and Y values into the fgWidth[], fgHeight[], fgRectX[] and fgRectY[] lists (respectively), as the print function outputs the correct numbers i calculated. these lists are further used to determine the position of the rects, but it doesn't seem to be working, or maybe something else isn't working, but i thought to add that just in case people were wondering if those list values were correct.
I am new to coding games using pygame and python, so i may have missed something very obvious, if i have please just let me know. Thanks in advance!!!
The code is below:
import pygame
from pygame.locals import *
import random
import math
import time
pygame.init()
# create the game window
game_width = 1920
game_height = 1080
size = (game_width, game_height)
game = pygame.display.set_mode(size)
pygame.display.set_caption('Neon Rush')
# game variables
score = 0
speed = 3
class Player(pygame.sprite.Sprite):
gravity = 1
def __init__(self, x, y, width, height):
pygame.sprite.Sprite.__init__(self)
self.height = 96
self.action = 'running'
self.health = 1
self.y_vel = 0
self.x_vel = 25
self.rect = pygame.Rect(x, y, width, height)
self.mask = None
self.direction = "right"
self.animation_count = 0
self.fall_count = 0
self.jump_count = 0
self.hit = False
self.hit_count = 0
self.y = 792
self.jumpActive = False
# load the running sprites
self.running_sprites = []
self.running_sprite_index = 0
for i in range(3):
running_sprite = pygame.image.load(f'images/player/running/run{i}.png').convert_alpha()
scale = self.height / running_sprite.get_height()
new_width = running_sprite.get_width() * scale
new_height = running_sprite.get_height() * scale
running_sprite = pygame.transform.scale(running_sprite, (new_width, new_height))
self.running_sprites.append(running_sprite)
# load the jumping sprites
self.jumping_sprites = []
self.jumping_sprite_index = 0
for i in range(5):
jumping_sprite = pygame.image.load(f'images/player/jumping/jump{i}.png').convert_alpha()
scale = self.height / jumping_sprite.get_height()
new_width = jumping_sprite.get_width() * scale
new_height = jumping_sprite.get_height() * scale
jumping_sprite = pygame.transform.scale(jumping_sprite, (new_width, new_height))
self.jumping_sprites.append(jumping_sprite)
# load the slide sprites
self.sliding_sprites = []
self.sliding_sprite_index = 0
for i in range(3):
sliding_sprite = pygame.image.load(f'images/player/sliding/slide{i}.png').convert_alpha()
scale = self.height / sliding_sprite.get_height()
new_width = sliding_sprite.get_width() * scale
new_height = sliding_sprite.get_height() * scale
sliding_sprite = pygame.transform.scale(sliding_sprite, (new_width, new_height))
self.sliding_sprites.append(sliding_sprite)
# set the initial sprite rect
self.rect = self.running_sprites[self.running_sprite_index].get_rect()
# number of frames player is invincible after getting hurt
self.invincibility_frame = 0
def draw(self):
''' draw the sprite based on the character action and index '''
if self.action == 'running':
running_sprite = self.running_sprites[int(self.running_sprite_index)]
# add invincibility effect when hurt
if self.invincibility_frame > 0:
self.invincibility_frame -= 1
if self.invincibility_frame % 10 == 0:
game.blit(running_sprite, (25, self.y))
elif self.action == 'jumping' or self.action == 'landing':
jumping_sprite = self.jumping_sprites[int(self.jumping_sprite_index)]
# add invincibility effect when hurt
if self.invincibility_frame > 0:
self.invincibility_frame -= 1
if self.invincibility_frame % 10 == 0:
game.blit(jumping_sprite, (25, self.y))
elif self.action == 'sliding':
sliding_sprite = self.sliding_sprites[int(self.sliding_sprite_index)]
# add invincibility effect when hurt
if self.invincibility_frame > 0:
self.invincibility_frame -= 1
if self.invincibility_frame % 10 == 0:
game.blit(sliding_sprite, (25, self.y))
def update(self):
''' update the sprite index so the next sprite image is drawn '''
''' also update the y position when jumping or landing '''
if self.action == 'running':
# increment the sprite index by 0.2
# so it takes 5 frames to get to the next index
self.running_sprite_index += 0.25
# go back to index 0 after the last sprite image is drawn
if self.running_sprite_index >= 3:
self.running_sprite_index = 0
# update the rect
self.rect = self.running_sprites[int(self.running_sprite_index)].get_rect()
elif self.action == 'jumping' or self.action == 'landing':
# increment the sprite index by 0.1
# so it takes 10 frames to get to the next index
self.jumping_sprite_index += 0.14
# go back to index 0 after the last sprite image is drawn
if self.jumping_sprite_index >= 4:
self.jumping_sprite_index = 0
self.y += self.y_vel
# change to running when character touches the ground
if self.y == 792:
self.action = 'running'
# allows the player to jump again
self.jumpActive = False
# update the rect
self.rect = self.jumping_sprites[int(self.jumping_sprite_index)].get_rect()
elif self.action == 'sliding':
# increment the sprite index by 0.2
# so it takes 5 frames to get to the next index
self.sliding_sprite_index += 0.2
# go back to index 0 after the last sprite image is drawn
if self.sliding_sprite_index >= 3:
self.sliding_sprite_index = 0
# update the rect
self.rect = self.sliding_sprites[int(self.sliding_sprite_index)].get_rect()
# handles the movement of the character
def move(self, dy):
self.rect.y += dy
self.rect.x = 25
# updates movement values and variables every frame
def loop(self, fps):
self.y_vel += min(1, (self.fall_count / fps) * self.gravity)
self.move(self.y_vel)
self.fall_count += 1
self.update()
# jump
def jump(self):
self.y_vel = -self.gravity * 8
self.animation_count = 0
self.jump_count += 1
if self.jump_count == 1:
self.fall_count = 0
if self.action not in ['jumping', 'landing']:
self.action = 'jumping'
#slide
def slide(self):
''' make the player go to jumping action when not already jumping, landing or sliding '''
if self.action not in ['jumping', 'landing', 'sliding']:
self.action = 'sliding'
# set the image for the sky
sky = pygame.image.load('images/bg/sky3.png').convert_alpha()
num_bg_tiles = math.ceil(game_width / sky.get_width()) + 1
# set the images for the parallax background
bgs = []
bgs.append(pygame.image.load('images/bg/bg1.png').convert_alpha())
bgs.append(pygame.image.load('images/bg/bg2.png').convert_alpha())
bgs.append(pygame.image.load('images/bg/bg3.png').convert_alpha())
bgs.append(pygame.image.load('images/bg/bg4.png').convert_alpha())
# for the parallax effect, determine how much each layer will scroll
parallax = []
for x in range(len(bgs)):
parallax.append(0)
# set images for the buildings in the foreground
fgs = []
fgs.append(pygame.image.load('images/buildings/temp/building1.png').convert_alpha())
fgs.append(pygame.image.load('images/buildings/temp/building2.png').convert_alpha())
fgs.append(pygame.image.load('images/buildings/temp/building3.png').convert_alpha())
fgs.append(pygame.image.load('images/buildings/temp/building4.png').convert_alpha())
# create the player
player = Player(25, 10, 96, 96)
# game loop
clock = pygame.time.Clock()
fps = 60
slideActive = False
quit = False
while not quit:
clock.tick(fps)
for event in pygame.event.get():
if event.type == QUIT:
quit = True
# press SPACE to jump
if event.type == KEYDOWN and event.key == K_SPACE and player.jump_count < 2:
player.jumping_sprite_index = 0
player.jump()
# set to true to prevent the player resetting the jump animation mid jump
player.jumpActive = True
# press 'C' to slide
if event.type == KEYDOWN and event.key == K_c and slideActive == False:
slideActive = True
player.slide()
# prevents jump glitches if slide and jump are pressed very close together
player.jumpActive = False
elif event.type == KEYDOWN and event.key == K_c and slideActive == True:
slideActive = False
player.action = 'running'
# prevents jump glitches if slide and jump are pressed very close together
# Move the player
player.loop(fps)
# Draw the sky
for i in range(num_bg_tiles):
game.blit(sky, (i * sky.get_width(), 0))
# Draw each background layer
for i in range(len(bgs)):
bg = bgs[i]
for j in range(num_bg_tiles):
game.blit(bg, (j * bg.get_width() + parallax[i], 0))
# Update how much each layer will scroll
for i in range(len(parallax)):
# Top layer should scroll faster
parallax[i] -= i + 1
if abs(parallax[i]) > bgs[i].get_width():
parallax[i] = 0
# RECT AND FOREGROUND POSITION CODE STARTS HERE !!!
buildingX = 0
fgRectX = []
fgRectY = []
fgHeight = []
fgWidth = []
for i in range(len(fgs)):
fg = fgs[i]
if i != 0:
game.blit(fg, ((192 * i) + buildingX, 1080 - fg.get_height()))
fgRectX.append((192 * i) + buildingX)
fgRectY.append(1080 - fg.get_height())
fgHeight.append(fg.get_height())
fgWidth.append(fg.get_width())
buildingX += fg.get_width()
elif i == 0:
game.blit(fg, ((192 * i), 1080 - fg.get_height()))
fgRectX.append(192 * i)
fgRectY.append(1080 - fg.get_height())
fgHeight.append(fg.get_height())
fgWidth.append(fg.get_width())
buildingX += fg.get_width()
print(fgWidth[i], fgHeight[i], fgRectY[i], fgRectX[i])
# Check for collisions with objects in 'fgs' list
for i in range (len(fgs)):
if player.rect.colliderect(pygame.Rect(fgRectX[i], fgRectY[i], fgWidth[i], fgHeight[i])):
# Check if player is on top of the object
if player.y_vel > 0:
# Set the player's position on top of the object
player.rect.bottom = pygame.Rect(fgRectX[i], fgRectY[i], fgWidth[i], fgHeight[i]).top
player.y_vel = 0
player.jump_count = 0
player.action = 'running'
print('collided!!')
# Draw the player
player.draw()
pygame.display.update()
pygame.quit()