Pygame realistic angles - python

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

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

Related

Simple pong: but the ball goes into the corner and bounces the wrong way and then it keeps cycling how can I fix this?

Simple pong: but on the 5th hit the ball goes into the corner and bounces the wrong way vertically off the player1 rectangle, and then it goes to the opponent AI in the corner, also bounces the wrong way vertically off the AI rectangle, and then keeps cycling. How can I fix this? I'm very new to python and pygame so maybe there's something obvious I missed. I made the AI speed faster on purpose.
import pygame, sys, random
pygame.init()
WIDTH = 1100
HEIGHT = 800
screen = pygame.display.set_mode((WIDTH, HEIGHT))
opponent = pygame.Rect(10, HEIGHT/2 - 50, 10, 100)
player = pygame.Rect(WIDTH - 20, HEIGHT/2 - 50, 10, 100)
ball = pygame.Rect(WIDTH/2 - 15 , HEIGHT/2 - 15,30,30)
clock = pygame.time.Clock()
player_speed = 0
opponent_speed = 15
ball_speed_x = 5
ball_speed_y = 5
score_player = 0
score_ai = 0
score = (score_ai, score_player)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
player_speed -= 9
if event.key == pygame.K_DOWN:
player_speed += 9
if event.type == pygame.KEYUP:
if event.key == pygame.K_UP:
player_speed += 9
if event.key == pygame.K_DOWN:
player_speed -= 9
#ball movement
ball.x += ball_speed_x
ball.y += ball_speed_y
# ball colliding with ceiling/floor
if ball.top <= 0:
ball_speed_y *= -1
if ball.bottom >= HEIGHT:
ball_speed_y *= -1
# if player misses ball
if ball.left <= 0:
ball = pygame.Rect(WIDTH/2 - 15 , HEIGHT/2 - 15,30,30)
ball_speed_x = 5
ball_speed_y = 5
score_player += 1
pygame.time.delay(1000)
continue
if ball.right >= WIDTH:
ball = pygame.Rect(WIDTH / 2 - 15, HEIGHT / 2 - 15, 30, 30)
ball_speed_x = 5
ball_speed_y = 5
score_ai += 1
pygame.time.delay(1000)
continue
# hitting the ball
if ball.colliderect(opponent):
ball_speed_x = -10
ball_speed_x *= -1
ball_speed_y = -9
ball_speed_y *= -1
if ball.colliderect(player):
ball_speed_x = 10
ball_speed_x *= -1
ball_speed_y = 9
ball_speed_y *= -1
# AI for opponent, if ball is higher, go up, if ball is lower, go down
if ball.y <= opponent.centery:
opponent.y -= opponent_speed
if ball.y >= opponent.centery:
opponent.y += opponent_speed
# don't let the slides go too high
player.y += player_speed
if player.top <= 0:
player.top = 0
if player.bottom >= HEIGHT:
player.bottom = HEIGHT
if opponent.top <= 0:
opponent.top = 0
if opponent.bottom >= HEIGHT:
opponent.bottom = HEIGHT
screen.fill((0,0,0))
pygame.draw.rect(screen, (0, 255, 0), player)
pygame.draw.rect(screen, (255, 0, 0), opponent)
pygame.draw.ellipse(screen, (255, 255, 255), ball)
pygame.draw.aaline(screen, (0,0,255), (WIDTH/2, 0), (WIDTH/2, HEIGHT))
pygame.display.update()
clock.tick(60)
See Sometimes the ball doesn't bounce off the paddle in pong game. When the ball hits the player, you need to reverse the velocity along the x-axis and limit the position of the ball ball through the paddle:
while True:
# [...]
if ball.colliderect(opponent):
ball.left = opponent.right
ball_speed_x *= -1
if ball.colliderect(player):
ball.right = player.left
ball_speed_x *= -1

Pong paddles have some weird bug preventing from letting a player score [duplicate]

