Need some assistance needed with detecting multiple collisions - python

I created a simple program consisting of bouncy balls that bounce off the walls and are supposed to turn green upon collision with another ball. However when I add more than 2 balls to the screen, the collision detections falls apart: some balls that collide with one another don't turn green or of the balls that do collide, only one of them turns green. id appreciate it if anybody can look through my code and tell me what the issue is
I detect collisions between balls in the update method of the Ball class.
Here is the Ball class
class Ball:
def __init__(self,x,y,radius,speed,color=RED):
self.x=x
self.y=y
self.color=color
self.speed=speed
self.radius=radius
def update(self):
for i in range(0,len(ball)):
for j in range(0,len(ball)):
if i==j:
continue
elif is_collision(ball[i],ball[j]):
ball[i].color=GREEN
ball[j].color=GREEN
else:
ball[i].color=RED
ball[j].color=RED
def draw(self):
pygame.draw.circle(screen,self.color(self.x,self.y),radius)
This is the rest of the code outside of the class
def is_collision(ball1, ball2):
distance = math.hypot(ball2.x - ball1.x, ball2.y - ball1.y)
if distance<=ball1.radius+ball2.radius:
return True
else:
return False
for i in range(number_balls): # adds balls to list
rX=random.randint(radius,WIDTH-radius)
rY=random.randint(radius,HEIGHT-radius)
dx=random.randint(1,10)
dy=random.randint(1,10)
rC1=random.randint(0,255)
rC2=random.randint(0,255)
rC3=random.randint(0,255)
rColor=(rC1,rC2,rC3)
ball.append(Ball(rX,rY,25,[dx,dy]))
while True: # main loop
for events in pygame.event.get():
if events.type==pygame.QUIT:
sys.exit()
for x in ball:
x.x+=x.speed[0]
x.y+=x.speed[1]
if x.x-x.radius<0 or x.x+x.radius>WIDTH:
x.speed[0]=-x.speed[0]
if x.y-x.radius<0 or x.y+x.radius>HEIGHT:
x.speed[1]=-x.speed[1]
x.draw()
x.update()
pygame.display.update()
screen.fill(BLACK)
Assistance would be greatly appreciated

Related

Implementing a collision detect feature between a Rect object and a ball in pygame [duplicate]

