Object goes through the screen when used the gravity in pygame

Viewed 54

I am a fifth-grader and I'm making a platformer. I'm trying to keep my player from penetrating through the screen when I use gravity. I also need help jumping once with one click and jumping twice as high with 2 clicks. I also used some pieces of code that I found online. So this is my code:

import pygame

canvas_width = 1000
canvas_height = 500
pygame.init()
screen = pygame.display.set_mode([canvas_width,canvas_height])
screen_rect = screen.get_rect()
clock = pygame.time.Clock()

red = (255,0,0)
blue = (0,0,255)
green = (0,255,0)
white = (255,255,255)
black = (0,0,0)


class Ground:
    def __init__ (self,position,size,color):
        self.xPos = position[0]
        self.yPos = position[1]
        self.width = size[0]
        self.height = size[1]
        self.color = color
        self.rect = pygame.rect.Rect(position,size)

#class Enemyline:
#    def __init__ (self,position,size,color):
#        self.xPos = position[0]
#        self.yPos = position[1]
#        self.width = size[0]
#        self.height = size[1]
#        self.color = color
#        self.rect = pygame.rect.Rect(position,size)
#        #self.rect[(xPos,yPos),(width,height)]
        
#     def __init__(self, xsize, ysize,  color):
#         self.color = color
#         self.xsize = xsize
#         self.ysize = ysize
#         self.pos = [0, 10]
#         self.rect = pygame.rect.Rect(self.pos, (xsize, ysize))

class Ball:
    
    def __init__(self,position,size,color):
        self.xPos = position[0]
        self.yPos = position[1]
        self.width = size[0]
        self.height = size[1]
        self.color = color
        #self.size = size
        #self.pos = [10, 450]
        self.speed = [0.001, 0.001]
        #speed [xSpeed,ySpeed]
        self.rect = pygame.rect.Rect(position,size)
        #self.rect = pygame.rect.Rect(self.pos,(size,size))

    def move (self):
        self.keymove()
        #self.hitRect(Enemyline)
        self.gravity()
        #self.edge()
        self.rect.move_ip(self.speed)
        #self.pos = self.rect[0:2]
        pygame.draw.rect(screen, self.color,self.rect)
        
    def touchRect(self,Enemyline, Ground):
        #if self.rect.colliderect(Enemyline):
            #self.speed[1] = self.speed[1] * -1
            
        if self.rect.colliderect(Ground):
            self.speed[1] = self.speed[1] * 0
    
    
    def gravity(self):
        self.speed[1] = self.speed[1] + 0.5
        
#     def edge(self):
#         spot = self.rect[0:2]
#         xPos = spot[0]
#         yPos = spot[1]
#         if xPos <= 0 or xPos >= (canvas_width - self.size):
#             self.speed[0] = self.speed[0] + 0
#         
#         if yPos <= 0 or yPos >= (canvas_height - self.size):
#             self.speed[1] = self.speed[1] + 0
    
    def keymove(self):
        spot = self.rect[0:2]
        xPos = spot[0]
        yPos = spot[1]
        
        
        key = pygame.key.get_pressed()
        
        if key[pygame.K_LEFT]:
            self.speed[0] = self.speed[0] - 0.5

        
        #RIGHT
        if key[pygame.K_RIGHT]:
            self.speed[0] = self.speed[0] + 0.5
        
        #UP
        if key[pygame.K_UP]:
            self.speed[1] = self.speed[1] - 0.5
        #DOWN
        if key[pygame.K_DOWN]:
            self.speed[1] = self.speed[1] + 0.5
                    
        if xPos <= 0 or xPos >= (canvas_width - (self.width + self.height)):
            self.speed[0] = self.speed[0] * -1
        
        if yPos <= 0 or yPos >= (canvas_height - (self.width + self.height)):
            self.speed[1] = self.speed[1] * -1
            
    
#     def stop(self):
#         self.speed[0] = 0
#         self.speed[1] = 0
#         
#     def key_up(self):
#         if [pygame.K_LEFT] == pygame.KEYUP:
#             stop()


        

        
ball = Ball ([500,250],[30,30],blue)
ground = Ground ([0,470],[1000,10],green)
#L1enemy1 = Enemyline ([0,10], [1000,10],red)

Game = True

while Game:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            Game = False
    
    
    ball.move()
#     if (ball.hitRect(L1enemy1) == True):
#         self.speed = [0.001, 0.001]
#         self.speed[1] = self.speed[1] * -1
    screen.fill(black)
    pygame.draw.rect(screen, ball.color, ball.rect)
    pygame.draw.rect(screen, ground.color, ground.rect)
#    pygame.draw.rect(screen, L2enemy2.color, L2enemy2.rect)
    
    pygame.display.update()
    clock.tick(60)

pygame.quit()```

Is there anything you could do?

Thank you.
2 Answers

I played around with your game and got the same behavior. After testing out various blocks of code, I determined that during the collisions with the ground, the "ball" position would often times be below the ground position when the switch from "plus" speed to "minus" speed would occur. So before the ball got moving back up, its position would be checked. And, since its position was still below the ground level which would again reverse the speed to a positive value. Thus it was displaying that "sinking in quicksand" effect. Once the position was below the ground level, it would just keep sinking.

You might want to explore other options, but the way I got it to quit oscillating between positive and negative speed values while the object appeared to be at or under the ground level was to change the following line of code from:

if yPos <= 0 or yPos >= (canvas_height - (self.width + self.height)):

to:

if yPos <= 0 or yPos >= (canvas_height - (self.width + self.height)) and self.speed[1] > 0:

Only doing the speed switch from positive to negative when the noted speed was positive seemed to provide a "shock absorber" effect to counter any lag between the positioning of the ball and the tests for hitting the ground.

Try that out and see if that helps you out.

Regards.

The condition

if yPos <= 0 or yPos >= (canvas_height - (self.width + self.height)):

makes no sense. There is absolutely no point in adding width and height together. The width is a size along the x-axis and the height is a size along the y-axis. Your code works by chance, because self.width is the same as distacnce from the bottom of the window to the top of the green line. What you actually wanted to do is:

if yPos <= 0 or yPos >= ground.rect.top - self.height:

Anyway, that doesn't solve your problem. The problem is similar to the problem described here: Sometimes the ball doesn't bounce off the paddle in pong game. The real problem is that the speed decreases. If the object goes below the line and the upward speed is insufficient to lift the object above the line, the object will get stuck.

You have 2 Option to solve the problem

  1. Make sure the movement is up as long as the object is beneath the ground. You can achieve that with self.speed[1] = -abs(self.speed[1]) instead of self.speed[1] = self.speed[1] * -1
if yPos <= 0:
    self.speed[1] = abs(self.speed[1])

elif yPos >= ground.rect.top - self.height:
    self.speed[1] = -abs(self.speed[1])
  1. Limit the bottom of the object to the top of the ground when the object hits the ground (self.rect[1] = ground.rect.top - self.height):
if yPos <= 0:
    self.speed[1] = self.speed[1] * -1

elif yPos >= ground.rect.top - self.height:
    self.speed[1] = self.speed[1] * -1
    yPos = ground.rect.top - self.height
    self.rect[1] = yPos
Related