This question already has an answer here:
How to detect collisions between two rectangular objects or images in pygame
(1 answer)
Closed 2 years ago.
I have made a pong game. Now the problem is that when I move the paddle, the ball doesn't go past it but bounces of some invisible wall. Here is my code and run it yourselves and you can see the problem(You have to move the paddles around a little bit)
# Pong
# Importing libraries
import pygame
import random
import time
# Initializing PyGame
pygame.init()
# Creating a font
font = pygame.font.SysFont(None, 30)
# Set the height and width of the screen
window_width = 700
window_height = 500
size = [window_width, window_height]
game_win = pygame.display.set_mode(size)
# Creating a messaging system
def message(sentence, x, y):
sentence = font.render(sentence, True, white)
game_win.blit(sentence, [x, y])
# Creating a color
white = (225, 225, 225)
black = (0, 0, 0)
# Setting up ball
ball_size = 25
class Ball:
"""
Class to keep track of a ball's location and vector.
"""
def __init__(self):
self.x = 0
self.y = 0
self.change_x = 0
self.change_y = 0
def make_ball():
ball = Ball()
# Starting position of the ball.
# Take into account the ball size so we don't spawn on the edge.
ball.x = 350
ball.y = 250
# Speed and direction of rectangle
ball.change_x = 5
ball.change_y = 5
return ball
def main():
# Scores
left_score = 0
right_score = 0
pygame.init()
pygame.display.set_caption("Ping Pong")
# Loop until the user clicks the close button.
done = False
# Used to manage how fast the screen updates
clock = pygame.time.Clock()
ball_list = []
ball = make_ball()
ball_list.append(ball)
# Right paddle coordinates
y = 200
y_change = 0
x = 50
# Left paddle coordinates
y1 = 200
y1_change = 0
x1 = 650
while not done:
# --- Event Processing
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_w:
y_change = -5
elif event.key == pygame.K_s:
y_change = 5
elif event.key == pygame.K_UP:
y1_change = -5
elif event.key == pygame.K_DOWN:
y1_change = 5
elif event.type == pygame.KEYUP:
y_change = 0
y1_change = 0
y += y_change
y1 += y1_change
if y > window_height - 100:
y -= 5
if y < 50:
y += 5
if y1 > window_height - 100:
y1 -= 5
if y1 < 50:
y1 += 5
message("Left player score: " + str(left_score), 10, 10)
message("Right player score: " + str(right_score), 490, 10)
# Drawing a left paddle
pygame.draw.rect(game_win, white, [x, y, 25, 100])
# Drawing a right paddle
pygame.draw.rect(game_win, white, [x1, y1, 25, 100])
# Updating screen to changes take place
pygame.display.update()
# Logic
for ball in ball_list:
# Move the ball's center
ball.x += ball.change_x
ball.y += ball.change_y
# Bounce the ball if needed
if ball.y > 50 - ball_size or ball.y < ball_size:
ball.change_y *= -1
if ball.x > window_width - ball_size:
ball.change_x *= -1
left_score += 1
if ball.x < ball_size:
ball.change_x *= -1
right_score += 1
# Here is the part where it all becomes weird and buggy
if ball.x-ball_size <= x <= ball.x + ball_size:
ball.change_x *= -1
if ball.x-ball_size <= x1 <= ball.x + ball_size:
ball.change_x *= -1
if ball.y-ball_size <= y <= ball.y + ball_size:
ball.change_x *= -1
if ball.y-ball_size <= y1 <= ball.y + ball_size:
ball.change_x *= -1
if right_score == 10:
message('RIGHT PLAYER HAS WON!!', 300, 200)
time.sleep(10)
done = True
elif left_score == 10:
message("LEFT PLAYER HAS WON!!", 300, 200)
time.sleep(10)
done = True
# Drawing
# Set the screen background
game_win.fill(black)
# Draw the balls
for ball in ball_list:
pygame.draw.circle(game_win, white, [ball.x, ball.y], ball_size)
# Wrap-up
# Limit to 60 frames per second
clock.tick(60)
# Go ahead and update the screen with what we've drawn.
pygame.display.flip()
# Close everything down
pygame.quit()
if __name__ == "__main__":
main()
When I move the paddle away, the ball can still bounce off some invisible wall. Everything else is good, just the part where I have put # Bounce if needed.
I recommend to compute the bounding rectangle of the ball and the paddles and to use pygame.Rect and colliderect() to detect the collision between a ball and a paddle.
See alos Sometimes the ball doesn't bounce off the paddle in pong game.
For instance:
def main():
# [...]
while not done:
# [...]
# Logic
for ball in ball_list:
# [...]
ball_rect = pygame.Rect(ball.x-ball_size, ball.y-ball_size, ball_size*2, ball_size*2)
left_paddle_rect = pygame.Rect(x, y, 25, 75)
if ball.change_x < 0 and ball_rect.colliderect(left_paddle_rect):
ball.change_x = abs(ball.change_x)
right_paddle_rect = pygame.Rect(x1, y1, 25, 75)
if ball.change_x > 0 and ball_rect.colliderect(right_paddle_rect):
ball.change_x = -abs(ball.change_x)
Furthermore, there the height of the window is 500 rather than 50_
if ball.y > 50 - ball_size or ball.y < ball_size:
if ball.y > 500 - ball_size or ball.y < ball_size:
I recommend to remove the multiple calls to pygame.display.flip() respectively pygame.display.update(). Do just one update of the display at the end of the main application loop. See the complete example:
# Importing libraries
import pygame
import random
import time
# Initializing PyGame
pygame.init()
# Creating a font
font = pygame.font.SysFont(None, 30)
# Set the height and width of the screen
window_width = 700
window_height = 500
size = [window_width, window_height]
game_win = pygame.display.set_mode(size)
# Creating a messaging system
def message(sentence, x, y):
sentence = font.render(sentence, True, white)
game_win.blit(sentence, [x, y])
# Creating a color
white = (225, 225, 225)
black = (0, 0, 0)
# Setting up ball
ball_size = 25
class Ball:
"""
Class to keep track of a ball's location and vector.
"""
def __init__(self):
self.x = 0
self.y = 0
self.change_x = 0
self.change_y = 0
def make_ball():
ball = Ball()
# Starting position of the ball.
# Take into account the ball size so we don't spawn on the edge.
ball.x = 350
ball.y = 250
# Speed and direction of rectangle
ball.change_x = 5
ball.change_y = 5
return ball
def main():
# Scores
left_score = 0
right_score = 0
pygame.init()
pygame.display.set_caption("Ping Pong")
# Loop until the user clicks the close button.
done = False
# Used to manage how fast the screen updates
clock = pygame.time.Clock()
ball_list = []
ball = make_ball()
ball_list.append(ball)
# Right paddle coordinates
y = 200
y_change = 0
x = 50
# Left paddle coordinates
y1 = 200
y1_change = 0
x1 = 650
while not done:
# --- Event Processing
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_w:
y_change = -5
elif event.key == pygame.K_s:
y_change = 5
elif event.key == pygame.K_UP:
y1_change = -5
elif event.key == pygame.K_DOWN:
y1_change = 5
elif event.type == pygame.KEYUP:
y_change = 0
y1_change = 0
y += y_change
y1 += y1_change
if y > window_height - 100:
y -= 5
if y < 50:
y += 5
if y1 > window_height - 100:
y1 -= 5
if y1 < 50:
y1 += 5
message("Left player score: " + str(left_score), 10, 10)
message("Right player score: " + str(right_score), 490, 10)
# Logic
for ball in ball_list:
# Move the ball's center
ball.x += ball.change_x
ball.y += ball.change_y
# Bounce the ball if needed
if ball.y > 500 - ball_size or ball.y < ball_size:
ball.change_y *= -1
if ball.x > window_width - ball_size:
ball.change_x *= -1
left_score += 1
if ball.x < ball_size:
ball.change_x *= -1
right_score += 1
# Here is the part where it all becomes weird and buggy
ball_rect = pygame.Rect(ball.x-ball_size, ball.y-ball_size, ball_size*2, ball_size*2)
left_paddle_rect = pygame.Rect(x, y, 25, 75)
if ball.change_x < 0 and ball_rect.colliderect(left_paddle_rect):
ball.change_x = abs(ball.change_x)
right_paddle_rect = pygame.Rect(x1, y1, 25, 75)
if ball.change_x > 0 and ball_rect.colliderect(right_paddle_rect):
ball.change_x = -abs(ball.change_x)
if right_score == 10:
message('RIGHT PLAYER HAS WON!!', 300, 200)
time.sleep(10)
done = True
elif left_score == 10:
message("LEFT PLAYER HAS WON!!", 300, 200)
time.sleep(10)
done = True
# Drawing
# Set the screen background
game_win.fill(black)
# Drawing a left paddle
pygame.draw.rect(game_win, white, [x, y, 25, 100])
# Drawing a right paddle
pygame.draw.rect(game_win, white, [x1, y1, 25, 100])
# Draw the balls
for ball in ball_list:
pygame.draw.circle(game_win, white, [ball.x, ball.y], ball_size)
# Go ahead and update the screen with what we've drawn.
pygame.display.flip()
# Wrap-up
# Limit to 60 frames per second
clock.tick(60)
# Close everything down
pygame.quit()
if __name__ == "__main__":
main()

