Trying to a get a movable image with a PNG background using pygame [duplicate] - pygame

This question already has an answer here:
How can I move the ball instead of leaving a trail all over the screen in pygame?
(1 answer)
Closed 6 months ago.
We are trying to create a program using pygame that allows preset image files to be dragged around over another PNG image as the background. Used some code from a tutorial for pygame and tweaked it to fit our specific needs. We have gotten the images to populate and are movable but only with a white background. When importing the PNG file for the background instead, the moveable image disappears behind the background. I have replace the the two filepaths with just "filepath." Still fairly new to python so sorry for ugly code.
Code:
# Import the library pygame
import pygame
from pygame.locals import *
##background
bg = pygame.image.load(filepath)
# Take colors input
YELLOW = (255, 255, 0)
BLUE = (0, 0, 255)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
BLACK = (0, 0, 0)
# Construct the GUI game
pygame.init()
# Set dimensions of game GUI
w, h = 1280, 720
screen = pygame.display.set_mode((w, h))
# Take image as input
img = pygame.image.load(filepath)
img.convert()
# Draw rectangle around the image
rect = img.get_rect()
rect.center = w//2, h//2
# Set running and moving values
running = True
moving = False
# Setting what happens when game
# is in running state
while running:
screen.blit(bg, (320,0))
for event in pygame.event.get():
# Close if the user quits the
# game
if event.type == QUIT:
running = False
# Making the image move
elif event.type == MOUSEBUTTONDOWN:
if rect.collidepoint(event.pos):
moving = True
# Set moving as False if you want
# to move the image only with the
# mouse click
# Set moving as True if you want
# to move the image without the
# mouse click
elif event.type == MOUSEBUTTONUP:
moving = False
# Make your image move continuously
elif event.type == MOUSEMOTION and moving:
rect.move_ip(event.rel)
# Set screen color and image on screen
screen.fill(WHITE)
screen.blit(img, rect)
# Construct the border to the image
pygame.draw.rect(screen, BLACK, rect, 2)
# Update the GUI pygame
pygame.display.update()
##trying to draw the background
# Quit the GUI game
pygame.quit()

I tried out your code and through a bit of experimentation, came up with some refinements that display the image in front of the background. Following is your program with a few tweaks.
# Import the library pygame
import pygame
from pygame.locals import *
##background
bg = pygame.image.load("background.png") # Just a sky blue background
bg = pygame.transform.scale(bg, (1280, 720)) # Made it fit the screen size
# Take colors input
YELLOW = (255, 255, 0)
BLUE = (0, 0, 255)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
BLACK = (0, 0, 0)
# Construct the GUI game
pygame.init()
# Set dimensions of game GUI
w, h = 1280, 720
screen = pygame.display.set_mode((w, h))
# Take image as input
img = pygame.image.load("car.png")
img.convert()
# Draw rectangle around the image
rect = img.get_rect()
rect.center = w//2, h//2
# Set running and moving values
running = True
moving = False
# Setting what happens when game
# is in running state
while running:
screen.blit(bg, (0,0)) # Start the background at the upper left
for event in pygame.event.get():
# Close if the user quits the
# game
if event.type == QUIT:
running = False
# Making the image move
elif event.type == MOUSEBUTTONDOWN:
if rect.collidepoint(event.pos):
moving = True
# Set moving as False if you want
# to move the image only with the
# mouse click
# Set moving as True if you want
# to move the image without the
# mouse click
elif event.type == MOUSEBUTTONUP:
moving = False
# Make your image move continuously
elif event.type == MOUSEMOTION and moving:
rect.move_ip(event.rel)
# Set screen color and image on screen
#screen.fill(WHITE)
screen.blit(img, rect) # Changing the indentation seems to make it visible
# Construct the border to the image
#pygame.draw.rect(screen, BLACK, rect, 2)
# Update the GUI pygame
pygame.display.update()
##trying to draw the background
# Quit the GUI game
pygame.quit()
Following are the highligts.
I utilized a "png" file I had for a sky blue background which was loaded and then transformed to fill the entire 1280 x 720 screen.
The placement of the background was set to start at the upper left; if your background image needs to be centered or oriented differently, keep that in mind.
The indentation of the load of the image to be moved (in my case a "car") was revised to be a part of the "while" loop and not a part of the "for" loop; otherwise, the image only flashes and appears during the handling of the mouse events.
After those tweaks, the image appeared and was moved around by the mouse.
The takeaway from this is that you would want your image to be handled within your drawing/blit cycle. That appears to be where you got tripped up.
Give that a try.