This question already has an answer here:
Sometimes the ball doesn't bounce off the paddle in pong game
(1 answer)
Closed 2 years ago.
import pygame, random, time
# main function where we call all other functions, start the game loop, quit pygame and clean up the window. Inside we create a game object, display surface, and start the game loop by calling the play method on the game object. There is also a set caption with the title of the game.
def main():
pygame.init()
size =(500,400)
surface=pygame.display.set_mode(size)
pygame.display.set_caption('Pong v2')
game = Game(surface)
game.play()
pygame.quit()
# This is where we define the class game
class Game():
# an object in this class represents a complete game
# here we initialize a game. self is the game that is initialized surface is the display window surface object we also set default values for continuing the game and closing the window. we also define what fps we are running the game at, and define the velocity color position and radius of the ball
def __init__(self,surface):
# defining surface, fps, background color
self.surface=surface
self.FPS=120
self.bg_color=pygame.Color('black')
screen_width = surface.get_width()
screen_height = surface.get_height()
# defining ball attributes
ball_radius=10
ball_pos = [random.randint(ball_radius, screen_width-ball_radius),
random.randint(ball_radius, screen_height-ball_radius)]
ball_color=pygame.Color('white')
ball_velocity=[2,1]
self.ball=Ball(ball_pos,ball_radius,ball_color,ball_velocity,surface)
# defining paddle attributes
rect_left=[50,450]
rect_top=225
rect_height=60
rect_width=10
self.Paddle1=Rect(rect_left[0],rect_top,rect_width,rect_height,surface)
self.Paddle2=Rect(rect_left[1],rect_top,rect_width,rect_height,surface)
self.game_Clock=pygame.time.Clock()
self.close_clicked=False
self.continue_game=True
self.score1=0
self.score2=0
self.frame_counter=0
def play(self):
# game is played until player clicks close
while not self.close_clicked:
self.handle_events()
self.draw()
# if nothing sets this to false the game will continue to update
if self.continue_game:
self.update()
self.game_Clock.tick(self.FPS)
# score is drawn onto the screen (unimportant this is just playing with a feature for the next version), we define color font background etc of the score message and update score upon points being scored
def draw_score(self):
font_color = pygame.Color("white")
font_bg = pygame.Color("black")
font = pygame.font.SysFont("arial", 18)
text_img = font.render("Score for Player 1: " + str(self.score1) + ' Score for Player 2: ' + str(self.score2), True, font_color, font_bg)
text_pos = (0,0)
self.surface.blit(text_img, text_pos)
# ball, surface, score, and two paddles are drawn, pygame also updates this drawing once per frame
def draw(self):
self.surface.fill(self.bg_color)
self.draw_score()
#pygame.draw.rect(self.surface,pygame.Color('blue'),(50,225,10,50))
#pygame.draw.rect(self.surface,pygame.Color('red'),(450,225,10,50))
self.Paddle1.draw()
self.Paddle2.draw()
self.ball.draw()
pygame.display.update()
# score value set to default of 0 we tell ball to move and add 1 to frame counter upon each update. update game object for the next frame
def update(self):
self.ball.move()
self.score=0
self.frame_counter+=self.frame_counter+1
# here we setup an event loop and figure out if the action to end the game has been done
def handle_events(self):
events=pygame.event.get()
for event in events:
if event.type== pygame.QUIT:
self.close_clicked=True
# user defined class ball
class Ball:
# self is the ball to intialize. color/center/radius are defined for the ball that is initialized
def __init__(self,center,radius,color,velocity,surface):
self.center=center
self.radius=radius
self.color=color
self.velocity=velocity
self.surface=surface
# screen size is determined and edge of ball is checked that it is not touching the edge. if it is touching the edge it bounces and reverses velocity
def move(self):
screen_width=self.surface.get_width()
screen_height=self.surface.get_height()
screen_size=(screen_width,screen_height)
for i in range(0,len(self.center)):
self.center[i]+=self.velocity[i]
if (self.center[i]<=0 + self.radius or self.center[i]>=screen_size[i] - self.radius):
self.velocity[i]=-self.velocity[i]
# ball is drawn
def draw(self):
pygame.draw.circle(self.surface,self.color,self.center,self.radius)
class Rect:
def __init__(self,left,top,width,height,surface):
#self.left=left
#self.top=top
#self.width=width
#self.height=height
self.surface=surface
self.rect=pygame.Rect(left,top,width,height)
def draw(self):
pygame.draw.rect(self.surface,pygame.Color('red'),self.rect)
def collide(self):
if pygame.Rect.collide(x,y) == True:
return True
else:
return False
main()
Above is my work so far basically its supposed to be the retro arcade game pong where the ball bounces off of the edges of the paddles and if it doesn't the opposite side scores a point upon it hitting the edge of the window. So specifically this part of the project requires me to make the ball bounce off of the front of the paddles and I'm confused as to how to do this. My idea originally was to use the collidepoint method inside of the class Rect that if it returns true would reverse the balls velocity. However, I don't have access to the centre coordinates of the ball inside of the class or inside of the method play in the class game where I intended to make this work on the specific instances of ball and paddle1,paddle2 so I don't know how to do this.
Evaluate if the ball hits the left paddle at the right, respectively the right paddle at the left in Game.update.
If the ball hits the paddle the the score can be incremented:
class Game():
# [...]
def update(self):
self.ball.move()
# evaluate if Ball hits the left paddle (Paddle1) at the right
if self.ball.velocity[0] < 0 and self.Paddle1.rect.top <= self.ball.center[1] <= self.Paddle1.rect.bottom:
if self.Paddle1.rect.right <= self.ball.center[0] <= self.Paddle1.rect.right + self.ball.radius:
self.ball.velocity[0] = -self.ball.velocity[0]
self.ball.center[0] = self.Paddle1.rect.right + self.ball.radius
self.score1 += 1
# evaluate if Ball hits the right paddle (Paddle2) at the left
if self.ball.velocity[0] > 0 and self.Paddle2.rect.top <= self.ball.center[1] <= self.Paddle2.rect.bottom:
if self.Paddle2.rect.left >= self.ball.center[0] >= self.Paddle2.rect.left - self.ball.radius:
self.ball.velocity[0] = -self.ball.velocity[0]
self.ball.center[0] = self.Paddle2.rect.left - self.ball.radius
self.score2 += 1
Get the state of the keys by pygame.key.get_pressed() and change the position of the paddles is Game.handle_events.
e.g. Move the left paddle by w / s and the right paddle by UP / DOWN:
class Game():
# [...]
def handle_events(self):
events=pygame.event.get()
for event in events:
if event.type== pygame.QUIT:
self.close_clicked=True
keys = pygame.key.get_pressed()
if keys[pygame.K_w]:
self.Paddle1.rect.top = max(0, self.Paddle1.rect.top - 3)
if keys[pygame.K_s]:
self.Paddle1.rect.bottom = min(self.surface.get_height(), self.Paddle1.rect.bottom + 3)
if keys[pygame.K_UP]:
self.Paddle2.rect.top = max(0, self.Paddle2.rect.top - 3)
if keys[pygame.K_DOWN]:
self.Paddle2.rect.bottom = min(self.surface.get_height(), self.Paddle2.rect.bottom + 3)