rect.collisionrect not working between two rects [duplicate]

This question already has an answer here:
How to detect collisions between two rectangular objects or images in pygame
(1 answer)
Closed 2 years ago.
I'm having trouble getting my collision to work in my game, I am using two rects, one acting as a paddle and one as a ball. I am using the normal pygame statement, rect1.colliderect(rect2), but for some reason it is not working.
Here is the line of code for the rects collision
#collision of ball with paddle
if (paddle.colliderect(ball)):
ball_x = 10
ball_y = 10
Not sure what's wrong. Here is the full coding if you wanna run it.
#December 16, 2019
#Final Project - Breakout
#IMPORTING LIBRARIES-----
import pygame
import sys
import time
#INITIALIZING SCREEN SIZE-----
pygame.init()
screen_size = (700, 750)
screen = pygame.display.set_mode((screen_size),0)
pygame.display.set_caption("BREAKOUT")
#retrieve screen measurements
screen_w = screen.get_width()
screen_h = screen.get_height()
#retrieve position of center of screen
center_x = int(screen_w/2)
center_y = int(screen_h/2)
#COLOURS-----
WHITE = (255,255,255)
BLACK = (0, 0, 0)
GREEN = (0,255,0)
RED = (255,0,0)
BLUE = (0,0,255)
PURPLE = (154, 136, 180)
#BACKGROUND-----
screen.fill(BLACK)
pygame.display.update()
#SPEED-----
clock = pygame.time.Clock()
FPS = 60 #set frames per second
speed = [4,4]
paddle_speed = 6
#VARIABLES-----
#paddle
paddle_w = 100
paddle_h = 10
paddle_x = 500
paddle_y = 670
paddle_dx = 0
paddle_dy = 0
#ball
ball_w = 10
ball_h = 10
ball_x = center_x
ball_y = center_y
#RECTS-----
paddle = pygame.Rect(paddle_x, paddle_y, paddle_w, paddle_h)
ball = pygame.Rect(ball_x, ball_y, ball_w, ball_h)
#LOOPS-----
game = False
#loop for game
game = True
while game:
for event in pygame.event.get():
if event.type ==pygame.QUIT:
game = False
pygame.quit()
sys.exit()
#moving paddle with keys
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
paddle_dx = -paddle_speed
elif event.key == pygame.K_RIGHT:
paddle_dx = paddle_speed
if event.type == pygame.KEYUP:
paddle_dx = 0
#constrain this loop to the specified FPS
clock.tick(FPS)
#PADDLE EVENTS-----
#store old paddle positions
old_paddle_x = paddle.x
old_paddle_y = paddle.y
#moving the paddle rect
paddle.move_ip(paddle_dx, paddle_dy)
#check to see if rect has left screen
if paddle.left < 0 or paddle.right > screen_w:
paddle.x = old_paddle_x
#BALL EVENTS-----
#moving ball
ball = ball.move(speed)
#collision left & right
if ball.left < 0 or ball.right > screen_w:
speed[0] = -speed[0]
#collision top
if ball.top < 0 or ball.bottom > screen_h:
speed[1] = -speed[1]
#collision of ball with paddle
if (paddle.colliderect(ball)):
ball_x = 10
ball_y = 10
#removes screen trail
screen.fill(BLACK)
#drawing paddle/ball inside rect
pygame.draw.rect(screen,PURPLE,paddle,0)
pygame.draw.rect(screen,WHITE,ball,0)
#updating the screen
pygame.display.update()
pygame.quit()
sys.exit()
The position of the ball is defined by the pygame.Rect object ball. ball_x and ball_y is just used to initialize ball.
You have to set ball.x = 10 and ball.y = 10 rather than ball_x = 10 and ball_y = 10:
if paddle.colliderect(ball):
ball.x = 10
ball.y = 10
To make the ball bounce of the paddle you have to invert speed[1] rather than changing the position of the ball by ball.x = 10 and ball.y = 10:
if paddle.colliderect(ball):
speed[1] = -speed[1]
I use this for rect collision :
if not (x1 >= x2 + w2 or x1 + w1 <= x2 or y1 >= y2 + h2 or y1 + h1 <= y2):
collision = True
else:
collision = False

