Search code examples
python-3.xpygamecollision-detectiongame-physics

Character is being telported away only in the second frame of collision. Why?


For an university programming course final project me and my team are building a platfromer. I'm responsible for making a collisions system. Even though I was able to code one I'm not satisfied with the outcome due to its imperfection.

The problem is that occasionally when character is falling after reaching the platform the player and the block overlap with each other for one frame. Of course, they don't stay like that forever. I've made it so the character teleports back on top of the platform. However, that action is excecuted only in the second frame. For whoever plays this game it will seem like the character is glitching. I believe that the problem lies somewhere in the collision detection. Sadly I couldn't find it.

Any help would be appreciated. Also I hope that this question won't be perceived as me begging someone else to complete my homework. I'm just striving for an advice :)

Collision detection function:

def Collision_Occurs(Block,Block_Length):
global Character, Max_Y_Speed
if(((Character.x <= Block.x + Block_Length and Character.x >= Block.x) or
  (Character.x + 100 <= Block.x + Block_Length and Character.x + 100 >= Block.x)) and
  (Character.y + 100 <= Block.y + Max_Y_Speed + 1 and Character.y + 100 >= Block.y)):
    return True
return False

Entire game code:

import pygame
from sys import exit

pygame.init()
screen = pygame.display.set_mode((1000,650))
pygame.display.set_caption("The Game")
Block1 = pygame.Rect(250,500,500,50)
Block2 = pygame.Rect(100,250,250,50)
Block3 = pygame.Rect(650,250,250,50)
clock = pygame.time.Clock()
Character = pygame.Rect(200,100,100,100)

Moving_X = False
X_Speed = 0
Max_X_Speed = 5
Y_Speed = 0
Max_Y_Speed = 15
Collisions_Off_Timer = 0
Jump_Count = 0
Time_Since_Last_Jump = 60

'''
-------------------------------------------------------------
Collision Detection
-------------------------------------------------------------
'''

def Collision_Occurs(Block,Block_Length):
    global Character, Max_Y_Speed
    if(((Character.x <= Block.x + Block_Length and Character.x >= Block.x) or
      (Character.x + 100 <= Block.x + Block_Length and Character.x + 100 >= Block.x)) and
      (Character.y + 100 <= Block.y + Max_Y_Speed + 1 and Character.y + 100 >= Block.y)):
        return True
    return False

'''
-------------------------------------------------------------
Teleportation back on the platform
-------------------------------------------------------------
'''

def Teleport_Back_Up(Block):
    global Character
    Character.bottom = Block.top

'''
-------------------------------------------------------------
Game loop
-------------------------------------------------------------
'''

while True:
    for event in pygame.event.get():
        if(event.type == pygame.QUIT):
            pygame.quit()
            exit()

    screen.fill((0,0,0))
    pygame.draw.rect(screen,'Red',Block1)
    pygame.draw.rect(screen,'Red',Block2)
    pygame.draw.rect(screen,'Red',Block3)
    pygame.draw.rect(screen,'Blue',Character)

    '''
    -------------------------------------------------------------
    Input
    -------------------------------------------------------------
    '''

    keys = pygame.key.get_pressed()

    if(keys[pygame.K_DOWN]):
        Collisions_Off_Timer = 5

    if(keys[pygame.K_UP] and Jump_Count < 2 and Time_Since_Last_Jump >= 30):
        Y_Speed = -20
        Collisions_Off_Timer = 20
        Jump_Count += 1
        Time_Since_Last_Jump = 0
    if(keys[pygame.K_RIGHT]):
        Moving_X = True
        if(X_Speed<Max_X_Speed):
            X_Speed+=1

    if(keys[pygame.K_LEFT]):
        Moving_X = True
        if(X_Speed>Max_X_Speed*(-1)):
            X_Speed-=1

    '''
    -------------------------------------------------------------
    Friction
    -------------------------------------------------------------
    '''

    if not(Moving_X):
        if X_Speed > 0:
            X_Speed -= 1
        if X_Speed < 0:
            X_Speed += 1

    Moving_X = False

    if not(Y_Speed>Max_Y_Speed):
        Y_Speed += 1

    '''
    -------------------------------------------------------------
    Checking for collisions
    -------------------------------------------------------------
    '''

    if(Y_Speed > 0 and Collisions_Off_Timer <=0):
        if Collision_Occurs(Block1,500):
            Teleport_Back_Up(Block1)
            Y_Speed = 0
            Jump_Count = 0
        if Collision_Occurs(Block2,250):
            Teleport_Back_Up(Block2)
            Y_Speed = 0
            Jump_Count = 0
        if Collision_Occurs(Block3,250):
            Teleport_Back_Up(Block3)
            Y_Speed = 0
            Jump_Count = 0

    '''
    -------------------------------------------------------------
    Teleport back to the middle when vertical bounds are crossed
    -------------------------------------------------------------
    '''

    if Character.y >=1500:
        Character.x = 450
        Character.y = 350
        Y_Speed = 0

    Collisions_Off_Timer -= 1
    Time_Since_Last_Jump +=1
    Character.x +=X_Speed
    Character.y +=Y_Speed

    pygame.display.update()
    clock.tick(60)

Solution

  • Just do the move before collision detection. This corrects the y-position of the player (Teleport_Back_Up) after the player has been moved and before the display is updated:

    while True:
        # [...]
    
        Character.x +=X_Speed # <--- INSERT
        Character.y +=Y_Speed
    
        '''
        -------------------------------------------------------------
        Checking for collisions
        -------------------------------------------------------------
        '''
    
        if(Y_Speed > 0 and Collisions_Off_Timer <=0):
            if Collision_Occurs(Block1,500):
                Teleport_Back_Up(Block1)
                Y_Speed = 0
                Jump_Count = 0
            if Collision_Occurs(Block2,250):
                Teleport_Back_Up(Block2)
                Y_Speed = 0
                Jump_Count = 0
            if Collision_Occurs(Block3,250):
                Teleport_Back_Up(Block3)
                Y_Speed = 0
                Jump_Count = 0
    
        # [...]
    
        Collisions_Off_Timer -= 1
        Time_Since_Last_Jump +=1
        # Character.x +=X_Speed   <--- DELETE
        # Character.y +=Y_Speed