Trouble with classes in pygame

I've been working on a game using pygame. So far I have got the shooting to work.
Here is the code:
def game_loop():
global x
global y
x=0
y=0
second_x=0
second_y=0
change_x=0
change_y=0
xlist=[]
ylist=[]
# paused=True
while True:
game_display.blit(background, (0, 0))
mouseclk=pygame.mouse.get_pressed()
for event in pygame.event.get():
if event.type==pygame.QUIT:
pygame.quit()
quit()
x, y = pygame.mouse.get_pos()
xlist.append(x)
ylist.append(y)
if x>=display_width-40:
x=display_width-40
if y>=display_height-48:
y=display_height-48
if event.type == pygame.MOUSEBUTTONUP :
if event.button==1:
bullets.append([x+16,y])
shoot.play()
for bullet in bullets:
game_display.blit(bulletpic, (bullet[0], bullet[1]+10))
for b in range(len(bullets)):
bullets[b][-1] -= 10
for bullet in bullets:
if bullet[1]>display_height:
bullets.remove(bullet)
mainship=Ship(spaceship, x, y, pygame.mouse.get_focused())
mainship.MakeShip()
if pygame.mouse.get_focused()==0:
pause()
pygame.display.update()
clock.tick(60)
The shooting works fine but i really want to use classes in my game. I tried making a class like this:
class Bullet:
def __init__(self, xpos, ypos, sprite, speed, llist):
self.xpos=xpos
self.ypos=ypos
self.sprite=sprite
self.speed=speed
self.list=llist
self.list.append([self.xpos+16,self.ypos])
for bullet in self.list:
game_display.blit(self.sprite, (bullet[0], bullet[1]+10))
for b in range(len(self.list)):
self.list[b][-1] -= self.speed
for bullet in self.list:
if bullet[1]>display_height:
self.list.remove(bullet)
Now, even though this looks like it would work, it just makes the bullets glitchy and slow and sometimes the bullets won't even spawn properly.
Is there any way i can make this work? I'm a noob programmer and new to classes in general so any help will be appreciated.
I think your Bullet does far too much. You could model bullets like so:
class Bullet:
"""This is a bullet. It knows about its position, direction and speed.
It can move. It knows when it crosses the classvariables bounds."""
# bounds as class variable for _all_ bullets - set it to dimensions of your game field
bounds = (0,0,10,10) # if position is lower or higher that these it is out of bounds
# if you got only 1 spite for all bullets (no acid-, fire-, waterbullets) you could
# set it here. I would not, I would leave bullets simply to do the movement and tell
# me if they get out of bounds
def __init__(self, xpos, ypos, xdir, ydir, speed):
# where does the bullet start (f.e. muzzle position)
self.xpos=xpos
self.ypos=ypos
# direction that the bullet flies
self.xdir=xdir
self.ydir=ydir
# initial speed of the bullet
self.speed=speed
def moveBullet(self):
self.xpos += int(self.xdir * self.speed) # speed may be 0.8 or so
self.ypos += int(self.ydir * self.speed)
def outOfBounds(self):
minX,minY,maxX,maxY = Bullet.bounds
return self.xpos < minX or self.ypos < minY or self.xpos > maxX or self.ypos > maxY
def __repr__(self):
"""Pretty print stuff"""
return "({},{}){}".format(self.xpos,self.ypos,"" if
not self.outOfBounds()
else " : Out Of Bounds {}".format(Bullet.bounds))
Your game then sets the bounds of all bullets and keeps track of a list of bullets. If you need a new bullet, simply place it into the list
Bullet.bounds = (0,0,90,90)
bullets = [ Bullet(10,10,12,0,3), Bullet(10,10,11,9,3), Bullet(10,10,0,5,1)]
while bullets:
# you should move this into some kind of `def moveBullets(bulletlist): ...`
# that you then call inside your game loop
for b in bullets:
b.moveBullet()
print(b) # instead draw a spirte at b.xpos, b.ypos
print("")
# return from moveBullets(..)
# remove all out of bounds bullets from list
bullets = filter(lambda b: not b.outOfBounds(),bullets)
Output:
(46,10)
(43,37)
(10,15)
(82,10)
(76,64)
(10,20)
(118,10) : Out Of Bounds (0, 0, 90, 90)
(109,91) : Out Of Bounds (0, 0, 90, 90)
(10,25)
(10,30)
(10,35)
(10,40)
(10,45)
(10,50)
(10,55)
(10,60)
(10,65)
(10,70)
(10,75)
(10,80)
(10,85)
(10,90)
(10,95) : Out Of Bounds (0, 0, 90, 90)