Forwards movement in a sprite

I have been making a game in pygame where 2 squares hit a ball around score when they hit it into a net. I have added a minimalistic version of the code underneath.
When the ball hits the square, I am trying to make the ball go forward and a bit upwards before coming back down due to gravity in a realistic-looking way. Is there any possible way of doing this?
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()
pg.display.set_caption("Super Acrobatic Rocket Powered Battle Polygons(SARPBP)")
x = 740
y = 500
x2 = 395
y2 = 15
x_change = 0
y_change = 0
bluespeed = 5
ground_pos = 70
on_ground = False
bluecar = pg.Surface((60, 30))
bluecar.fill((0,0,255))
bluegoal = pg.Surface((60,150))
bluegoal.fill((0,0,255))
BALL = pg.Surface((30, 30), pg.SRCALPHA)
pg.draw.circle(BALL, [0,0,0], [15, 15], 15)
ball_pos = Vector2(x2, y2)
ballrect = BALL.get_rect(center=ball_pos)
ball_vel = Vector2(0, 0)
ball_mask = pg.mask.from_surface(BALL)
mask_blue = pg.mask.from_surface(bluecar)
mask_ball = pg.mask.from_surface(BALL)
pos_blue = Vector2(x,y)
bluerect = bluecar.get_rect(center = pos_blue)
vel_blue = Vector2(bluespeed,0)
# A constant value that you add to the y_change each frame.
GRAVITY = .5
GRAVITY2 = .5
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:
x_change = -5
elif event.key == pg.K_d:
x_change = 5
elif event.key == pg.K_w:
if on_ground: # Only jump if the player is on_ground.
y_change = -12
on_ground = False
elif event.type == pg.KEYUP:
if event.key == pg.K_a and x_change < 0:
x_change = 0
elif event.key == pg.K_d and x_change > 0:
x_change = 0
ball_vel.y += GRAVITY # Accelerate downwards.
ball_pos += ball_vel # Move the ball.
ballrect.center = ball_pos
# Bounce when the ball touches the bottom of the screen.
if ballrect.bottom >= height - ground_pos:
# Just invert the y-velocity to bounce.
ball_vel.y *= -0.7 # Change this value to adjust the elasticity.
# Don't go below the ground.
ballrect.bottom = height - ground_pos
ball_pos.y = ballrect.centery
# Add the GRAVITY value to y_change, so that
# the object moves faster each frame.
y_change += GRAVITY
x += x_change
y += y_change
# Stop the object when it's near the bottom of the screen.
if y >= height - 100:
y = height - 100
y_change = 0
on_ground = True
if x == 0:
x = 5
elif x == 740:
x = 735
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.
ball_vel = Vector2(vel_blue) * 1.4
# Draw everything.
screen.fill(LIGHTBLUE)
pg.draw.line(screen, (0, 0, 0), (0, height-70), (width, height-70))
screen.blit(bluecar, (x, y))
screen.blit(BALL, ballrect)
pg.display.update()
clock.tick(60)
pg.quit()
You have to update the position of the blue rect as well each frame or it will stay at it's original position and the collision detection won't work.
Then I suggest getting rid of the x, y, x2, y2, x_change, y_change variables, since you can just use the vectors pos_blue and vel_blue.
When the player collides with the ball you can set the y-component of the ball's velocity to some negative value, so that it moves up (I just set the x-component to the player's velocity.x here if the player is moving).
ball_vel = Vector2(vel_blue.x, -17)
Note that you have to use surfaces with an alpha channel or the pygame.mask.from_surface won't work, therefore I pass pygame.SRCALPHA as the second argument to pygame.Surface (you could also call convert_alpha() or set_colorkey()).
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
on_ground = False
ground_y = height - 100
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:
if on_ground: # Only jump if the player is on_ground.
vel_blue.y = -12
on_ground = False
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
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
# 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
on_ground = True
if bluerect.x < 0:
bluerect.x = 0
pos_blue.x = bluerect.centerx
elif bluerect.right > width:
bluerect.right = width
pos_blue.x = bluerect.centerx
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()
clock.tick(60)
pg.quit()

