I have a huge assignment due in class and Im lost like , asking god to spare me lost. Im making a brick game and the most basic function of getting the ball to bounce of the walls I cant figure out , my teacher is too busy and classmates are competitive , I dont want you to write the code for me, I just dont know how to code it. I keep getting errors and its just been hell the last couple of classes.
import math
import pygame
import random
pygame.init()
screen = pygame.display.set_mode([800,600])
done = False
clock = pygame.time.Clock()
#DEFINE COLORS
WHITE = (255,255,255)
BLUE=(0,102,204)
LIGHT_BLUE=(0,0,204)
pink=(238,130,238)
#import images
#lives = pygame.image.load("heart.png")
# initialize font; must be called after 'pygame.init()' to avoid 'Font not Initialized' error
myfont = pygame.font.SysFont("monospace", 15)
#var carying the space of the game
top = -100
left= -50
right= -750
bottom= -600
angle = math.radians(random.randint(-140,-30))
class game_screen():
def draw_screen():
pygame.draw.rect(screen,BLUE,(50,100,700,600))
def save_button():
pygame.draw.rect(screen,pink,(500,20,50,30),0)
save = "Save"
label = myfont.render(save, 40, (0,0,0))
screen.blit(label, (505, 20))
def quit_button():
pygame.draw.rect(screen,pink,(600,20,50,30),0)
quit1 = "Quit"
label = myfont.render(quit1, 40, (0,0,0))
screen.blit(label, (605, 20))
mX, mY = pygame.mouse.get_pos()
mouseButtons = pygame.mouse.get_pressed()
if mouseButtons[0] == True:
if (mX in range (600-20,600+20) and mY in range (20-20,20+20)):
pygame.quit()
#def display_lives():
#lives_counter = screen.blit(lives,(50,30))
#lives_counter2 = screen.blit(lives,(120,30))
#lives_counter3 = screen.blit(lives,(190,30))
class PADDLE:
def __init__(self,xpos,ypos):
self.x = xpos
self.y = ypos
def draw(self): # draws paddle
pygame.draw.rect(screen,pink,(self.x,self.y,70,20))
def move(self):
keys = pygame.key.get_pressed() #checking pressed keys
if keys[pygame.K_LEFT]:
if self.x<=50:
self.x=50
else:
self.x -= 10
elif keys[pygame.K_RIGHT]:
if self.x >=680:
self.x = 680
else:
self.x += 10
class BALL:
def __init__(self,paddle1):
self.x = (paddle1.x+35)
self.y = (paddle1.y-5)
self.speed = 0
self.speedX = 0
self.speedY = 0
self.direction = 200
def draw(self):
pygame.draw.circle(screen,WHITE,(self.x,self.y),10)
def bounce(self):
self.direction = (180 - self.direction) % 360
def move_ball(self):
keys = pygame.key.get_pressed() #checking pressed keys
ball_on_paddle = True
if ball_on_paddle == True :
self.x = (paddle1.x+35)
self.y = (paddle1.y-5)
self.speed = 0
if keys[pygame.K_UP] == True:
ball_on_paddle = False
print("a")
self.speed = 10
self.speedX += int(math.cos(angle)* self.speed)
self.speedY += int(math.sin(angle)* self.speed)
print(bottom)
print(self.speedY)
if self.y <= 0:
self.bounce(0)
self.y = 1
if self.x <= 0:
self.direction = (360 - self.direction) % 360
self.x = 1
if self.x > self.screenwidth - self.width:
self.direction = (360 - self.direction) % 360
self.x = self.screenwidth - self.width - 1
paddle1 = PADDLE(350,550)
ball1 = BALL(paddle1)
# MAIN LOOP
while not done:
screen.fill((LIGHT_BLUE))
game_screen.draw_screen()
game_screen.save_button()
game_screen.quit_button()
paddle1.draw()
paddle1.move()
ball1.draw()
ball1.move_ball()
ball1.bounce()
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
pygame.display.flip()
clock.tick(60)
pygame.quit()
The collission between ball and wall can be detected by comparing the ball's coordinates with the coordinates of the walls. Reflecting the ball means a multiplication of either speedX or speedY - depending on the wall - with -1.
The easiest way to do this is to make a function bounce(self) in your class BALL.
First, you decide which border you touch.
Then, you change the angle of the ball according to which border you bounce on. If you work in a 360-degree system, the angle is mirrored:
around a vertical axis when the ball bounces of the bottom and the top border. So angle = 180 - angle.
around a horizontal axis when the ball bounces of the side borders. So angle = - angle .
And lastly, you have to move the ball back what it has travelled too much.
Like you can see, the ball travels in 1 iteration from C1 to C3', while we want it to go to C3. We can achieve this by reflecting it around the axis BOUNDARY_X - ball_size.
The new x-coordinate of the ball becomes then x = BOUNDARY_X - ball_size - (x - BOUNDARY_X + ball_size) = 2*(BOUNDARY_X - ball_size) - x .
When we cast that into code, we get:
class BALL:
def bounce(self):
if self.x < ball_size:
self.angle = math.pi - self.angle
self.x = 2*ball_size - self.x
elif self.x > DISPLAY_WIDTH - ball_size:
self.angle = math.pi - self.angle
self.x = 2*(DISPLAY_WIDTH - ball_size) - self.x
if self.y < ball_size:
self.angle = - self.angle
self.y = 2*ball_size - self.y
elif self.y > DISPLAY_HEIGHT - ball_size:
self.angle = - self.angle
self.y = 2*(DISPLAY_HEIGHT - ball_size) - self.y
Related
Apologies for asking so many questions recently. I'm just starting to get into pygame.
With my previous questions I don't think I've been wording it properly for what I am trying to do.
Here is a quick picture I did to try demonstrate
This is a single background image or map that I would like the player to move across. The red X is just a starting place for the character.
I'm try to make it so when I move the player the background will also follow as if the player is moving through the map. I've already limited the player from not being able to go off the borders of the actual screen. Just having a bit of trouble now trying to make it so the single image will move along the player and if the player reaches the end of the map picture movement stops. I have seen people use scrolling and duplicating the image when the player moves. I just want to see it to the single image that the player will move across. I don't want to worry about collisions just be able to get the movement working.
This is the code I am currently using:
from pygame.locals import *
from math import sin
pygame.display.set_caption("TEST")
clock = pygame.time.Clock()
time_passed = 0
class Player():
def __init__(self,x,y):
self.Image = pygame.image.load("myAvatar.png").convert()
self.x = 200
self.y = 200
def getX(self):
return self.rect.x
def getY(self):
return self.rect.y
def handle_keys(self,screenHeight,screenWidth):
key = pygame.key.get_pressed()
dist = 2
if key[K_LEFT] and self.x > 0:
self.x -= 500 * time_passed
if key[K_RIGHT] and self.x < screenWidth -20:
self.x += 500 * time_passed
if key[K_UP] and self.y > 0:
self.y -= 500 * time_passed
if key[K_DOWN] and self.y < screenHeight -20:
self.y += 500 * time_passed
def draw(self, game_window):
self.Image = pygame.transform.scale(self.Image,(20,20))
game_window.blit(self.Image, (int(self.x), int(self.y)))
class Map():
def __init__(self):
self.Image = pygame.image.load("test2.png").convert()
self.rect = self.Image.get_rect()
self.x = 0
self.y = 0
def draw(self, game_window,screenHeight,screenWidth):
self.x = min(max(self.x, player.x - 2 * screenWidth / 2), player.x - screenWidth / 2)
self.y = min(max(self.y, player.y -2 * screenHeight / 2), player.y - screenHeight / 2)
game_window.blit(self.Image,(-self.x,-self.y))
class Enemy():
def __init__ (self,x,y):
self.Image = pygame.image.load("WC.jpg").convert()
self.rect = self.Image.get_rect(topleft = (x,y))
def draw(self, game_window):
self.Image = pygame.transform.scale(self.Image,(20,20))
game_window.blit(self.Image, (self.rect.x, self.rect.y))
pygame.init()
clock = pygame.time.Clock()
screenWidth = 400
screenHeight = 400
game_window = pygame.display.set_mode((screenWidth,screenHeight))
player = Player(200,200)
map = Map()
enemy = Enemy(250,250)
leave = False
while not leave:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
running = False
player.handle_keys(screenHeight,screenWidth)
game_window.fill((0,0,0))
map.draw(game_window,screenHeight,screenWidth)
#enemy.draw(game_window)
player.draw(game_window)
pygame.display.update()
pygame.display.flip()
time_passed = clock.tick() / 1000
pygame.quit()
quit()
Thanks
Shay
The player's movement depends on the size of the map. The x and y attributes of the "player" store the position on the map and are limited to the size of the map (map_size). The player is always drawn in the center of the screen, except it is near the boarders of the map:
class Player():
def __init__(self, x, y):
self.Image = pygame.image.load("myAvatar.png").convert()
self.x = 200
self.y = 200
def handle_keys(self, map_size):
key = pygame.key.get_pressed()
self.x += (key[K_RIGHT] - key[K_LEFT]) * 500 * time_passed
self.y += (key[K_DOWN] - key[K_UP]) * 500 * time_passed
self.x = max(0, min(map_size[0]-20, self.x))
self.y = max(0, min(map_size[1]-20, self.y))
def draw(self, game_window, map_size):
window_size = game_window.get_size()
center = window_size[0] // 2, window_size[0] // 2
pos = [self.x, self.y]
for i in range(2):
if center[i] < pos[i] <= map_size[i]-center[i]:
pos[i] = center[i]
elif pos[i] > map_size[i] - center[i]:
pos[i] = window_size[i] - map_size[i] + pos[i]
game_window.blit(self.Image, (int(pos[0]), int(pos[1])))
The player's position on the map is centered in the window:
class Map():
def __init__(self):
self.Image = pygame.image.load("test2.png").convert()
def draw(self, game_window):
window_size = game_window.get_size()
map_size = self.Image.get_size()
x = max(0, min(map_size[0] - window_size[0], player.x - 200))
y = max(0, min(map_size[1] - window_size[1], player.y - 200))
game_window.blit(self.Image, (-x, -y))
Application loop:
leave = False
while not leave:
for event in pygame.event.get():
if event.type == pygame.QUIT:
leave = True
player.handle_keys(map.Image.get_size())
game_window.fill((0,0,0))
map.draw(game_window)
#enemy.draw(game_window)
player.draw(game_window, map.Image.get_size())
pygame.display.update()
pygame.display.flip()
time_passed = clock.tick() / 1000
pygame.quit()
Apologies for asking so many questions recently. I'm just starting to get into pygame.
With my previous questions I don't think I've been wording it properly for what I am trying to do.
Here is a quick picture I did to try demonstrate
This is a single background image or map that I would like the player to move across. The red X is just a starting place for the character.
I'm try to make it so when I move the player the background will also follow as if the player is moving through the map. I've already limited the player from not being able to go off the borders of the actual screen. Just having a bit of trouble now trying to make it so the single image will move along the player and if the player reaches the end of the map picture movement stops. I have seen people use scrolling and duplicating the image when the player moves. I just want to see it to the single image that the player will move across. I don't want to worry about collisions just be able to get the movement working.
This is the code I am currently using:
from pygame.locals import *
from math import sin
pygame.display.set_caption("TEST")
clock = pygame.time.Clock()
time_passed = 0
class Player():
def __init__(self,x,y):
self.Image = pygame.image.load("myAvatar.png").convert()
self.x = 200
self.y = 200
def getX(self):
return self.rect.x
def getY(self):
return self.rect.y
def handle_keys(self,screenHeight,screenWidth):
key = pygame.key.get_pressed()
dist = 2
if key[K_LEFT] and self.x > 0:
self.x -= 500 * time_passed
if key[K_RIGHT] and self.x < screenWidth -20:
self.x += 500 * time_passed
if key[K_UP] and self.y > 0:
self.y -= 500 * time_passed
if key[K_DOWN] and self.y < screenHeight -20:
self.y += 500 * time_passed
def draw(self, game_window):
self.Image = pygame.transform.scale(self.Image,(20,20))
game_window.blit(self.Image, (int(self.x), int(self.y)))
class Map():
def __init__(self):
self.Image = pygame.image.load("test2.png").convert()
self.rect = self.Image.get_rect()
self.x = 0
self.y = 0
def draw(self, game_window,screenHeight,screenWidth):
self.x = min(max(self.x, player.x - 2 * screenWidth / 2), player.x - screenWidth / 2)
self.y = min(max(self.y, player.y -2 * screenHeight / 2), player.y - screenHeight / 2)
game_window.blit(self.Image,(-self.x,-self.y))
class Enemy():
def __init__ (self,x,y):
self.Image = pygame.image.load("WC.jpg").convert()
self.rect = self.Image.get_rect(topleft = (x,y))
def draw(self, game_window):
self.Image = pygame.transform.scale(self.Image,(20,20))
game_window.blit(self.Image, (self.rect.x, self.rect.y))
pygame.init()
clock = pygame.time.Clock()
screenWidth = 400
screenHeight = 400
game_window = pygame.display.set_mode((screenWidth,screenHeight))
player = Player(200,200)
map = Map()
enemy = Enemy(250,250)
leave = False
while not leave:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
running = False
player.handle_keys(screenHeight,screenWidth)
game_window.fill((0,0,0))
map.draw(game_window,screenHeight,screenWidth)
#enemy.draw(game_window)
player.draw(game_window)
pygame.display.update()
pygame.display.flip()
time_passed = clock.tick() / 1000
pygame.quit()
quit()
Thanks
Shay
The player's movement depends on the size of the map. The x and y attributes of the "player" store the position on the map and are limited to the size of the map (map_size). The player is always drawn in the center of the screen, except it is near the boarders of the map:
class Player():
def __init__(self, x, y):
self.Image = pygame.image.load("myAvatar.png").convert()
self.x = 200
self.y = 200
def handle_keys(self, map_size):
key = pygame.key.get_pressed()
self.x += (key[K_RIGHT] - key[K_LEFT]) * 500 * time_passed
self.y += (key[K_DOWN] - key[K_UP]) * 500 * time_passed
self.x = max(0, min(map_size[0]-20, self.x))
self.y = max(0, min(map_size[1]-20, self.y))
def draw(self, game_window, map_size):
window_size = game_window.get_size()
center = window_size[0] // 2, window_size[0] // 2
pos = [self.x, self.y]
for i in range(2):
if center[i] < pos[i] <= map_size[i]-center[i]:
pos[i] = center[i]
elif pos[i] > map_size[i] - center[i]:
pos[i] = window_size[i] - map_size[i] + pos[i]
game_window.blit(self.Image, (int(pos[0]), int(pos[1])))
The player's position on the map is centered in the window:
class Map():
def __init__(self):
self.Image = pygame.image.load("test2.png").convert()
def draw(self, game_window):
window_size = game_window.get_size()
map_size = self.Image.get_size()
x = max(0, min(map_size[0] - window_size[0], player.x - 200))
y = max(0, min(map_size[1] - window_size[1], player.y - 200))
game_window.blit(self.Image, (-x, -y))
Application loop:
leave = False
while not leave:
for event in pygame.event.get():
if event.type == pygame.QUIT:
leave = True
player.handle_keys(map.Image.get_size())
game_window.fill((0,0,0))
map.draw(game_window)
#enemy.draw(game_window)
player.draw(game_window, map.Image.get_size())
pygame.display.update()
pygame.display.flip()
time_passed = clock.tick() / 1000
pygame.quit()
VIDEO When my player distance is a little farther than the knife they stop shooting at the player I am not sure why? How could I fix it and make sure it shoots where ever the player distance is. Like I don't want it to stop shooting when the player is a little farther away from the projectile I am not sure if I have a range for it to shoot the player or maybe when my player scrolls
when it stops shooting at the player the shooting sound still plays but my projectiles aren't shooting and that causes my sound to keep playing rapidly.
This is how my enemies shoot:
for shootss in shootsright:
shootss.x += shootss.xspeed
shootss.y += shootss.yspeed
if shootss.x > 700 or shootss.x < 0 or shootss.y > 500 or shootss.y < 0:
shootsright.pop(shootsright.index(shootss))
shootss.lookAt((playerman.x,playerman.y))
if box1.health > 25:
if len(shootsright) < 1:
for enemyshoot in enemyshooting:
BULLET_SPEED = 10
start_x = round(enemyshoot.x+enemyshoot.width+-35)
start_y = round(enemyshoot.y + enemyshoot.height+-25)
target_x = playerman.x+playerman.width//2
target_y = playerman.y+playerman.width//2
delta_x, delta_y = target_x - start_x, target_y - start_y
distance = math.sqrt(delta_x ** 2 + delta_y ** 2)
dir_x = BULLET_SPEED * delta_x / distance
dir_y = BULLET_SPEED * delta_y / distance
distance = math.sqrt(dir_x**2 + dir_y**2)
knifesound.play()
if distance > 0:
shootsright.append(enemyboolss(start_x,start_y,(0,0,0),dir_x, dir_y))
This is the class for the projectile:
class projectile(object):
def __init__(self, x, y, dirx, diry, color):
self.x = x
self.y = y
self.dirx = dirx
self.diry = diry
self.isJump = False
self.slash = pygame.image.load("round.png")
self.slash = pygame.transform.scale(self.slash,(self.slash.get_width()//6,self.slash.get_height()//6))
self.rect = self.slash.get_rect()
self.rect.topleft = ( self.x, self.y )
self.speed = 18
self.color = color
self.hitbox = (self.x + -18, self.y, 46,60)
def move(self):
self.x += self.dirx * self.speed
self.y += self.diry * self.speed
def draw(self, window):
self.rect.topleft = (round(self.x), round(self.y))
window.blit(self.slash, self.rect)
self.hitbox = (self.x + -18, self.y, 30,30)
my full code: script
Your screen is 800x800, but the knife code is checking 700x500. The knife projectile is created but immediately removed from the knife list at the next loop:
if shootss.x > 700 or shootss.x < 0 or shootss.y > 500 or shootss.y < 0:
shootsright.pop(shootsright.index(shootss))
shootss.lookAt((playerman.x,playerman.y))
This causes the knife flash but no movement for knives past 700.
After setting the check to 800, the knives worked correctly including sound.
Let's consider following code (balls with random centers and velocities collide, with screen surface bounds, and with each other):
import pygame,sys,math
from pygame.locals import *
from random import randrange
WIDTH = 500
HEIGHT = 500
WHITE = (255,255,255)
BLUE = (0, 0, 255)
RADIUS = 10
FPS = 30
DISPLAYSURF = pygame.display.set_mode((WIDTH,HEIGHT),0,32)
TAB = []
fpsClock = pygame.time.Clock()
pygame.display.set_caption('Animation')
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def distance(self, A):
return math.sqrt((A.x-self.x)**2 + (A.y-self.y)**2)
def getTouple(self):
return (self.x,self.y)
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def norm(self):
return math.sqrt(self.x**2 + self.y**2)
class Ball:
def __init__(self, center, radius, velocity):
self.center = center
self.radius = radius
self.velocity = velocity
def __init__(self):
self.radius = RADIUS
self.center = Point(randrange(RADIUS,WIDTH-RADIUS), randrange(RADIUS,HEIGHT-RADIUS))
vx = randrange(-5,5)
vy = randrange(-5,5)
while vx == 0 or vy == 0:
vx = randrange(-5,5)
vy = randrange(-5,5)
self.velocity = Vector(vx,vy)
def draw(self):
self.center.x += self.velocity.x
self.center.y += self.velocity.y
for ball in TAB:
if ball != self:
if ball.center.distance(self.center) <= 2*RADIUS:
tmp = self.velocity
self.velocity = ball.velocity
ball.velocity = tmp
if self.center.x - self.radius <= 0:
self.velocity.x = -self.velocity.x
if self.center.x + self.radius >= WIDTH:
self.velocity.x = -self.velocity.x
if self.center.y - self.radius <= 0:
self.velocity.y = -self.velocity.y
if self.center.y + self.radius >= HEIGHT:
self.velocity.y = -self.velocity.y
pygame.draw.circle(DISPLAYSURF, BLUE, self.center.getTouple(), self.radius, 0)
BallNum = 30
for i in range(BallNum):
k = Ball()
TAB.append(k)
while True:
DISPLAYSURF.fill(WHITE)
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
for ball in TAB:
ball.draw()
pygame.display.update()
fpsClock.tick(FPS)
This code works but not corectly: some balls are 'catched' by the bounds and others 'stick together'. I think the problem is in Ball class method draw. I will be grateful for any ideas on how to improve that code.
The main problem with the code you have posted which causes some of the balls to stick together, is that after they bounce off of each other, they still are in contact, so they bounce off of each other indefinitely which causes them to 'stick together'.
To fix this problem:
Add in an attribute/variable which holds a reference to the ball that it last hit. I called this attribute ball_last_hit and I initialized it to None.
Then, in the collision detection code, simply check to make sure the ball is not in contact with the ball it last hit, with an if statement like this: if ball != self.ball_last_hit:.
Then, if the balls should bounce off of each other, after swapping their velocities, set self.ball_last_hit to ball.
This will solve the 'sticking together' problem between the balls, but the ball could still get 'stuck' to the wall, although it is less common. To solve this you could add in an attribute called something like self.ignore_walls and after each collision with a wall, set it to true, then after a certain amount of time, set it back to false, so it can collide with walls again.
Here is the fixed code:
import pygame,sys,math
from pygame.locals import *
from random import randrange
WIDTH = 500
HEIGHT = 500
WHITE = (255,255,255)
BLUE = (0, 0, 255)
RADIUS = 10
FPS = 30
DISPLAYSURF = pygame.display.set_mode((WIDTH,HEIGHT),0,32)
TAB = []
fpsClock = pygame.time.Clock()
pygame.display.set_caption('Animation')
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def distance(self, A):
return math.sqrt((A.x-self.x)**2 + (A.y-self.y)**2)
def getTouple(self):
return (self.x,self.y)
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def norm(self):
return math.sqrt(self.x**2 + self.y**2)
class Ball:
def __init__(self):
self.radius = RADIUS
self.center = Point(randrange(RADIUS,WIDTH-RADIUS), randrange(RADIUS,HEIGHT-RADIUS))
vx = randrange(-5,5)
vy = randrange(-5,5)
while vx == 0 or vy == 0:
vx = randrange(-5,5)
vy = randrange(-5,5)
self.velocity = Vector(vx,vy)
self.ball_last_hit = None
def draw(self):
self.center.x += self.velocity.x
self.center.y += self.velocity.y
for ball in TAB:
if ball != self:
if ball.center.distance(self.center) <= 2*RADIUS:
if ball != self.ball_last_hit:
tmp = self.velocity
self.velocity = ball.velocity
ball.velocity = tmp
self.ball_last_hit = ball
if self.center.x - self.radius <= 0:
self.velocity.x = -self.velocity.x
if self.center.x + self.radius >= WIDTH:
self.velocity.x = -self.velocity.x
if self.center.y - self.radius <= 0:
self.velocity.y = -self.velocity.y
if self.center.y + self.radius >= HEIGHT:
self.velocity.y = -self.velocity.y
pygame.draw.circle(DISPLAYSURF, BLUE, self.center.getTouple(), self.radius, 0)
BallNum = 30
for i in range(BallNum):
k = Ball()
TAB.append(k)
while True:
DISPLAYSURF.fill(WHITE)
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
for ball in TAB:
ball.draw()
pygame.display.update()
fpsClock.tick(FPS)
Note: instead of using a temp variable, and executing three lines of code, you can swap the balls' velocities with just one this line of code: self.velocity, ball.velocity = ball.velocity, self.velocity.
I hope this answer helped you, and if you have any further questions, please feel free to leave a comment below!
Edit: to get rid of the sticking to the walls, all we need to do is make sure that after a collision with the wall, the ball is not still in contact with the wall, because if it is, it will bounce against the wall indefinitely, and get 'stuck'
After changing the ball's velocity, when it collides with the wall, I just set the center of the ball to one pixel away from the wall, so it is definitely not touching the wall, and this fixed the problem for me.
Here is the new collision detection code, which goes at the end of the draw() method. There are just 4 new lines needed.
if self.center.x - self.radius <= 0:
self.velocity.x = -self.velocity.x
self.center.x = self.radius + 1
if self.center.x + self.radius >= WIDTH:
self.velocity.x = -self.velocity.x
self.center.x = WIDTH - self.radius - 1
if self.center.y - self.radius <= 0:
self.velocity.y = -self.velocity.y
self.center.y = self.radius + 1
if self.center.y + self.radius >= HEIGHT:
self.velocity.y = -self.velocity.y
self.center.y = HEIGHT - self.radius - 1
Note: this simulation gets very tricky if you increase the ball number even higher, because groups of 3 balls start to collide with each other at a time, and your current code is only capable of handling collision between two balls.
I am new to python and am trying to write a game that launches a character and when he interacts with a sprite on the ground, something will change, for example speed. My apologies for the disorganization in my code. I have taken samples from a few tutorials and I can't make them work together.
How do I make the player's collision with the bomb detectable?
import pygame
import random
import math
drag = 1
gravity = (math.pi, .4)
elasticity = .75
# Colors
BLACK = ( 0, 0, 0)
WHITE = ( 255, 255, 255)
BLUE = ( 0, 0, 255)
RED = ( 255, 0, 0)
GREEN = ( 0, 255, 0)
# Screen dimensions
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
def addVectors((angle1, length1), (angle2, length2)):
x = math.sin(angle1) * length1 + math.sin(angle2) * length2
y = math.cos(angle1) * length1 + math.cos(angle2) * length2
angle = 0.5 * math.pi - math.atan2(y, x)
length = math.hypot(x, y)
return (angle, length)
class Player(pygame.sprite.Sprite):
change_x = 0
change_y = 0
level = None
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load('player.png')
image_rect = self.image.get_rect()
self.rect = pygame.rect.Rect(x, y, image_rect.width, image_rect.height)
def update(self):
pass
def move(self):
(self.angle, self.speed) = addVectors((self.angle, self.speed), gravity)
self.rect.x += math.sin(self.angle) * self.speed
self.rect.y -= math.cos(self.angle) * self.speed
self.speed *= drag
def bounce(self):
if self.rect.x > 800 - self.rect.width:
self.rect.x = 2*(800 - self.rect.width) - self.rect.x
self.angle = - self.angle
self.speed *= elasticity
elif self.rect.x < 0:
self.rect.x = 2*self.rect.width - self.rect.x
self.angle = - self.angle
self.speed *= elasticity
if self.rect.y > SCREEN_HEIGHT - self.rect.height:
self.rect.y = 2*(SCREEN_HEIGHT - self.rect.height) - self.rect.y
self.angle = math.pi - self.angle
self.speed *= elasticity
class Bomb(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load('cherry.png')
image_rect = self.image.get_rect()
self.rect = pygame.rect.Rect(x, y, image_rect.width, image_rect.height)
class Level():
bomb_list = None
world_shift = 0
def __init__(self, player):
self.bomb_list = pygame.sprite.Group()
self.player = player
def update(self):
self.bomb_list.update()
def draw(self, screen):
screen.fill(BLACK)
self.bomb_list.draw(screen)
def shift_world(self, shift_x):
self.world_shift += shift_x
for bomb in self.bomb_list:
bomb.rect.x += shift_x
if bomb.rect.x < 0:
self.bomb_list.remove(bomb)
self.bomb_list.add(Bomb(random.randint(SCREEN_WIDTH, 2*SCREEN_WIDTH), 580))
I am not sure if this Level_01 class is even necessary:
class Level_01(Level):
def __init__(self, player):
Level.__init__(self, player)
bombs = 0
for n in range(10):
self.bomb_list.add(Bomb(random.randint(0, SCREEN_WIDTH), 580))
This was my attempt at a collision detection method. I'm not sure if it should be in a class, in main, or seperate. I can't seem to get the list of bombs, and the player at the same time.
def detectCollisions(sprite1, sprite_group):
if pygame.sprite.spritecollideany(sprite1, sprite_group):
sprite_group.remove(pygame.sprite.spritecollideany(sprite1, sprite_group))
print True
else:
print False
def main():
pygame.init()
size = [SCREEN_WIDTH, SCREEN_HEIGHT]
screen = pygame.display.set_mode(size)
active_sprite_list = pygame.sprite.Group()
enemy_list = pygame.sprite.Group()
player = Player(0, 0)
player.speed = 30
player.angle = math.radians(45)
player.rect.x = 500
player.rect.y = SCREEN_HEIGHT - player.rect.height
active_sprite_list.add(player)
done = False
clock = pygame.time.Clock()
current_level = Level_01(player)
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
player.go_left()
if event.key == pygame.K_RIGHT:
player.go_right()
active_sprite_list.update()
enemy_list.update()
player.level = current_level
player.move()
player.bounce()
if player.rect.x >= 500:
diff = player.rect.x - 500
player.rect.x = 500
current_level.shift_world(-diff)
if player.rect.x <= 120:
diff = 120 - player.rect.x
player.rect.x = 120
current_level.shift_world(diff)
current_level.draw(screen)
active_sprite_list.draw(screen)
enemy_list.draw(screen)
clock.tick(60)
pygame.display.flip()
pygame.quit()
if __name__ == "__main__":
main()
Thanks for helping me out!
Probably the easiest thing to do is to draw out pixels (or virtual pixels) and if in drawing bomb/person pixels you find an overlap then you know a collision occurred.
You can however get way more complicated (and efficient) in your collision detection if you need a higher performance solution. See Wikipedia Collision Detection for a reference.
I suggest creating sprite groups using pygame.sprite.Group() for each class; Bomb and Player. Then, use pygame.sprite.spritecollide().
For example:
def Main()
...
player_list = pygame.sprite.Group()
bomb_list = pygame.sprite.Group()
...
Then in your logic handling loop, after creating a Player and Bomb instance, you could do something like this:
for bomb in bomb_list:
# See if the bombs has collided with the player.
bomb_hit_list = pygame.sprite.spritecollide(bomb, player_list, True)
# For each bomb hit, remove bomb
for bomb in bomb_hit_list:
bomb_list.remove(bomb)
# --- Put code here that will trigger with each collision ---