Shooting bullets in pygame

I'm making my own space invaders game and so far I've been able to move my ship around using the mouse. However, I still can't shoot. Here's my game loop.
def game_loop():
x=0
y=0
xlist=[]
ylist=[]
while True:
mouseclk=pygame.mouse.get_pressed()
game_display.fill(white)
for event in pygame.event.get():
if event.type==pygame.QUIT:
pygame.quit()
quit()
x, y = pygame.mouse.get_pos()
xlist.append(x)
ylist.append(y)
if x>=display_width-40:
x=display_width-40
if y>=display_height-48:
y=display_height-48
if pygame.mouse.get_focused()==0:
game_display.blit(spaceship, (x, y))
elif pygame.mouse.get_focused()==1:
game_display.blit(spaceshipflames, (x, y))
pygame.display.update()
if pygame.mouse.get_focused()==0:
pause()
clock.tick(500)
I've tried using the following code inside my game loop:
if mouseclk[0]==1:
shoot.play()
while True:
pygame.draw.circle(game_display, white, (x+20, y-2), 5)
pygame.draw.circle(game_display, red, (x+20, y-7), 5)
y-=5
if y<=0:
break
pygame.display.update()
clock.tick(400)
But the end result is very glitchy and doesn't allow me to shoot multiple bullets without making the game choppy.
Is there a way to run both loops at the same time, or a completely different way to implement shooting?
I'd recommend making use of classes (especially for games) and splitting up your code into smaller functions.
When making a game, each class should represent some type of object in the game, for example a ship or a bullet here. Using classes should help with this problem of multiple bullets causes glitches.
Breaking into smaller functions will make your code much easier to read and update as it grows. Try to stick to the Single Responsibility Principle as much as possible.
How you might implement shooting with these things in mind:
bullets = []
class Bullet:
def __init__(self, position, speed):
self.position = position
self.speed = speed
def move(self):
self.position[1] += self.speed
class Ship:
def __init__(self, position, bullet_speed):
self.position = position
self.bullet_speed = bullet_speed
def shoot(self):
new_bullet = Bullet(self.position, self.bullet_speed)
bullets.append(new_bullet)
Where the position variables have the form [x,y]. Then to move your bullets forward, put this line somewhere in your main game loop:
for bullet in bullets:
bullet.move()
And loop over all the bullets and draw each to the screen to render them.
This isn't the most detailed example, but hopefully it's enough to get you going in the right direction.