Related

Pygame keeps flickering even though display.update is only called once. Couldn't find an applicable answer from previous posts

I can't seem to figure out why my groups Pygame code keeps flickering when we run it. pygame.display.update is only called once, but our background seems to be potentially causing the issue.
Any an all help would be greatly appreciated! :)
Here's a link to my Replit with the code:
https://replit.com/#LukaLUSV6912/Santa-vs-Evil-Elves-Game?v=1
We are trying to get rid of the flickering from the Pygame animation and implement a winning and loosing screen based off the visibility of the elves(enemies) when the timer runs down to 0
Instructions: move with right + left arrow keys and fire snowballs with spacebar
'''"""
import pygame, random
pygame.init() # initialize pygame managers
# create a window
w = 600
h = 600
win = pygame.display.set_mode((w,h)) # define window variable
# pygame.display.set_caption("Read carefully.") # uncomment & edit to caption the window
#======================== Variables & functions ===================================================
img_size = (50,100)
img_size2 = (30,30)
# class that creates Santa
class Santa:
def __init__(self,color,x,y):
self.color = color
self.x = x
self.y = y
self.img = pygame.image.load("santaimg.png").convert_alpha()
self.img = pygame.transform.scale(self.img, img_size)
self.center=(x,y)
# The methods Right and Left allow Santa to move right and left and have boundries
def Right(self):
if self.x <551:
self.x +=20
self.center=(self.x,self.y)
# self.rect=self.img.get_rect(center=self.center)
def Left(self):
if self.x >0:
self.x -=20
self.center=(self.x,self.y)
# self.rect=self.img.get_rect(center=self.center)
# method that allows the bubbles to be drawn on the screen
def show(self):
# self.img = pygame.image.load("santaimg.png").convert()
# self.rect=self.img.get_rect(center=self.center)
win.blit(self.img, self.center)
#pygame.draw.ellipse(win,self.color,self.img)
#Class that creates the elves
class Elves():
def __init__(self, color, pos_x, pos_y):
self.color = color
self.pos_x= pos_x
self.pos_y = pos_y
self.square = 20
self.velocity = 0 # elves start stationary
self.box = pygame.Rect(self.pos_x,self.pos_y,self.square,self.square)
self.img2 = pygame.image.load("elfimg.png").convert_alpha()
self.img2 = pygame.transform.scale(self.img2,img_size2)
#self.area = pygame.Surface((self.square,self.square))
self.draw = True
# method that displays the elves
def Show(self):
if (self.draw == True):
win.blit(self.img2, (self.pos_x, self.pos_y))
# method that makes elves dissapear if snowball hits them
def dissapear(self, box):
global Dis
if (self.draw):
if self.box.colliderect(box): # box is the rect for elf
Dis += 1 # Each time user hits elf with snowball, value of Dis goes up one
self.draw = False
if (Dis == 20): # New victory image shows once 20 collisions occur
print("Victory! You Saved Christmas!")
global BG
BG = False # Makes it so background image stops refreshing
global SUp
SUp = False #Snowballs stop moving
gameDisplay = pygame.display.set_mode((w,h)) # Background Code - https://stackoverflow.com/questions/65009141/in-pygame-how-do-i-make-an-image-the-background#:~:text=Load%20a%20back%20ground%20image%20%28e.g.%20%27background.jpg%27%29%2C%20scale,pygame.transform.smoothscale%20%28background%2C%20gameDisplay.get_size%20%28%29%29%20gameDisplay.blit%20%28background%2C%20%280%2C%200%29%29
background = pygame.image.load('Victory.png').convert() #Retrieve the image
background = pygame.transform.smoothscale(background, gameDisplay.get_size()) # Sets the image size to the window size
gameDisplay.blit(background, (0, 0)) # Draws the image onto the screen
global running
running = False
# allows the elves to move up and down continuously
def Moving (self):
self.pos_y += self.velocity # Elves move based on velocity value
if self.pos_y >= 0 and self.pos_y < h/2.4: # Elves accelerate downwards when in top half of screen
self.velocity +=.3 # Velocity goes up, pos_y goes down
if self.pos_y > h/2.4: # Elves accelerate upwards when in bottom half of screen
self.velocity -= .3 # Velocity goes down, pos_y goes up
self.box = pygame.Rect(self.pos_x,self.pos_y,self.square,self.square)
#Class that creates snowballs
class Snow:
def __init__(self,color,x,y):
self.x = x
self.y = y
self.color = color
self.height = 15
self.length = 15
self.draw = True
self.snowy = pygame.draw.ellipse(win,self.color,(self.x,self.y,self.height,self.length))
# Show snowballs on screen
def show(self):
if (self.draw == True):
self.snowy = pygame.draw.ellipse(win,self.color,self.snowy)
#Snowballs go up
def upward(self):
global SUp
if SUp == True:
self.snowy.y -= 5
self.snowy = pygame.draw.ellipse(win,self.color,self.snowy)
# class that creates a timer
class Timer:
def __init__(self,limit):
self.limit = limit
self.clock = pygame.time.Clock()
self.start=pygame.time.get_ticks() # get starter tick at Timer instantiation
self.timeLeft = self.countDown()
def countDown(self):
'''Note: Will count down based on time passed, not frame rate, or call frequency.
Retuurns whole value time in seconds (int)'''
currTime=(self.start + pygame.time.get_ticks())/1500 #calculate how many seconds
# if self.timeLeft > 0:
return int(self.limit - currTime) # returns whole second numbers
#================================ Animation loop ===================================================
class Manager:
def __init__(self):
self.colors = ((255,255,255),(0,0,0),(173,216,230),(252,36,2),(0,0,255))
self.ElvesList = []
self.snowballs=[]
global SUp
SUp = True
global BG
BG = True
global Dis
Dis = 0
def main(self):
global running
running = True
clock = pygame.time.Clock()
# code that allows elves to show, everytime you play the game the elves have a different x and y
for i in range(20):
x = random.randint(0,w-20)
y= random.randint(0,h/3)
self.ElvesList.append(Elves(self.colors[2],x,y)) # instantiates the elves class
# calls Santa class in a variable called "SantaC"
SantaC= Santa(self.colors[3],w/2,h-50)
while running:
# clear window (comment out to have trace behind animation)
if (BG == True): # Refresh of image can be turned on/off
gameDisplay = pygame.display.set_mode((w,h)) # Background Code - https://stackoverflow.com/questions/65009141/in-pygame-how-do-i-make-an-image-the-background#:~:text=Load%20a%20back%20ground%20image%20%28e.g.%20%27background.jpg%27%29%2C%20scale,pygame.transform.smoothscale%20%28background%2C%20gameDisplay.get_size%20%28%29%29%20gameDisplay.blit%20%28background%2C%20%280%2C%200%29%29
background = pygame.image.load('SnowBackground.webp').convert() #Retrieve the image
background = pygame.transform.smoothscale(background, gameDisplay.get_size()) # Sets the image size to the window size
gameDisplay.blit(background, (0, 0)) # Draws the image onto the screen
# for every elf created in line 128 they will all show and move, methods of the elves are being called
for evil in self.ElvesList:
evil.Show()
evil.Moving()
# method that allows Santa to be shown
SantaC.show()
#borrowed code in order to display score in left corner (line 141-152)
font = pygame.font.Font("freesansbold.ttf",32)
textx = 10
texty = 10
timer = Timer(27) # 27 is used because there is a 2 second delay to run the code
timeLeft =timer.countDown()
# function that displays timer
def showtimer(x,y):
timing = font.render("Timer :" +str(timeLeft),True,(self.colors[4]))
win.blit(timing,(x,y))
showtimer(textx,texty) # determines where the timer will be displayed
# when the game reaches 0 seconds on the timer, "gameover" is displayed to the terminal
if timeLeft<=0:
gameDisplay = pygame.display.set_mode((w,h)) # Background Code - https://stackoverflow.com/questions/65009141/in-pygame-how-do-i-make-an-image-the-background#:~:text=Load%20a%20back%20ground%20image%20%28e.g.%20%27background.jpg%27%29%2C%20scale,pygame.transform.smoothscale%20%28background%2C%20gameDisplay.get_size%20%28%29%29%20gameDisplay.blit%20%28background%2C%20%280%2C%200%29%29
background = pygame.image.load('Defeat.png').convert() #Retrieve the image
background = pygame.transform.smoothscale(background, gameDisplay.get_size()) # Sets the image size to the window size
gameDisplay.blit(background, (0, 0)) # Draws the image onto the screen
for s in self.snowballs:
s.show()
s.upward()
# Sees if the rect of snowball and rect of elves has collided, if so the elf dissapears
for evil in self.ElvesList:
for s in self.snowballs:
evil.dissapear(s.snowy)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False # stops animation
if event.type == pygame.KEYDOWN:
# handles key press event
if event.key == pygame.K_SPACE:
# When the spacebar is pressed, a snowball will be shot out of Santas location
self.snowballs.append(Snow(self.colors[0],17.5+ SantaC.x,SantaC.y))
if event.key == pygame.K_RIGHT or event.key == pygame.K_d: # santa moves right if right key is clicked. Calls method from Santa's class.
SantaC.Right()
if event.key == pygame.K_LEFT or event.key == pygame.K_a: # santa moves left if left key is clicked. Calls method from Santa's class.
SantaC.Left()
# snowballs will be shown and move upward
#================== Animation control ===================
pygame.display.update()
clock.tick(30) # framerate in fps (30-60 is typical)
Manager().main()
The code is doing a lot of image and font loading repeatedly in the main loop. It's just re-loading the same thing, so this should be moved to before the loop. But I suspect the flickering is caused by the repeated calls to pygame.display.set_mode((w,h)) which is typically only called once, or when handling a window-resize event.
So maybe you want something like this for your main loop:
def showtimer(x,y,font,timeLeft,win):
timing = font.render("Timer :" +str(timeLeft),True,(self.colors[4]))
win.blit(timing,(x,y))
# Set the window size, and setup the timer
gameDisplay = pygame.display.set_mode((w,h))
timer = Timer(27) # 27 because there is a 2 second delay to run the code
timer_x = 10
timer_y = 10
# Load & scale any images, fonts, etc.
font = pygame.font.Font("freesansbold.ttf",32)
snowBackground = pygame.image.load('SnowBackground.webp').convert()
snowBackground = pygame.transform.smoothscale( snowBackground, gameDisplay.get_size())
defBackground = pygame.image.load('Defeat.png').convert()
defBackground = pygame.transform.smoothscale( defBackground, gameDisplay.get_size())
victBackground = pygame.image.load('Victory.png').convert()
victBackground = pygame.transform.smoothscale( victBackground, gameDisplay.get_size())
while running:
timeLeft = timer.countDown()
# clear window (comment out to have trace behind animation)
if (BG == True): # Refresh of image can be turned on/off
# when the game reaches 0 seconds on the timer, "gameover" is displayed to the terminal. Otherwise it's a snowy background
if Dis >= 20:
background = victBackground # player won!
elif timeLeft<=0:
background = defBackground # player defeated
else:
background = snowBackground # still playing
gameDisplay.blit( background, (0, 0) )
# paint & update Evil Elves
for evil in self.ElvesList:
evil.Show()
evil.Moving()
# method that allows Santa to be shown
SantaC.show()
# paint the timer
showtimer( timer_x, timer_y, font, timeLeft, win )
# flush the updates to the window
pygame.display.update()
# handle events & user input
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False # stops animation
# ... etc.
clock.tick( 60 )
I'm not 100% sure how the timer system works, so I just re-arranged it to how I guess it functions. Errors and Omissions should be expected.
The upshot of the changes above is that anything that can be pre-calculated is done outside of the loop. There absolutely no need to re-load and re-scale a background image to the window size every update loop. If you do need to handle window re-size events, do that in an event handler.

Antialiasing an image

I have an image in pygame. I want to apply antialiasing to it. How can I do this? Ideally, I would be able to do this just with pygame and built-in modules but I'm open to other options if necessary.
More specifically, I've got an image of a square split into 4. Each of the quadrants has a different colour. I want to blur it so it looks more like a gradient, so instead of the colours switching instantly where the quadrants meet, it would fade slowly. I believe anti-aliasing is the best way to accomplish this? Here's an image of what I mean:left: what I've got, right: what I need to have
It's not clear what specific image smoothing algorithm you want to use. SciPy provides some relevant functions, so I've constructed an example using ndimage.gaussian_filter().
Clicking a mouse button will apply the filter, or you can scroll the wheel.
import pygame
from scipy.ndimage import gaussian_filter
# https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.gaussian_filter.html
def smooooooth(screen):
"""Apply a gaussian filter to each colour plane"""
# Get reference pixels for each colour plane and then apply filter
r = pygame.surfarray.pixels_red(screen)
gaussian_filter(r, sigma=72, mode="nearest", output=r)
g = pygame.surfarray.pixels_green(screen)
gaussian_filter(g, sigma=72, mode="nearest", output=g)
b = pygame.surfarray.pixels_blue(screen)
gaussian_filter(b, sigma=72, mode="nearest", output=b)
height = 300
width = 300
pygame.init()
screen = pygame.display.set_mode((width, height), pygame.RESIZABLE)
pygame.display.set_caption("Smooth Image")
clock = pygame.time.Clock()
# draw initial pattern
screen.fill((194, 198, 199)) # fill with gray
# fill top left quadrant with white
screen.fill(pygame.Color("white"), pygame.Rect(0, 0, width // 2, height // 2))
# fill bottom left quadrant with black
screen.fill(pygame.Color("black"), pygame.Rect(width // 2, height // 2, width, height))
count = 0
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONUP:
smooooooth(screen)
count += 1
pygame.display.set_caption(f"Smooth Image {count}")
pygame.display.update()
# limit framerate
clock.tick(30)
pygame.quit()
If you play with the sigma argument to the gaussian_filter function, you might be able to get closer to what you're after. E.g. with sigma 72, two passes looks like this:
I've modified the example code to properly support screen resizing, which will redraw the base pattern and using the + and - keys to increment the sigma value so you can do it interactively. Hold Shift to adjust by ten instead of one.
import pygame
from scipy.ndimage import gaussian_filter
# https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.gaussian_filter.html
def smooooooth(screen, sisgma):
"""Apply a gaussian filter to each colour plane"""
# Get reference pixels for each colour plane and then apply filter
r = pygame.surfarray.pixels_red(screen)
gaussian_filter(r, sigma=sigma, mode="nearest", output=r)
g = pygame.surfarray.pixels_green(screen)
gaussian_filter(g, sigma=sigma, mode="nearest", output=g)
b = pygame.surfarray.pixels_blue(screen)
gaussian_filter(b, sigma=sigma, mode="nearest", output=b)
def draw_grey_squares(screen):
"""Draw the base pattern"""
screen.fill((194, 198, 199)) # fill with grey
# fill top left quadrant with white
screen.fill(pygame.Color("white"), (0, 0, width // 2, height // 2))
# fill bottom left quadrant with black
screen.fill(pygame.Color("black"), (width // 2, height // 2, width, height))
height = 300
width = 300
pygame.init()
screen = pygame.display.set_mode((width, height), pygame.RESIZABLE)
draw_grey_squares(screen)
pygame.display.set_caption("Smooth Image")
clock = pygame.time.Clock()
sigma = 72
count = 0
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.VIDEORESIZE:
# redraw on window resize
width, height = event.w, event.h
draw_grey_squares(screen)
count = 0
elif event.type == pygame.KEYUP:
# support + and - to modify sigma
if event.key in (pygame.K_PLUS, pygame.K_KP_PLUS):
if event.mod & pygame.KMOD_SHIFT:
sigma += 10
else:
sigma += 1
elif event.key in (pygame.K_MINUS, pygame.K_KP_MINUS):
if event.mod & pygame.KMOD_SHIFT:
sigma -= 10
else:
sigma -= 1
sigma = max(sigma, 1) # sigma below one doesn't make sense
elif event.type == pygame.MOUSEBUTTONUP:
smooooooth(screen, sigma)
count += 1
pygame.display.set_caption(f"Smooth Image {count} σ {sigma}")
pygame.display.update()
clock.tick(30) # limit framerate
pygame.quit()
Here's an example with sigma 200:

What's the best way to add title to a menu screen in pygame

So, for a school assignment, I need to make a game on Adult Education and so I did but just for the basics, I cannot figure out how to add a title on my menu screen in this game. I have tried many other tutorials and some are pretty easy to understand but for some reason, it ends up not working in my program. What is the best way to add a simple title in pygame?
Heres the code(If you would like to check for any errors I have made):
# Program: Import Library, Pygame, for initialization of this program
import pygame
# Initialize the game engine
pygame.init()
# Define Colours
BLACK = ( 0, 0, 0)
WHITE = ( 255, 255, 255)
GREEN = ( 0, 255, 0)
RED = ( 255, 0, 0)
BLUE = ( 0, 0, 255)
size = (1080, 720)
screen = pygame.display.set_mode(size)
pygame.display.set_caption("MiniConomy Trivia, for Adults")
#Loop until the user clicks the close button
done = False
# -------- Main Program Loop -----------
while done == False:
for event in pygame.event.get(): # User did something
if event.type == pygame.QUIT: # If user clicked close
done = True # Flag that we are done so we exit this loop
# Set the screen background
screen.fill(BLUE)
pygame.display.flip()
# Setting a Title Screen
def text_objects(text, font):
textSurface = font.render(text, True, BLACK)
return textSurface, textSurface.get_rect()
largeText = pygame.font.Font('freesansbold.ttf', 100)
# Creating a Title Screen
TextSurf, TextRect = text_objects("MiniConomy", largeText)
TextRect.center = ((display_width/2),(display_height/2))
gameDisplay.blit(TextSurf, TextRect)
The code you have here is mostly syntactically correct (except for a couple of variable names which don't exist) but it isn't in the right order.
the "Creating a title screen" section should be out of the loop, the first two lines:
TextSurf, TextRect = text_objects("MiniConomy", largeText)
TextRect.center = ((display_width/2),(display_height/2))
should be placed before the game loop and then the blit function:
gameDisplay.blit(TextSurf, TextRect)
should be at the end of the game loop but instead of "gameDisplay" write "screen" instead as that is what you called your display at the start of the program.
The function "text_objects" should also go at the start of the program otherwise it cannot be called.
Here's the full modified code: https://pastebin.com/hQDbD2ya
Which produces this title screen:

pygame Pong - how to attribute my scoring system and collision detection

Hey I'm working on pong and regardless of how hard I try, I keep facing the same problem; the scoring system doesn't appear on the window when I run the code.
I was told I need to initialize attributes such as scoring system, ball and paddles and I guess I did so for the ball and paddle even though I don't really get what that means since this course is the farthest thing from being kind to students who are new to python. But I guess I didn't do so for the scoring system since it's not appearing on the screen.
I've been struggling with these past few weeks and still haven't found a way to fix this and I need to hand in this next week which is making me freak out.
I would appreciate it if you can explain me what did I do wrong and what initializing attribute means and also check my codes for collision detection!
Thank you all SO MUCH!! (By the way, this always happens and confuses me a lot since I get no error message but the scoring system is just not working as I expected)
# Pong V2
# Pong game needs two players and players try to prevent the ball from hitting
# the edge of the screen of their side. If the ball hits the edge of the window,
# the counterpart of the player who couldn't defence the wall gains a point.
# When one of the players reach 11 points, game ends and the player with higher
# points win the game. Players and move the paddles of their own by pressing
# some keys and ball bounces off when the ball has hit the front part of the
# paddle but passes through when they hit the back part of the paddle.
# wall, game continuing until the player presses 'x' button to close the window,
# paddles on the screen but cannot be moved.
# Version 2 of Pong implies the scoring system and game ending when someone hits
# 11 scores. Also, now collision detection for the paddles and the ball has been
# added as a new feature even though still player cannot move the paddles.
import pygame
from random import randint
def main():
# initialize all pygame modules (some need initialization)
pygame.init()
# create a pygame display window
pygame.display.set_mode((500, 400))
# set the title of the display window
pygame.display.set_caption('Pong')
# get the display surface
w_surface = pygame.display.get_surface()
# create a game object
game = Game(w_surface)
# start the main game loop by calling the play method on the game object
game.play()
# quit pygame and clean up the pygame window
pygame.quit()
class Game:
def __init__(self, surface):
# Initialize a Game.
self.surface = surface
self.bg_color = pygame.Color('black')
self.FPS = 60
self.game_Clock = pygame.time.Clock()
self.close_clicked = False
self.continue_game = True
#iniotialize the ball
self.ball = Ball('white', 5, [250, 200], [1, 2], self.surface)
self.max_frames = 150
self.frame_counter = 0
# initialize paddles
self.paddleA = pygame.Rect((70, 170), (10, 60))
self.paddleB = pygame.Rect((415, 170), (10, 60))
# initialize paddles
self.scoreA = 0
self.scoreB = 0
def draw_score_A(self):
# this method draws the player's score in the top-left corner of the
# game window.
strscoreA = str(self.scoreA)
font_color = pygame.Color('white')
font_bg = pygame.Color('black')
font = pygame.font.SysFont("arial", 72)
text_img = font.render(strscoreA, True, font_color, font_bg)
text_pos = (0, 0)
self.surface.blit(text_img, text_pos)
def draw_score_B(self):
# this method draws the player's score in the top-right corner of the
# game window.
strscoreB = str(self.scoreB)
font_color = pygame.Color('white')
font_bg = pygame.Color('black')
font = pygame.font.SysFont("arial", 72)
text_img = font.render(strscoreB, True, font_color, font_bg)
text_pos = (425,0)
self.surface.blit(text_img, text_pos)
def update_score(self):
# check if the ball has hit the left or right side edge and update score
# if the ball hit left side, scoreB goes up by 1 and if the ball hit the right
# side the scoreA goes up by 1.
if self.ball.center[0] < self.ball.radius:
self.scoreB += 1
if self.ball.center[0] + self.ball.radius > self.surface.get_width():
self.scoreA += 1
return self.scoreA, self.scoreB
def play(self):
# Play the game until the player presses the close box.
# - self is the Game that should be continued or not.
while not self.close_clicked: # until player clicks close box
# play frame
self.handle_events()
self.draw()
self.update_score()
self.draw_score_A()
self.draw_score_B()
if self.continue_game:
self.update()
self.decide_continue()
self.game_Clock.tick(self.FPS) # run at most with FPS Frames Per Second
def handle_events(self):
# Handle each user event by changing the game state appropriately.
# - self is the Game whose events will be handled
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
self.close_clicked = True
def draw(self):
# Draw all game objects.
self.surface.fill(self.bg_color) # clear the display surface first
# draw ball on the surface
self.ball.draw()
# draw both paddles on the surface
paddleA = pygame.Rect((70, 170), (10, 60))
self.paddleA = pygame.draw.rect(self.surface, (255, 255, 255), paddleA)
paddleB = pygame.Rect((415, 170), (10, 60))
self.paddleB = pygame.draw.rect(self.surface, (255, 255, 255), paddleB)
pygame.display.update() # make the updated surface appear on the display
def collision_detection(self):
# figure out if the paddle and the ball has collide or not.
# If the ball has hit the front of the paddle, it is recognizes as
# a collision and bounces off to the opposite direction. However
# if the ball has hit the backside of the paddle, this is not
# considered as a collision and this will not make the ball bounce of but
# go through the paddle.We need to check if the center of the ball has passed
# the center of the paddle or not and for this we will use rect.collidepoint
if self.paddleA.collidepoint(self.center) and self.velocity[index] > 0 is True:
self.velocity[index] = self.velocity[index]
elif self.paddleA.collidepoint(self.center) and self.velocity[index] < 0 is True:
self.velocity[index] = - self.velocity[index]
elif self.paddleB.collidepoint(self.center) and self.velocity[index] > 0 is True:
self.velocity[index] = - self.velocity[index]
elif self.paddleB.collidepoint(self.center) and self.velocity[index] < 0 is True:
self.velocity[index] = self.velocity[index]
def update(self):
# Update the game objects for the next frame.
self.ball.move()
self.frame_counter += self.frame_counter
def decide_continue(self):
# Check and remember if the game should continue
# if the score of one of the players reaches 11, the game should end.
# thus we need to check if anyone's score has reached 11 or not and
# decide if we'll continue the game or not.
if self.scoreA == 11 or self.scoreB == 11 :
self.continue_game = False
class Ball:
# An object in this class represents a ball that moves
def __init__(self, ball_color, ball_radius, ball_center, ball_velocity,
surface):
# Initialize a ball.
# - self is the ball to initialize
# - color is the pygame.Color of the ball
# - center is a list containing the x and y int
# coords of the center of the dot
# - radius is the int pixel radius of the ball
# - velocity is a list containing the x and y components
# - surface is the window's pygame.Surface object
self.color = pygame.Color(ball_color)
self.radius = ball_radius
self.center = ball_center
self.velocity = ball_velocity
self.surface = surface
def move(self):
# Change the location of the ball by adding the corresponding
# speed values to the x and y coordinate of its center
# we need the height and width of the screen to determine when ball
# should change their direction of motion (bounce)
screen_width = self.surface.get_width()
screen_height = self.surface.get_height()
# updates the ball's position based on its velocity and also bounces the
# ball off a wall if it gets too close
screen_size = (screen_width, screen_height)
for index in range(0, len(self.center)):
self.center[index] += self.velocity[index]
if (self.center[index] <= 0 + self.radius or self.center[index] >=
screen_size[index] - self.radius):
self.velocity[index] = -self.velocity[index]
def draw(self):
# Draw the ball on the surface
pygame.draw.circle(self.surface, self.color, self.center, self.radius)
main()
The code is calling draw() (to repaint and flush the screen), and then calling draw_score_A() and draw_score_B() without flushing. So the changes made by the draw_score_ functions are never seen by the user.
If you have a function draw() - make it draw everything. This keeps all the drawing and flushing operations in a single place. Sure the draw() function calls other functions to do the painting, but it's all starts from one place.
def draw( self ):
# Draw all game objects.
# clear the display surface first
self.surface.fill( self.bg_color )
# draw ball on the surface
self.ball.draw()
# draw both paddles on the surface
pygame.draw.rect( self.surface, ( 255, 255, 255 ), self.paddleA )
pygame.draw.rect( self.surface, ( 255, 255, 255 ), self.paddleB )
# Paint the scores
self.draw_score_A()
self.draw_score_B()
# make the updated surface appear on the display
pygame.display.update()
Also, your draw() function seems to be re-defining paddleA and paddleB as part of the drawing. I corrected this in the version above.

Pygame: Why is my circle not being drawn onto my sprite's surface?

This is probably so simple, but I cannot see my error. Why is my ball not being drawn onto my sprite's surface and thus not appearing on the screen? When I change the 'draw ellipse' line in the class so that it is drawn onto the screen (instead of onto the surface), it shows. What am I doing wrong?
import pygame
BLACK = ( 0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
class Ball(pygame.sprite.Sprite):
"""This class represents the ball."""
def __init__(self, width, height):
""" Constructor. Pass in the balls x and y position. """
# Call the parent class (Sprite) constructor
super().__init__()
# Create the surface, give dimensions and set it to be transparent
self.image = pygame.Surface([width, height])
self.image.fill(WHITE)
self.image.set_colorkey(WHITE)
# Draw the ellipse onto the surface
pygame.draw.ellipse(self.image, (255,0,0), [0,0,width,height], 10)
# Initialize Pygame
pygame.init()
# Set the height and width of the screen
screen_width = 700
screen_height = 400
screen = pygame.display.set_mode((screen_width, screen_height))
# Used to manage how fast the screen updates
clock = pygame.time.Clock()
# Loop until the user clicks the close button.
done = False
# -------- Main Program Loop -----------
while not done:
# --- Events code goes here (mouse clicks, key hits etc)
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
# --- Clear the screen
screen.fill((255,255,255))
# --- Draw all the objects
ball = Ball(100,100)
# --- Update the screen with what we've drawn.
pygame.display.flip()
# --- Limit to 60 frames per second
clock.tick(60)
pygame.quit()
The circle is drawn onto your sprite's surface, but you never draw the sprite onto the screen.
Also, you should probably only create one instance of Ball, not one on every iteration of your main loop.
You usually put your sprites into groups and call draw on those to actually draw the sprites, like
...
# Loop until the user clicks the close button.
done = False
# --- Create sprites and groups
ball = Ball(100,100)
g = pygame.sprite.Group(ball)
# -------- Main Program Loop -----------
while not done:
# --- Events code goes here (mouse clicks, key hits etc)
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
# --- Clear the screen
screen.fill((255,255,255))
# --- Draw all the objects
g.draw(screen)
# --- Update the screen with what we've drawn.
pygame.display.flip()
# --- Limit to 60 frames per second
clock.tick(60)
pygame.quit()
Note that your sprite needs also a rect attribute for this to work:
...
# Create the surface, give dimensions and set it to be transparent
self.image = pygame.Surface([width, height])
self.image.fill(WHITE)
self.image.set_colorkey(WHITE)
# this rect determinies the position the ball is drawn
self.rect = self.image.get_rect()
# Draw the ellipse onto the surface
pygame.draw.ellipse(self.image, (255,0,0), [0,0,width,height], 10)
...