0

I have been making a pygame game where 2 cars hit a ball around a pitch. When the player collides with the ball, the ball goes forwards, however, if the bottom of the player hits the top of the ball, the ball still goes upwards, understandingly as the code tells the ball to go upwards after contact. However, I want the ball to go downwards realistically when the ball is hit downwards. Has anyone got any ideas?

import pygame as pg
from pygame.math import Vector2


pg.init()
LIGHTBLUE = pg.Color('lightskyblue2')
DARKBLUE = pg.Color(11, 8, 69)

screen = pg.display.set_mode((800, 600))
width, height = screen.get_size()
clock = pg.time.Clock()
# You need surfaces with an alpha channel for the masks.
bluecar = pg.Surface((60, 30), pg.SRCALPHA)
bluecar.fill((0,0,255))
BALL = pg.Surface((30, 30), pg.SRCALPHA)
pg.draw.circle(BALL, [0,0,0], [15, 15], 15)
ball_pos = Vector2(395, 15)
ballrect = BALL.get_rect(center=ball_pos)
ball_vel = Vector2(0, 0)
mask_blue = pg.mask.from_surface(bluecar)
mask_ball = pg.mask.from_surface(BALL)
pos_blue = Vector2(740, 500)  # Just use the pos vector instead of x, y.
bluerect = bluecar.get_rect(center = pos_blue)
vel_blue = Vector2(0, 0)  # Replace x_change, y_change with vel_blue.
# A constant value that you add to the y-velocity each frame.
GRAVITY = .5

ground_y = height - 100
timer = 3
dt = 1

def bluejumps():
    if timer > 0: 
        vel_blue.y = -12

if pos_blue.y > 469:
    timer -= dt

done = False
while not done:
    for event in pg.event.get():
        if event.type == pg.QUIT:
            done = True
        elif event.type == pg.KEYDOWN:
            if event.key == pg.K_a:
                vel_blue.x = -5
            elif event.key == pg.K_d:
                vel_blue.x = 5
            elif event.key == pg.K_w:
                bluejumps()
        elif event.type == pg.KEYUP:
            if event.key == pg.K_a and vel_blue.x < 0:
                vel_blue.x = 0
            elif event.key == pg.K_d and vel_blue.x > 0:
                vel_blue.x = 0
        if bluerect.bottom >= ground_y:
            bluerect.bottom = ground_y
            pos_blue.y = bluerect.centery
            #vel_blue.y = 0
            timer = 3


    ball_vel.y += GRAVITY  # Accelerate downwards.

    ball_pos += ball_vel  # Move the ball.
    ballrect.center = ball_pos  # Update the rect.
    # Bounce when the ball touches the bottom of the screen.
    if ballrect.bottom >= ground_y:
        # Just invert the y-velocity to bounce.
        ball_vel.y *= -0.7  # Change this value to adjust the elasticity.
        ball_vel.x *= .95  # Friction
        # Don't go below the ground.
        ballrect.bottom = ground_y
        ball_pos.y = ballrect.centery
    # Left and right wall collisions.
    if ballrect.left < 0:
        ball_vel.x *= -1
        ballrect.left = 0
        ball_pos.x = ballrect.centerx
    elif ballrect.right > width:
        ball_vel.x *= -1
        ballrect.right = width
        ball_pos.x = ballrect.centerx
    if ballrect.top <= 0:
        # Just invert the y-velocity to bounce.
        ball_vel.y *= -0.4  # Change this value to adjust the elasticity.
        ballrect.top = 1
        ball_pos.y = ballrect.centery

    # Add the GRAVITY value to vel_blue.y, so that
    # the object moves faster each frame.
    vel_blue.y += GRAVITY
    pos_blue += vel_blue
    bluerect.center = pos_blue  # You have to update the rect as well.

    # Stop the object when it's near the bottom of the screen.
    if bluerect.bottom >= ground_y:
        bluerect.bottom = ground_y
        pos_blue.y = bluerect.centery
        vel_blue.y = 0
        timer = 3
    if bluerect.x < 0:
        bluerect.x = 0
        pos_blue.x = bluerect.centerx
    elif bluerect.right > width:
        bluerect.right = width
        pos_blue.x = bluerect.centerx
    print(timer)

    offset_blue = bluerect[0] - ballrect[0], bluerect[1] - ballrect[1]
    overlap_blue = mask_ball.overlap(mask_blue, offset_blue)
    if overlap_blue:  # Blue collides with the ball.
        if vel_blue.x != 0:  # Player is moving.
            ball_vel = Vector2(vel_blue.x, -17)
        else:  # If the player is standing, I just update the vel.y.
            ball_vel.y = -17

    # Draw everything.
    screen.fill(LIGHTBLUE)
    pg.draw.line(screen, (0, 0, 0), (0, ground_y), (width, ground_y))
    screen.blit(bluecar, bluerect)  # Blit it at the rect.
    screen.blit(BALL, ballrect)

    pg.display.update()
    dt = clock.tick(60)/1000

pg.quit()
Nainman
  • 119
  • 7
  • What do you mean horizontally?It is going vertically for me, as you would expect it to – Nainman Sep 18 '18 at 18:59
  • I see... but if the players are cars, how can the ball hit the bottom of the players? – meowgoesthedog Sep 18 '18 at 19:01
  • If the ball hits the underside of the rectangle – Nainman Sep 18 '18 at 19:16
  • Hmm, ok. Doesn't sound like a car to me but there's no point arguing about this I guess. If the speeds involved in your simulation is low enough, you can get away with static collision detection between circles and rectangles. See [this post](https://stackoverflow.com/questions/401847/circle-rectangle-collision-detection-intersection) for example methods. – meowgoesthedog Sep 18 '18 at 19:18
  • Thanks, still not sure how to implement it for my own code though, can you help? – Nainman Sep 18 '18 at 20:25
  • There is **literally a full implementation** [here](https://stackoverflow.com/questions/18704999/how-to-fix-circle-and-rectangle-overlap-in-collision-response). You should try your hardest to translate this into Python, which shouldn't be too challenging because this solution is already working and also very well explained. There are plenty of other duplicate posts scattered around too. – meowgoesthedog Sep 18 '18 at 21:56
  • Ok thanks, I understand this one. – Nainman Sep 19 '18 at 06:29

1 Answers1

0

The easiest fix is to act like the player is actually two objects. If the ball intersects with the top half, have it bounce upwards, and if it hits the bottom half have it bound downwards. The more accurate approach would be to calculate the collision point and apply some rigid body collision response math based on that, but the approach might be good enough for your case.

The simpler approach will only work if the ball isn't moving too quickly, when the ball passes through a large portion of the player in a single game tick, but it's probably good enough for your case, you'll need to do some testing to figure out whether it's worth the extra complexity

cactus1
  • 629
  • 5
  • 8