Pygame Sprite flickers when moving

I am new to pygame and have been having some problems with a sprite flickering/dragging it's image when it moves across the screen. I have read other answers on here about getting a certain pix/s, where you should change the framerate and lower pix/frame, but that solution hasn't helped so I was wondering if I'm just not implementing it correctly and that's the only solution or if I am missing something. Below is some the code for the game that runs at a constant framerate. Thank you!
pygame.init()
display=pygame.display.set_mode((screen_width,screen_height), pygame.RESIZABLE)
background=pygame.Surface((screen_width, screen_height))
background.fill(background_color)
class Ship(pygame.sprite.Sprite):
def __init__(self, image_file):
pygame.sprite.Sprite.__init__(self)
self.original_image_unscaled=pygame.image.load(image_file)
self.original_image=pygame.transform.smoothscale(self.original_image_unscaled, (56,70))
self.image=self.original_image
self.rect=self.image.get_rect()
self.xpos=0
self.ypos=0
self.rect.centerx=self.xpos
self.rect.centery=self.ypos
self.vel=0
self.acc=.05
self.brake=.2
self.turn_brake=1.5
self.angle=0
self.max_speed=5
def getShipPos(self):
return (self.xpos, self.ypos)
def getShipVel(self):
return self.vel
def getShipAcc(self):
return self.acc
def getShipRect(self):
return self.rect
def update(self):
self.rect.centerx=self.xpos
self.rect.centery=self.ypos
def rotateShip(self, angle):
#this rotates the ship by a specified angle
self.image, self.rect = pygameRotateImage2(self.original_image, self.rect, angle-self.angle)
self.angle+=angle
def simpleMove(self, x_final, y_final):
self.xpos=x_final
self.ypos=y_final
def moveShip(self):
keys=pygame.key.get_pressed()
xpos_i=self.xpos
ypos_i=self.ypos
if keys[K_w]:
if self.vel>self.max_speed:
self.vel=self.max_speed
else:
self.vel+=self.acc
elif keys[K_s]:
self.vel-=self.brake
if self.vel<0:
self.vel=0
if keys[K_a]:
self.rotateShip(-self.turn_brake)
elif keys[K_d]:
self.rotateShip(self.turn_brake)
self.xpos+=self.vel*math.sin(math.radians(self.angle))
self.ypos+= -self.vel*math.cos(math.radians(self.angle))
display.blit(background, (0,0))
pygame.display.update()
player_ship=Ship('image.png')
player_ship.simpleMove(screen_width/2, screen_height/2)
movement_group=pygame.sprite.Group()
movement_group.add(player_ship)
clock=pygame.time.Clock()
while True:
clock.tick(60)
(mousex,mousey)=pygame.mouse.get_pos()
for event in pygame.event.get():
if event.type==QUIT:
pygame.quit()
sys.exit()
player_ship.moveShip()
movement_group.update()
display.blit(background, (0,0))
movement_group.draw(display)
pygame.display.update()
Double buffering will probably help at least some. It is a technique used in computer graphics to reduce the number of times a pixel changes. It can be implemented as:
pygame.display.set_mode(dimensions, pygame.RESIZEABLE|pygame.DOUBLEBUF)
Another option is to move the clock.tick from the beginning of the loop to the end of the loop. That is where I usually see it, and it could alter the outcome of the graphics.
Try changing the line pygame.display.update() to pygame.display.flip().
I had the same problem in the past.
I now usually create a method in the classes that need to be displayed on screen which blit the picture (of the class itself) to the screen.
I than call that method before the screen update.
For example, this is a method of the class "Tank", which actually build the tank used by the player:
def master_player(self): #calculate and draw the player sprite
self.posix() # call of a method which calculate the position
self.image_tank.set_colorkey(PURPLE)
screen.blit(player.image_tank, player.position_tuple)
As I said before, I call this method at the end of the game loop and before the screen update.
Try moving the player_ship.moveShip() right under the pygame.display.update()
Like this:
player_ship.moveShip()
movement_group.update()
display.blit(background, (0,0))
movement_group.draw(display)
pygame.display.update()