Object bounce and movement [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I have been making a game where 2 people try to score a goal on a net, but to do so, the ball must move and bounce. I would like the ball to move forward and upwards at a realistic angle, then bounce on the ground a few times before becoming stationary again. I'm not sure how to achieve this, my code is below, I really hope someone can edit it to work.
When I run the code, I get the following error
TypeError: unsupported operand type(s) for +=: 'pygame.math.Vector2' and 'int'(for the line "ball_vel += x2")
import pygame as pg
from pygame.math import Vector2
pg.init()
LIGHTBLUE = pg.Color('lightskyblue2')
screen = pg.display.set_mode((800, 600))
width, height = screen.get_size()
clock = pg.time.Clock()
pg.display.set_caption("Super Acrobatic Rocket Powered Battle Polygons(SARPBP)")
x = 740
y = 0
x2 = 370
y2 = 500
x_change = 0
y_change = 0
bluespeed = 5
on_ground = False
bluecar = pg.Surface((60, 30))
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(x2, y2)
ballrect = BALL.get_rect(center=ball_pos)
ball_vel = Vector2(0, 0)
ball_mask = pg.mask.from_surface(BALL)
mask_blue = pg.mask.from_surface(bluecar)
mask_ball = pg.mask.from_surface(BALL)
pos_blue = Vector2(x,y)
bluerect = bluecar.get_rect(center = pos_blue)
vel_blue = Vector2(bluespeed,0)
# A constant value that you add to the y_change each frame.
GRAVITY = .5
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:
x_change = -5
elif event.key == pg.K_d:
x_change = 5
elif event.key == pg.K_w:
if on_ground: # Only jump if the player is on_ground.
y_change = -12
on_ground = False
elif event.type == pg.KEYUP:
if event.key == pg.K_a and x_change < 0:
x_change = 0
elif event.key == pg.K_d and x_change > 0:
x_change = 0
# Add the GRAVITY value to y_change, so that
# the object moves faster each frame.
y_change += GRAVITY
x += x_change
y += y_change
#x += vel_blue
# Stop the object when it's near the bottom of the screen.
if y >= height - 100:
y = height - 100
y_change = 0
on_ground = True
if x == 0:
x = 5
elif x == 740:
x = 735
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.
ball_vel = Vector2(x_change) * 1.4
ball_vel += x2
# Draw everything.
screen.fill(LIGHTBLUE)
pg.draw.line(screen, (0, 0, 0), (0, height-70), (width, height-70))
screen.blit(bluecar, (x, y))
screen.blit(BALL, (x2,y2))
pg.display.update()
clock.tick(60)
pg.quit()
You can just invert the y component of the velocity vector when the ball touches the ground to make it bounce: ball_vel.y *= -1. Change the value by which you multiply the velocity to adjust the elasticity: ball_vel.y *= -0.94 # Lower elasticity.
import pygame as pg
from pygame.math import Vector2
pg.init()
LIGHTBLUE = pg.Color('lightskyblue2')
screen = pg.display.set_mode((800, 600))
width, height = screen.get_size()
clock = pg.time.Clock()
BALL = pg.Surface((30, 30), pg.SRCALPHA)
pg.draw.circle(BALL, [0,0,0], [15, 15], 15)
ball_pos = Vector2(370, 0)
ballrect = BALL.get_rect(center=ball_pos)
ball_vel = Vector2(0, 0)
ground_pos = 70
GRAVITY = .9
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
ball_vel.y += GRAVITY # Accelerate downwards.
ball_pos += ball_vel # Move the ball.
ballrect.center = ball_pos
# Bounce when the ball touches the bottom of the screen.
if ballrect.bottom >= height - ground_pos:
# Just invert the y-velocity to bounce.
ball_vel.y *= -0.94 # Change this value to adjust the elasticity.
# Don't go below the ground.
ballrect.bottom = height - ground_pos
ball_pos.y = ballrect.centery
# Draw everything.
screen.fill(LIGHTBLUE)
pg.draw.line(screen, (0, 0, 0), (0, height-70), (width, height-ground_pos))
screen.blit(BALL, ballrect)
pg.display.update()
clock.tick(60)
pg.quit()
The error says that you cannot add an int and Vector2 together directly. The program does not know how to. What you need to do instead is to add to the x value of the vector. Change the line ball_vel += x2 to ball_vel.x += x2.

Categories

Resources