This question already has answers here:
Pygame doesn't let me use float for rect.move, but I need it
(2 answers)
Closed 1 year ago.
I have written two versions of the same bouncing ball game. One is OOP based and one is procedural, and I would expect them to do the same thing. Except, the Object Oriented program behaves differently.
I don't know the best way to explain it but the procedural code 'bouncing' keeps the ball bouncing indefinitely and bouncing to the same height each time. But the OOP code 'bouncing' increases the bounce height on each consecutive bounce. Yet I cannot find a difference in the logic between them.
OOP code
import pygame, time
class Ball(pygame.sprite.Sprite):
def __init__(self, colour, radius):
super().__init__()
self.image = pygame.Surface([radius*2, radius*2])
self.colour = colour
self.radius = radius
pygame.draw.circle(self.image, self.colour, (radius, radius), self.radius)
self.rect = self.image.get_rect()
self.rect.x = 350
self.rect.y = 350
self.change_y = 0.5
self.vel_y = 0
def update(self):
self.vel_y += self.change_y
self.rect.y += self.vel_y
def bounce(self):
self.vel_y = self.vel_y * -1
self.rect.y += self.vel_y
def play_game():
all_sprites_list = pygame.sprite.Group()
ball_list = pygame.sprite.Group()
ball = Ball(WHITE, 10)
ball_list.add(ball)
all_sprites_list.add(ball)
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
screen.fill(BLACK)
all_sprites_list.draw(screen)
for ball in ball_list:
if ball.rect.y > 690:
ball.bounce()
else:
ball.update()
pygame.display.update()
clock.tick(60)
BLACK = (0,0,0)
WHITE = (255,255,255)
RED = (255,0,0)
BLUE = (0,255,0)
GREEN = (0,0,255)
pygame.init()
screen_width = 700
screen_height = 700
screen = pygame.display.set_mode([screen_width, screen_height])
clock = pygame.time.Clock()
play_game()
pygame.quit()
Procedural code
import pygame
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
BROWN = (200, 100, 0)
pygame.init()
size = (700, 700)
screen = pygame.display.set_mode(size)
done = False
clock = pygame.time.Clock()
rect_x = 350
rect_y = 350
rect_changey = 0
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
screen.fill(BLACK)
pygame.draw.circle(screen, WHITE, [rect_x, rect_y], 10)
if (rect_y > 690):
rect_changey = rect_changey* -1
rect_y += rect_changey
else:
rect_changey = rect_changey + 0.5
rect_y += rect_changey
pygame.display.flip()
clock.tick(60)
pygame.quit()
Update: The ball.update() function is running 1 more time than the equivalent part of the code in the procedural code. Still dont know why though
rect_x and rect_y can store floating point values. However rect.x and rect.y cannot just store integral values.
Since pygame.Rect is supposed to represent an area on the screen, a pygame.Rect object can only store integral data.
The coordinates for Rect objects are all integers. [...]
The fraction part of the coordinates gets lost when the new position of the object is assigned to the Rect object. If this is done every frame, the position error will accumulate over time.
If you want to store object positions with floating point accuracy, you have to store the location of the object in separate variables respectively attributes and to synchronize the pygame.Rect object. round the coordinates and assign it to the location (e.g. .topleft) of the rectangle:
x, y = # floating point coordinates
rect.topleft = round(x), round(y)
Ball class:
class Ball(pygame.sprite.Sprite):
def __init__(self, colour, radius):
super().__init__()
self.image = pygame.Surface([radius*2, radius*2])
self.colour = colour
self.radius = radius
pygame.draw.circle(self.image, self.colour, (radius, radius), self.radius)
self.rect = self.image.get_rect()
self.x = 350
self.y = 350
self.rect.x = self.x
self.rect.y = self.y
self.change_y = 0.5
self.vel_y = 0
def update(self):
self.vel_y += self.change_y
self.y += self.vel_y
self.rect.topleft = round(self.x), round(self.y)
def bounce(self):
self.vel_y = self.vel_y * -1
self.y += self.vel_y
self.rect.topleft = round(self.x), round(self.y)
Related
I am creating an Asteroids type game using Pygame and am having trouble firing projectiles. I have a sprite group for the projectile object and some code to add the projectile.
if pressed[pygame.K_SPACE]:
projectiles.add(Projectile(player.rect.x, player.rect.y, player.direction))
Problem is when I press space in game to fire the projectile I get the error "add() argument after * must be an iterable, not int".
I have a very similar statement for adding asteroids when the game first starts without any issues so I'm not really sure what the problem is. I'll leave the rest of the code below. The add statement giving issues is in the main function near the bottom. Any help is appreciated.
#Import Modules
import pygame
import math
import random
#Movement Function
def calculate_new_xy(old_xy,speed,direction):
new_x = old_xy[0] + (speed*math.cos(direction))
new_y = old_xy[1] + (speed*math.sin(direction))
return new_x, new_y
#Collision Function
def isColliding(x, y, xTo, yTo, size):
if x > xTo - size and x < xTo + size and y > yTo - size and y < yTo + size:
return True
return False
#Draw Text Function
def drawText(msg, color, x, y, s, center=True):
screen_text = pygame.font.SysFont("Impact", s).render(msg, True, color)
if center:
rect = screen_text.get_rect()
rect.center = (x, y-50)
else:
rect = (x, y)
display.blit(screen_text, rect)
#Initialize Variables
#Colors
white = (255, 255, 255)
black = (0, 0, 0)
#Display Height/Width
display_width = 800
display_height = 600
#Asteroid Class
class Asteroid(pygame.sprite.Sprite):
#Initialize values
def __init__(self, pos=(0, 0)):
#Initialize sprite class
pygame.sprite.Sprite.__init__(self)
#Asteroid sprite
self.asteroid = pygame.image.load("asteroid.png").convert()
self.image = self.asteroid
#Rectangle
self.rect = self.image.get_rect()
self.rect.center = pos
#Initialize random starting angle
self.angle = random.randint(0, 360)
#Asteroid random Speed
self.speed = random.randint(2, 3)
#Asteroid random direction
self.direction = math.radians(random.randrange(0, 360, 3))
#Update asteroid object
def update(self):
#Constantly rotate asteroid
self.angle -= 3 % 360
#Get image angle and position
self.image = pygame.transform.rotate(self.asteroid, self.angle*-1)
#Use rectangle to get center of image
#Save ship's current center.
x, y = self.rect.center
#Replace old rect with new rect.
self.rect = self.image.get_rect()
#Put the new rect's center at old center.
self.rect.center = (x, y)
#Move Asteroid
self.rect.center = calculate_new_xy(self.rect.center,self.speed,self.direction)
#Screen Border
#Moves the asteroid to the opposite side of the screen if they go outside the border
if self.rect.x > display_width:
self.rect.x = -20
elif self.rect.x < -20:
self.rect.x = display_width
elif self.rect.y > display_height:
self.rect.y = -20
elif self.rect.y < -20:
self.rect.y = display_height
#Projectile Class
class Projectile(pygame.sprite.Sprite):
#Initialize values
def _init_(self,x,y,direction):
self.x = x
self.y = y
self.dir = direction
self.ttl = 30
#Update projectile object
def update(self):
#Changing direction
self.x += projectilespd * math.cos(self.direction)
self.y += projectilespd * math.sin(self.direction)
#Draw projectile
pygame.draw.circle(display, white, (self.x,self.y),1)
#Screen Border
if self.x > display_width:
self.x = 0
elif self.x < 0:
self.x = display_width
elif self.y > display_height:
self.y = 0
elif self.y < 0:
self.y = display_height
self.ttl -= 1
#Player Class
class Player(pygame.sprite.Sprite):
#Initialize ship sprite, angle lines, and rectangle
def __init__(self, pos=(0, 0), size=(200, 200)):
#Player sprite
self.ship = pygame.image.load("ship.png").convert()
self.image = self.ship
#Rectangle
self.rect = self.image.get_rect()
self.rect.center = pos
#Initialize angle
self.angle = 0
#Initialize direction
self.direction = 0
#Update player object
def update(self):
#Rotation
pressed = pygame.key.get_pressed()
if pressed[pygame.K_LEFT]: self.angle -= 3 % 360
if pressed[pygame.K_RIGHT]: self.angle += 3 % 360
#Get image angle and position
self.image = pygame.transform.rotate(self.ship, self.angle*-1)
#Use rectangle to get center of image
#Save ship's current center.
x, y = self.rect.center
#Replace old rect with new rect.
self.rect = self.image.get_rect()
#Put the new rect's center at old center.
self.rect.center = (x, y)
#Convert angle to radians
self.direction = math.radians(self.angle-90)
#Increase speed if Up is pressed
if pressed[pygame.K_UP]: self.speed = 5
else: self.speed = 0
#Move Ship
self.rect.center = calculate_new_xy(self.rect.center,self.speed,self.direction)
#Screen Border
#Moves the player to the opposite side of the screen if they go outside the border
if self.rect.x > display_width:
self.rect.x = -50
elif self.rect.x < -50:
self.rect.x = display_width
elif self.rect.y > display_height:
self.rect.y = -50
elif self.rect.y < -50:
self.rect.y = display_height
#Main Function
def main(gameState):
#Player starting position
player = Player(pos=(400, 300))
#Asteroid group
asteroids = pygame.sprite.Group()
#Projectile group
projectiles = pygame.sprite.Group()
#Create asteroids
for x in range(8):
asteroids.add(Asteroid(pos=(100 + (x*120), 100 + (x*20))))
while True:
for event in pygame.event.get():
#closes game
if event.type == pygame.QUIT:
done = True
pygame.quit()
exit()
#Game Menu
while gameState == "Menu":
#Fill background
display.fill((0,0,0))
#Display menu text
drawText("ASTEROIDS", white, display_width / 2, display_height / 2, 150)
drawText("Press any key to START", white, display_width / 2, display_height / 2 + 120, 40)
#Check game start or end
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
pygame.quit()
exit()
if event.type == pygame.KEYDOWN:
gameState = "Playing"
pygame.display.update()
#Low frame rate for menu
clock.tick(5)
#Get key inputs
pressed = pygame.key.get_pressed()
#Fill background
display.fill(black)
#Check for player collision with asteroid
for asteroid in asteroids:
if gameState != "Game Over":
if isColliding(player.rect.x, player.rect.y, asteroid.rect.x, asteroid.rect.y, 30):
gameState = "Game Over"
#Update and draw player if not game over
if gameState != "Game Over":
#Update player
player.update()
#Draw player
display.blit(player.image, player.rect)
#Update asteroids
asteroids.update()
#Draw asteroids
asteroids.draw(display)
#Fire Projectiles
if pressed[pygame.K_SPACE]:
projectiles.add(Projectile(player.rect.x, player.rect.y, player.direction))
#Update projectiles
projectiles.update()
#Draw projectiles
projectiles.draw(display)
#Display Game Over and restart option
if gameState == "Game Over":
drawText("GAME OVER", white, display_width / 2, display_height / 2, 150)
drawText("Press R to restart", white, display_width / 2, display_height / 2 + 120, 40)
if pressed[pygame.K_r]:
main(gameState = "Playing")
#Makes updates to the game screen
pygame.display.update()
#Frame rate
clock.tick(60)
#Initialize Game
if __name__ == '__main__':
#initialize pygame
pygame.init()
#initialize display settings
display = pygame.display.set_mode((display_width,display_height))
pygame.display.set_caption('Asteroids')
#initialize game clock
clock = pygame.time.Clock()
#start main function
main(gameState = "Menu")
The issue is not the pygame.sprite.Group.add operation, but the obejct you want to add is not a pygame.sprite.Sprite object, because the object is not constructed at all.
You missed to the super call in the constructor of Projectile. Furthermore the name of the constructor has to be __init__ rather _init_:
class Projectile(pygame.sprite.Sprite):
#Initialize values
def __init__(self,x,y,direction):
super.__init__()
self.x = x
self.y = y
self.dir = direction
self.ttl = 30
This question already has answers here:
How do I detect collision in pygame?
(5 answers)
Closed 1 year ago.
i found this unfinished file in my files and now i need to finish it, only problem is idk really how do i detect collision with the bullet and the player thing and/or the bullet and the enemy thing, when the bullets collide with the enemy it still dies, i just don't remember how.
here's the code ig help thanks
import pygame, os, random,sys
pygame.init()
FPS=60
SCREEN = pygame.display.set_mode((400,500))
pygame.display.set_caption('caption')
x=50
y=450
vel = 3
width = 20
height = 20
class Player(pygame.sprite.Sprite):
def __init__(self,x,y,width,height):
super().__init__()
self.x = x
self.y = y
self.vel = 4
self.image = pygame.Surface((width, height))
self.image.fill((0,0,0))
self.rect = self.image.get_rect(topleft = (x, y))
class B(pygame.sprite.Sprite):
def __init__(self,x,y,radius, color):
super().__init__()
self.x = x
self.y = y
self.color = color
self.radius = radius
self.vel = vel
self.image = pygame.Surface((self.radius * 2, self.radius * 2), pygame.SRCALPHA)
pygame.draw.circle(self.image, self.color, (self.radius, self.radius), self.radius)
self.rect = self.image.get_rect(center = (self.x, self.y))
class Enemy(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((20,20))
self.image.fill((255, 0, 0))
y = random.randrange (0, 480)
x = 400
self.rect = self.image.get_rect(topleft = (x, y))
self.speed = random.randrange(1,2)
player = Player(x, y, width, height)
enemies = pygame.sprite.Group()
bullets = pygame.sprite.Group()
all_sprites = pygame.sprite.Group()
all_sprites.add(player)
score = 0
# main loop
running = True
clock = pygame.time.Clock()
while running:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE and len(bullets) < 8:
bullet = B(player.rect.centerx, player.rect.centery, 2, (0,0,0))
bullets.add(bullet)
all_sprites.add(bullet)
if len(enemies) < 2:
e = Enemy()
enemies.add(e)
all_sprites.add(e)
for bullet in bullets:
if bullet.rect.right < 500:
bullet.rect.x += bullet.vel
else:
bullet.kill()
for enemy in enemies:
if enemy.rect.right > 0:
enemy.rect.x -= enemy.speed
else:
enemy.kill()
print ("L")
pygame.quit()
sys.exit()
pygame.sprite.groupcollide(bullets, enemies, True, True)
keys = pygame.key.get_pressed()
if keys[pygame.K_UP] and player.rect.top > player.vel:
player.rect.y -= player.vel
if keys[pygame.K_DOWN] and player.rect.bottom < 500 - player.vel:
player.rect.y += player.vel
SCREEN.fill((190, 232, 220))
all_sprites.draw(SCREEN)
pygame.display.update()
pygame.quit()
Collision detection depends on your needs.
Bounding boxes are simple and can detect if the x, y edges of each object are not within one another. This is fine for fast moving games and things where you don't necessarily require point precision.
Distance vectors are also simple and perfect solution for circle collisions. They calculate the difference in distance between two objects according to a radius prescribed with the hypotenuse of distX^2 + distY^2.
Compound bounding objects are harder to calculate, especially for concave areas on objects of arbitrary shape. There are too many notable and novel approaches to collision detection beyond these to remark on here.
They're also increasingly complex depending on things like variability (if they're deformable objects, if they have particle seams, etc and 2d vs 3d object collision can be vastly different worlds as well. There are tons of articles, but I'll post one with implementation here
I am writing a simple invaders game. To add damage to the bases I figured I could blit a small, black surface on the base at bullet impact, and use a mask to check if the bullet was on the damage or the base, but it isn't working and I feel I am misunderstanding the mask. The first collision is detected but after that it also detects a collision but doesn't put any more damage on the base. I thought because the surface was black the base mask wouldn't include it, but it isn't working. Here is a short test to demo this. Press space (or any key) to fire a bullet at the base. I thought maybe I should generate a new mask for the base but that doesn't work. The mask collide is from the pygame sprite code on github.
import sys, pygame, random
from pygame.locals import *
screenwidth = 600
screenheight = 400
pygame.init()
screen = pygame.display.set_mode((screenwidth, screenheight))
pygame.display.set_caption("shoot 'em up")
screenrect = screen.get_rect()
black = (0, 0, 0)
blue = (10, 10, 255)
yellow = (238, 238, 0)
base_width = 80
base_height = 40
bullet_width = 3
bullet_height = 10
class Bullet(pygame.Surface):
def __init__(self, point):
super().__init__((bullet_width, bullet_height), pygame.SRCALPHA)
self.rect = self.get_rect()
self.rect.midbottom = point
self.fill(yellow)
self.velocity = -5
self.alive = True
self.mask = pygame.mask.from_surface(self)
def update(self):
self.rect.top += self.velocity
def draw(self, surf):
surf.blit(self, self.rect)
class Base(pygame.Surface):
def __init__(self, x, y, colour):
super().__init__((base_width, base_height), pygame.SRCALPHA)
self.rect = self.get_rect()
self.rect.x = x
self.rect.y = y
self.fill(colour)
self.alive = True
def add_damage(self, bullet):
width = random.randint(3, 6)
height = random.randint(8, 12)
damage = pygame.Surface((width, height), pygame.SRCALPHA)
damage.fill(black)
rect = damage.get_rect()
rect.x = bullet.rect.x - self.rect.x
rect.y = bullet.rect.top - self.rect.top
self.blit(damage, rect)
#self.mask = pygame.mask.from_surface(self)
def draw(self, surf):
surf.blit(self, self.rect)
class Test(pygame.Surface):
def __init__(self):
super().__init__((600, 400))
self. base = Base(50, 300, blue)
self.bullets = []
def run(self):
while 1:
self.get_events()
self.update()
self.draw()
def get_events(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
if event.type == pygame.KEYDOWN:
bullet = Bullet((60, 380))
self.bullets.append(bullet)
def update(self):
if self.bullets:
for bullet in self.bullets:
bullet.update()
self.collision_check(bullet)
for bullet in self.bullets:
if not bullet.alive:
self.bullets.remove(bullet)
def collision_check(self, bullet):
if bullet.rect.colliderect(self.base):
if self.collide_mask(bullet, self.base):
print("collide")
self.base.add_damage(bullet)
bullet.alive = False
def collide_mask(self, left, right):
xoffset = right.rect[0] - left.rect[0]
yoffset = right.rect[1] - left.rect[1]
try:
leftmask = left.mask
except AttributeError:
leftmask = pygame.mask.from_surface(left)
try:
rightmask = right.mask
except AttributeError:
rightmask = pygame.mask.from_surface(right)
return leftmask.overlap(rightmask, (xoffset, yoffset))
def draw(self):
self.fill(black)
self.base.draw(self)
for bullet in self.bullets:
bullet.draw(self)
screen.blit(self, (0,0))
pygame.display.flip()
if __name__=="__main__":
t = Test()
t.run()
As you can see this is not using pygame sprites.
if the pygame.Surface object is changed you need to recreate the mask with pygame.mask.from_surface. However, the mask is generated form the Surface's alpha channel. Therefore, you need to make the damaged area transparent. Create a completely transparent rectangle (RGBA = 0, 0, 0, 0) and blit the rectangle using the special flag BLEND_RGBA_MULT (or BLEND_RGBA_MIN). Finally recreate the mask:
damage = pygame.Surface((width, height), pygame.SRCALPHA)
self.blit(damage, rect, special_flags=pygame.BLEND_RGBA_MULT)
self.mask = pygame.mask.from_surface(self)
add_damage Mehtod:
class Base(pygame.Surface):
# [...]
def add_damage(self, bullet):
width = random.randint(3, 6)
height = random.randint(8, 12)
damage = pygame.Surface((width, height), pygame.SRCALPHA)
rect = damage.get_rect()
rect.x = bullet.rect.x - self.rect.x
rect.y = bullet.rect.top - self.rect.top
self.blit(damage, rect, special_flags=pygame.BLEND_RGBA_MULT)
self.mask = pygame.mask.from_surface(self)
Hey i am trying to create a breakout clone with pygame, and i used
self.course(180 - self.course) % 360
To bounce the ball of the paddle, however i was looking into the vector 2 class, but i got no idea how to convert my Ball class using this. If anyone could guide me in the right direction.
here is my code that i want to convert using vector2.
import pygame
import math
class Ball(pygame.sprite.Sprite):
course = 130
def __init__(self):
# Calling the parent class (Sprite)
pygame.sprite.Sprite.__init__(self)
# Creating the ball and load the ball image
self.image = pygame.image.load("ball.png").convert()
self.rect = self.image.get_rect()
self.rect.x = 0
self.rect.y = 270
# Creating a bounce function to make the ball bounce of surfaces.
def bounce(self, diff):
self.course = (180 - self.course) % 360
self.course -= diff
# Create the function that will update the ball.
def update(self):
course_radianse = math.radians(self.course)
self.rect.x += 10 * math.sin(course_radianse)
self.rect.y -= 10 * math.cos(course_radianse)
self.rect.x = self.rect.x
self.rect.y = self.rect.y
# Check if ball goes past top
if self.rect.y <= 0:
self.bounce(0)
self.rect.y = 1
# Check if ball goes past left side
if self.rect.x <= 0:
self.course = (360 - self.course) % 360
self.rect.x = 1
# Check if ball goes past right side
if self.rect.x >= 800:
self.course = (360 - self.course) % 360
self.rect.x = 800 - 1
if self.rect.y > 600:
return True
else:
return False
A vector defines a direction and an amount. You have to add the vector to the location of the ball. Sadly pygame.Rect stores integral numbers only, so the location of the object has to be stored in a pygame.math.Vector2, too. You need 1 vector for the location of the object and a 2nd one for the direction. Every time when the location changes, then the .rect attribute has to be set by the rounded location.
If the object hits a surface then the Ball is reflected (.reflect()) by the Normal vector to the surface.
Minimal example: repl.it/#Rabbid76/PyGame-BallBounceOffFrame
import pygame
import random
class Ball(pygame.sprite.Sprite):
def __init__(self, startpos, velocity, startdir):
super().__init__()
self.pos = pygame.math.Vector2(startpos)
self.velocity = velocity
self.dir = pygame.math.Vector2(startdir).normalize()
self.image = pygame.image.load("ball.png").convert_alpha()
self.rect = self.image.get_rect(center = (round(self.pos.x), round(self.pos.y)))
def reflect(self, NV):
self.dir = self.dir.reflect(pygame.math.Vector2(NV))
def update(self):
self.pos += self.dir * self.velocity
self.rect.center = round(self.pos.x), round(self.pos.y)
pygame.init()
window = pygame.display.set_mode((500, 500))
clock = pygame.time.Clock()
all_groups = pygame.sprite.Group()
start, velocity, direction = (250, 250), 5, (random.random(), random.random())
ball = Ball(start, velocity, direction)
all_groups.add(ball)
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
all_groups.update()
if ball.rect.left <= 100:
ball.reflect((1, 0))
if ball.rect.right >= 400:
ball.reflect((-1, 0))
if ball.rect.top <= 100:
ball.reflect((0, 1))
if ball.rect.bottom >= 400:
ball.reflect((0, -1))
window.fill(0)
pygame.draw.rect(window, (255, 0, 0), (100, 100, 300, 300), 1)
all_groups.draw(window)
pygame.display.flip()
Lets assume you have a group of blocks:
block_group = pygame.sprite.Group()
Detect the collision of the ball and the block_group. Once a collision (pygame.sprite.spritecollide()) is detected, reflect the ball on the block:
block_hit = pygame.sprite.spritecollide(ball, block_group, False)
if block_hit:
bl = block_hit[0].rect.left - ball.rect.width/4
br = block_hit[0].rect.right + ball.rect.width/4
nv = (0, 1) if bl < ball.rect.centerx < br else (1, 0)
ball.reflect(nv)
I am teaching a fortnightly coding class to a group of dozen super bright young enthusiasts. We have already covered OOP and created a text based adventure using OOP.
Now I am planning to teach PyGame and continue using objects, and I am wondering if games could be built in such a way where the code for each object is in a separate file?, this would be really neat and easier to build on.
Well For the code below I tried making separate files for each object. This was only partially successful because the draw method never works quite well, I believe the issue that I cannot have separate files referencing the same pygame screen.
import pygame
import random
import time
# Define some colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
BLUE = (0,0,255)
SCREEN_WIDTH = 700
SCREEN_HEIGHT = 500
pygame.init()
class Paddle:
'''Class to keep players location'''
def __init__(self,x=350, y=480, width =70,height=20):
self.x = x
self.y = y
self.change_x = 0
self.change_y = 0
self.width = width
self.height = height
self.score = 0
def move(self):
self.x += self.change_x
self.y += self.change_y
def draw(self):
pygame.draw.rect(screen, BLUE, [self.x,self.y, self.width, self.height])
def check_collision(self,ball):
if ball.y>460:
if abs(35+ self.x - ball.x) < 30:
self.score += 1
ball.draw(BLUE)
ball.y = 0
ball.x = random.randint(0,650)
ball.change_y = random.randint(2,3+int(self.score/5))
class Ball:
"""Class to keep track of a ball's location and vector."""
def __init__(self,x=350,y=250,size=25):
self.x = x
self.y = y
self.change_x = 0
self.change_y = 0
self.size = size
def move(self):
self.x += self.change_x
self.y += self.change_y
def draw(self,colour = WHITE):
pygame.draw.circle(screen,WHITE, [self.x, self.y], self.size)
# Set the height and width of the screen
size = [SCREEN_WIDTH, SCREEN_HEIGHT]
screen = pygame.display.set_mode(size)
pygame.display.set_caption("Bouncing Balls")
done = False
clock = pygame.time.Clock()
screen.fill(BLACK)
ball = Ball()
player = Paddle()
ball.change_y = 2
ball.draw()
while not done:
screen.fill(BLACK)
# --- Event Processing
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
pass
if event.key == pygame.K_LEFT:
player.change_x = -5
if event.key == pygame.K_RIGHT:
player.change_x = 5
else:
ball.change_x = 0
player.change_x = 0
if ball.y > 500:
print('YOUR SCORE: ',player.score)
time.sleep(2)
pygame.quit()
#move ball and player and check if they collide
ball.move()
player.move()
player.check_collision(ball)
#draw ball and player
ball.draw()
player.draw()
#render frame
clock.tick(60)
pygame.display.flip()
# Print score and exit
print('YOUR SCORE: ',player.score)
pygame.quit()
When I had separate files this is the error that I got in relation to screen
line 20, in draw
pygame.draw.circle(screen,WHITE, [self.x, self.y], self.size)
NameError: name 'screen' is not defined
Add a surface argument to the draw() methods of the classes Paddle and Ball and draw the object on the surface which is passed to the method:
class Paddle:
# [...]
def draw(self, surface):
pygame.draw.rect(surface, BLUE, [self.x,self.y, self.width, self.height])
class Ball:
# [...]
def draw(self, surface, colour = WHITE):
pygame.draw.circle(surface, colour, [self.x, self.y], self.size)
Now you can draw the objects on any pygame.Surface you want, e.g. screen:
ball.draw(screen)
player.draw(screen)