Understand object orientated python - initialization of objects in pygame

I'm learning Object Orientated Python and understand the main principals of classes and creating objects from classes however I need something explained Re: the pygame code below. I'm struggling to get my head around what's happening when sprite lists are being created and the two lines of code under the code which creates the ball object (allsprites.add etc). In other words what are sprites and why are lists of them created? Why isn't the ball object just created from the class on its own? Why does it need to be added to a sprite list?? What's going on? Any explanation would be greatly appreciated.
"""
Sample Breakout Game
Sample Python/Pygame Programs
Simpson College Computer Science
http://programarcadegames.com/
http://simpson.edu/computer-science/
"""
# --- Import libraries used for this program
import math
import pygame
# Define some colors
black = (0, 0, 0)
white = (255, 255, 255)
blue = (0, 0, 255)
# Size of break-out blocks
block_width = 23
block_height = 15
class Block(pygame.sprite.Sprite):
"""This class represents each block that will get knocked out by the ball
It derives from the "Sprite" class in Pygame """
def __init__(self, color, x, y):
""" Constructor. Pass in the color of the block,
and its x and y position. """
# Call the parent class (Sprite) constructor
pygame.sprite.Sprite.__init__(self)
# Create the image of the block of appropriate size
# The width and height are sent as a list for the first parameter.
self.image = pygame.Surface([block_width, block_height])
# Fill the image with the appropriate color
self.image.fill(color)
# Fetch the rectangle object that has the dimensions of the image
self.rect = self.image.get_rect()
# Move the top left of the rectangle to x,y.
# This is where our block will appear..
self.rect.x = x
self.rect.y = y
class Ball(pygame.sprite.Sprite):
""" This class represents the ball
It derives from the "Sprite" class in Pygame """
# Speed in pixels per cycle
speed = 10.0
# Floating point representation of where the ball is
x = 0.0
y = 180.0
# Direction of ball (in degrees)
direction = 200
width = 10
height = 10
# Constructor. Pass in the color of the block, and its x and y position
def __init__(self):
# Call the parent class (Sprite) constructor
pygame.sprite.Sprite.__init__(self)
# Create the image of the ball
self.image = pygame.Surface([self.width, self.height])
# Color the ball
self.image.fill(white)
# Get a rectangle object that shows where our image is
self.rect = self.image.get_rect()
# Get attributes for the height/width of the screen
self.screenheight = pygame.display.get_surface().get_height()
self.screenwidth = pygame.display.get_surface().get_width()
def bounce(self, diff):
""" This function will bounce the ball
off a horizontal surface (not a vertical one) """
self.direction = (180 - self.direction) % 360
self.direction -= diff
def update(self):
""" Update the position of the ball. """
# Sine and Cosine work in degrees, so we have to convert them
direction_radians = math.radians(self.direction)
# Change the position (x and y) according to the speed and direction
self.x += self.speed * math.sin(direction_radians)
self.y -= self.speed * math.cos(direction_radians)
# Move the image to where our x and y are
self.rect.x = self.x
self.rect.y = self.y
# Do we bounce off the top of the screen?
if self.y <= 0:
self.bounce(0)
self.y = 1
# Do we bounce off the left of the screen?
if self.x <= 0:
self.direction = (360 - self.direction) % 360
self.x = 1
# Do we bounce of the right side of the screen?
if self.x > self.screenwidth - self.width:
self.direction = (360 - self.direction) % 360
self.x = self.screenwidth - self.width - 1
# Did we fall off the bottom edge of the screen?
if self.y > 600:
return True
else:
return False
class Player(pygame.sprite.Sprite):
""" This class represents the bar at the bottom that the player controls. """
def __init__(self):
""" Constructor for Player. """
# Call the parent's constructor
pygame.sprite.Sprite.__init__(self)
self.width = 75
self.height = 15
self.image = pygame.Surface([self.width, self.height])
self.image.fill((white))
# Make our top-left corner the passed-in location.
self.rect = self.image.get_rect()
self.screenheight = pygame.display.get_surface().get_height()
self.screenwidth = pygame.display.get_surface().get_width()
self.rect.x = 0
self.rect.y = self.screenheight-self.height
def update(self):
""" Update the player position. """
# Get where the mouse is
pos = pygame.mouse.get_pos()
# Set the left side of the player bar to the mouse position
self.rect.x = pos[0]
# Make sure we don't push the player paddle
# off the right side of the screen
if self.rect.x > self.screenwidth - self.width:
self.rect.x = self.screenwidth - self.width
# Call this function so the Pygame library can initialize itself
pygame.init()
# Create an 800x600 sized screen
screen = pygame.display.set_mode([800, 600])
# Set the title of the window
pygame.display.set_caption('Breakout')
# Enable this to make the mouse disappear when over our window
pygame.mouse.set_visible(0)
# This is a font we use to draw text on the screen (size 36)
font = pygame.font.Font(None, 36)
# Create a surface we can draw on
background = pygame.Surface(screen.get_size())
# Create sprite lists
blocks = pygame.sprite.Group()
balls = pygame.sprite.Group()
allsprites = pygame.sprite.Group()
# Create the player paddle object
player = Player()
allsprites.add(player)
# Create the ball
ball = Ball()
allsprites.add(ball)
balls.add(ball)
# The top of the block (y position)
top = 80
# Number of blocks to create
blockcount = 32
# --- Create blocks
# Five rows of blocks
for row in range(5):
# 32 columns of blocks
for column in range(0, blockcount):
# Create a block (color,x,y)
block = Block(blue, column * (block_width + 2) + 1, top)
blocks.add(block)
allsprites.add(block)
# Move the top of the next row down
top += block_height + 2
# Clock to limit speed
clock = pygame.time.Clock()
# Is the game over?
game_over = False
# Exit the program?
exit_program = False
# Main program loop
while exit_program != True:
# Limit to 30 fps
clock.tick(30)
# Clear the screen
screen.fill(black)
# Process the events in the game
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit_program = True
# Update the ball and player position as long
# as the game is not over.
if not game_over:
# Update the player and ball positions
player.update()
game_over = ball.update()
# If we are done, print game over
if game_over:
text = font.render("Game Over", True, white)
textpos = text.get_rect(centerx=background.get_width()/2)
textpos.top = 300
screen.blit(text, textpos)
# See if the ball hits the player paddle
if pygame.sprite.spritecollide(player, balls, False):
# The 'diff' lets you try to bounce the ball left or right
# depending where on the paddle you hit it
diff = (player.rect.x + player.width/2) - (ball.rect.x+ball.width/2)
# Set the ball's y position in case
# we hit the ball on the edge of the paddle
ball.rect.y = screen.get_height() - player.rect.height - ball.rect.height - 1
ball.bounce(diff)
# Check for collisions between the ball and the blocks
deadblocks = pygame.sprite.spritecollide(ball, blocks, True)
# If we actually hit a block, bounce the ball
if len(deadblocks) > 0:
ball.bounce(0)
# Game ends if all the blocks are gone
if len(blocks) == 0:
game_over = True
# Draw Everything
allsprites.draw(screen)
# Flip the screen and show what we've drawn
pygame.display.flip()
pygame.quit()
You don't need to add the balls and blocks to sprite lists - it's just a matter of convenience. You could manually check each ball for a collision, but it's easier to just tell pygame to check them all for you
# See if the ball hits the player paddle
if pygame.sprite.spritecollide(player, balls, False):
# The 'diff' lets you try to bounce the ball left or right
# depending where on the paddle you hit it
diff = (player.rect.x + player.width/2) - (ball.rect.x+ball.width/2)
# Set the ball's y position in case
# we hit the ball on the edge of the paddle
ball.rect.y = screen.get_height() - player.rect.height - ball.rect.height - 1
ball.bounce(diff)
You could draw each thing to the screen separately on each frame, but it's easier just to tell pygame to do it for you:
# Draw Everything
allsprites.draw(screen)
Things can be in more than one list as required, for example a ball is added to the balls list so that you can easily check for collisions, but also added to the allsprites list so that you can easily draw everything on the screen
# Create the ball
ball = Ball()
allsprites.add(ball)
balls.add(ball)
Edit:
An important distinction is that allsprites is actually a sprite.Group. It has a list of sprites inside it, but it also has other methods like draw.
To address your question of "what is a Sprite", it's simply a thing that gets drawn on screen. pygame methods like sprite.Group.draw expect a list of things with certain attributes - such as update. The easiest way to make sure that you provide all of those attributes with the right names is to subclass Sprite, however this is also a (strongly recommended) convenience thing - for instance, this is from the pygame source code:
While it is possible to design sprite and group classes that don't
derive from the Sprite and AbstractGroup classes below, it is strongly
recommended that you extend those when you add a Sprite or Group
class.
So what specifically does subclassing Sprite get you? Let's take a look at the source. Here's how to find the source code for a python module:
>>> import pygame.sprite
>>> pygame.sprite.__file__
'c:\\Python27\\lib\\site-packages\\pygame\\sprite.py'
>>>
Every python module has a __file__ attribute that tells you where the source is located (well not quite every). If you open it up in your editor, and scroll down, you see the class definition for Sprite:
class Sprite(object):
"""simple base class for visible game objects
pygame.sprite.Sprite(*groups): return Sprite
The base class for visible game objects. Derived classes will want to
override the Sprite.update() and assign a Sprite.image and
Sprite.rect attributes. The initializer can accept any number of
Group instances to be added to.
When subclassing the Sprite, be sure to call the base initializer before
adding the Sprite to Groups.
"""
def __init__(self, *groups):
self.__g = {} # The groups the sprite is in
if groups: self.add(groups)
def add(self, *groups):
"""add the sprite to groups
Sprite.add(*groups): return None
Any number of Group instances can be passed as arguments. The
Sprite will be added to the Groups it is not already a member of.
"""
has = self.__g.__contains__
for group in groups:
if hasattr(group, '_spritegroup'):
if not has(group):
group.add_internal(self)
self.add_internal(group)
else: self.add(*group)
def remove(self, *groups):
"""remove the sprite from groups
Sprite.remove(*groups): return None
Any number of Group instances can be passed as arguments. The Sprite will
be removed from the Groups it is currently a member of.
"""
has = self.__g.__contains__
for group in groups:
if hasattr(group, '_spritegroup'):
if has(group):
group.remove_internal(self)
self.remove_internal(group)
else: self.remove(*group)
def add_internal(self, group):
self.__g[group] = 0
def remove_internal(self, group):
del self.__g[group]
def update(self, *args):
"""method to control sprite behavior
Sprite.update(*args):
The default implementation of this method does nothing; it's just a
convenient "hook" that you can override. This method is called by
Group.update() with whatever arguments you give it.
There is no need to use this method if not using the convenience
method by the same name in the Group class.
"""
pass
def kill(self):
"""remove the Sprite from all Groups
Sprite.kill(): return None
The Sprite is removed from all the Groups that contain it. This won't
change anything about the state of the Sprite. It is possible to continue
to use the Sprite after this method has been called, including adding it
to Groups.
"""
for c in self.__g.keys():
c.remove_internal(self)
self.__g.clear()
def groups(self):
"""list of Groups that contain this Sprite
Sprite.groups(): return group_list
Return a list of all the Groups that contain this Sprite.
"""
return self.__g.keys()
def alive(self):
"""does the sprite belong to any groups
Sprite.alive(): return bool
Returns True when the Sprite belongs to one or more Groups.
"""
return (len(self.__g) != 0)
def __repr__(self):
return "<%s sprite(in %d groups)>" % (self.__class__.__name__, len(self.__g))
So in summary, you don't have to subclass Sprite - you could just provide all of these methods on your own - but it's easier if you do ;)

Categories

Resources