Pong is a table tennis-themed 2-player 2D arcade video game developed in the early 1970s. The game consists of two paddles/strikers, located at the left and right edges of the screen, and a ball.
Create a Pong Game in Python
The basic theme of this game is to make sure that the ball doesn’t hit the wall behind you. If it does, the opponent scores a point. And if the ball touches the opponent’s wall, you score a point. This article covers how to create such a game in Python using the Pygame module.
Key Controls
This is 2 player game and hence there will be two controllable paddles in the game. One at the leftmost edge of the screen and the other at the rightmost edge of the screen
- To control the left paddle, use the ‘s’ and ‘w’ keys. ‘w’ will move the paddle upwards and ‘s’ will move the paddle downwards
- To control the right paddle, use the UP and DOWN arrow keys. UP arrow will move the paddle upwards and the DOWN arrow will move the paddle downwards
Implementation to Create a Pong Game in Python
The game is developed in the Object Oriented Programming(OOP) style.
- Create a “Striker” (paddle) class that handles all the controls related to the player
- Create a “Ball” class that handles all the controls related to the ball
- Two objects of the Striker class, geek1, and geek2, are instantiated
- One object of the Ball class is instantiated.
- The “main” function acts as the game manager and controls the game logic and flow
Initial Setup: This is the initial setup that consists of all the initializations and global variables that are needed
Python3
import pygame pygame.init() # Font that is used to render the text font20 = pygame.font.Font( 'freesansbold.ttf' , 20 ) # RGB values of standard colors BLACK = ( 0 , 0 , 0 ) WHITE = ( 255 , 255 , 255 ) GREEN = ( 0 , 255 , 0 ) # Basic parameters of the screen WIDTH, HEIGHT = 900 , 600 screen = pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption( "Pong" ) # Used to adjust the frame rate clock = pygame.time.Clock() FPS = 30 |
Striker: The Striker class has the following methods:
- init() used to initialize the class variables
- display() used to render the object on the screen
- update() used to change the state of the object
- displayScore() used to render the score of the player on the screen in text format
- getRect() used to get the rect object
Python3
# Player class class Striker: # Take the initial position, # dimensions, speed and color of the object def __init__( self , posx, posy, width, height, speed, color): self .posx = posx self .posy = posy self .width = width self .height = height self .speed = speed self .color = color # Rect that is used to control the # position and collision of the object self .geekRect = pygame.Rect(posx, posy, width, height) # Object that is blit on the screen self .geek = pygame.draw.rect(screen, self .color, self .geekRect) # Used to display the object on the screen def display( self ): self .geek = pygame.draw.rect(screen, self .color, self .geekRect) # Used to update the state of the object # yFac represents the direction of the striker movement # if yFac == -1 ==> The object is moving upwards # if yFac == 1 ==> The object is moving downwards # if yFac == 0 ==> The object is not moving def update( self , yFac): self .posy = self .posy + self .speed * yFac # Restricting the striker to be below # the top surface of the screen if self .posy < = 0 : self .posy = 0 # Restricting the striker to be above # the bottom surface of the screen elif self .posy + self .height > = HEIGHT: self .posy = HEIGHT - self .height # Updating the rect with the new values self .geekRect = ( self .posx, self .posy, self .width, self .height) # Used to render the score on to the screen # First, create a text object using the font.render() method # Then, get the rect of that text using the get_rect() method # Finally blit the text on to the screen def displayScore( self , text, score, x, y, color): text = font20.render(text + str (score), True , color) textRect = text.get_rect() textRect.center = (x, y) screen.blit(text, textRect) def getRect( self ): return self .geekRect |
Ball: The Ball class has the following methods:
- init() used to initialize the class variables
- display() used to render the object onto the screen
- update() used to change the state of the object
- getRect() used to get the rect object
Python3
# Ball class class Ball: def __init__( self , posx, posy, radius, speed, color): self .posx = posx self .posy = posy self .radius = radius self .speed = speed self .color = color self .xFac = 1 self .yFac = - 1 self .ball = pygame.draw.circle( screen, self .color, ( self .posx, self .posy), self .radius) self .firstTime = 1 def display( self ): self .ball = pygame.draw.circle( screen, self .color, ( self .posx, self .posy), self .radius) def update( self ): self .posx + = self .speed * self .xFac self .posy + = self .speed * self .yFac # If the ball hits the top or bottom surfaces, # then the sign of yFac is changed and it # results in a reflection if self .posy < = 0 or self .posy > = HEIGHT: self .yFac * = - 1 # If the ball touches the left wall for the first time, # The firstTime is set to 0 and we return 1 # indicating that Geek2 has scored # firstTime is set to 0 so that the condition is # met only once and we can avoid giving multiple # points to the player if self .posx < = 0 and self .firstTime: self .firstTime = 0 return 1 elif self .posx > = WIDTH and self .firstTime: self .firstTime = 0 return - 1 else : return 0 # Used to reset the position of the ball # to the center of the screen def reset( self ): self .posx = WIDTH / / 2 self .posy = HEIGHT / / 2 self .xFac * = - 1 self .firstTime = 1 # Used to reflect the ball along the X-axis def hit( self ): self .xFac * = - 1 def getRect( self ): return self .ball |
Game Manager: The game manager consists of all the required game logic, like the game loop, event handling, entity handling, updating the scene, etc., and ensures proper game flow. The main() function is used to implement the game logic
Python3
# Game Manager def main(): running = True # Defining the objects geek1 = Striker( 20 , 0 , 10 , 100 , 10 , GREEN) geek2 = Striker(WIDTH - 30 , 0 , 10 , 100 , 10 , GREEN) ball = Ball(WIDTH / / 2 , HEIGHT / / 2 , 7 , 7 , WHITE) listOfGeeks = [geek1, geek2] # Initial parameters of the players geek1Score, geek2Score = 0 , 0 geek1YFac, geek2YFac = 0 , 0 while running: screen.fill(BLACK) # Event handling for event in pygame.event.get(): if event. type = = pygame.QUIT: running = False if event. type = = pygame.KEYDOWN: if event.key = = pygame.K_UP: geek2YFac = - 1 if event.key = = pygame.K_DOWN: geek2YFac = 1 if event.key = = pygame.K_w: geek1YFac = - 1 if event.key = = pygame.K_s: geek1YFac = 1 if event. type = = pygame.KEYUP: if event.key = = pygame.K_UP or event.key = = pygame.K_DOWN: geek2YFac = 0 if event.key = = pygame.K_w or event.key = = pygame.K_s: geek1YFac = 0 # Collision detection for geek in listOfGeeks: if pygame.Rect.colliderect(ball.getRect(), geek.getRect()): ball.hit() # Updating the objects geek1.update(geek1YFac) geek2.update(geek2YFac) point = ball.update() # -1 -> Geek_1 has scored # +1 -> Geek_2 has scored # 0 -> None of them scored if point = = - 1 : geek1Score + = 1 elif point = = 1 : geek2Score + = 1 if point: # Someone has scored a point and the # ball is out of bounds. So, we reset it's position ball.reset() # Displaying the objects on the screen geek1.display() geek2.display() ball.display() # Displaying the scores of the players geek1.displayScore( "Geek_1 : " , geek1Score, 100 , 20 , WHITE) geek2.displayScore( "Geek_2 : " , geek2Score, WIDTH - 100 , 20 , WHITE) pygame.display.update() # Adjusting the frame rate clock.tick(FPS) |
Complete Code
By joining all the above classes and the game manager together, the final code would look like this
Python3
import pygame pygame.init() # Font that is used to render the text font20 = pygame.font.Font( 'freesansbold.ttf' , 20 ) # RGB values of standard colors BLACK = ( 0 , 0 , 0 ) WHITE = ( 255 , 255 , 255 ) GREEN = ( 0 , 255 , 0 ) # Basic parameters of the screen WIDTH, HEIGHT = 900 , 600 screen = pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption( "Pong" ) clock = pygame.time.Clock() FPS = 30 # Striker class class Striker: # Take the initial position, dimensions, speed and color of the object def __init__( self , posx, posy, width, height, speed, color): self .posx = posx self .posy = posy self .width = width self .height = height self .speed = speed self .color = color # Rect that is used to control the position and collision of the object self .geekRect = pygame.Rect(posx, posy, width, height) # Object that is blit on the screen self .geek = pygame.draw.rect(screen, self .color, self .geekRect) # Used to display the object on the screen def display( self ): self .geek = pygame.draw.rect(screen, self .color, self .geekRect) def update( self , yFac): self .posy = self .posy + self .speed * yFac # Restricting the striker to be below the top surface of the screen if self .posy < = 0 : self .posy = 0 # Restricting the striker to be above the bottom surface of the screen elif self .posy + self .height > = HEIGHT: self .posy = HEIGHT - self .height # Updating the rect with the new values self .geekRect = ( self .posx, self .posy, self .width, self .height) def displayScore( self , text, score, x, y, color): text = font20.render(text + str (score), True , color) textRect = text.get_rect() textRect.center = (x, y) screen.blit(text, textRect) def getRect( self ): return self .geekRect # Ball class class Ball: def __init__( self , posx, posy, radius, speed, color): self .posx = posx self .posy = posy self .radius = radius self .speed = speed self .color = color self .xFac = 1 self .yFac = - 1 self .ball = pygame.draw.circle( screen, self .color, ( self .posx, self .posy), self .radius) self .firstTime = 1 def display( self ): self .ball = pygame.draw.circle( screen, self .color, ( self .posx, self .posy), self .radius) def update( self ): self .posx + = self .speed * self .xFac self .posy + = self .speed * self .yFac # If the ball hits the top or bottom surfaces, # then the sign of yFac is changed and # it results in a reflection if self .posy < = 0 or self .posy > = HEIGHT: self .yFac * = - 1 if self .posx < = 0 and self .firstTime: self .firstTime = 0 return 1 elif self .posx > = WIDTH and self .firstTime: self .firstTime = 0 return - 1 else : return 0 def reset( self ): self .posx = WIDTH / / 2 self .posy = HEIGHT / / 2 self .xFac * = - 1 self .firstTime = 1 # Used to reflect the ball along the X-axis def hit( self ): self .xFac * = - 1 def getRect( self ): return self .ball # Game Manager def main(): running = True # Defining the objects geek1 = Striker( 20 , 0 , 10 , 100 , 10 , GREEN) geek2 = Striker(WIDTH - 30 , 0 , 10 , 100 , 10 , GREEN) ball = Ball(WIDTH / / 2 , HEIGHT / / 2 , 7 , 7 , WHITE) listOfGeeks = [geek1, geek2] # Initial parameters of the players geek1Score, geek2Score = 0 , 0 geek1YFac, geek2YFac = 0 , 0 while running: screen.fill(BLACK) # Event handling for event in pygame.event.get(): if event. type = = pygame.QUIT: running = False if event. type = = pygame.KEYDOWN: if event.key = = pygame.K_UP: geek2YFac = - 1 if event.key = = pygame.K_DOWN: geek2YFac = 1 if event.key = = pygame.K_w: geek1YFac = - 1 if event.key = = pygame.K_s: geek1YFac = 1 if event. type = = pygame.KEYUP: if event.key = = pygame.K_UP or event.key = = pygame.K_DOWN: geek2YFac = 0 if event.key = = pygame.K_w or event.key = = pygame.K_s: geek1YFac = 0 # Collision detection for geek in listOfGeeks: if pygame.Rect.colliderect(ball.getRect(), geek.getRect()): ball.hit() # Updating the objects geek1.update(geek1YFac) geek2.update(geek2YFac) point = ball.update() # -1 -> Geek_1 has scored # +1 -> Geek_2 has scored # 0 -> None of them scored if point = = - 1 : geek1Score + = 1 elif point = = 1 : geek2Score + = 1 # Someone has scored # a point and the ball is out of bounds. # So, we reset it's position if point: ball.reset() # Displaying the objects on the screen geek1.display() geek2.display() ball.display() # Displaying the scores of the players geek1.displayScore( "Geek_1 : " , geek1Score, 100 , 20 , WHITE) geek2.displayScore( "Geek_2 : " , geek2Score, WIDTH - 100 , 20 , WHITE) pygame.display.update() clock.tick(FPS) if __name__ = = "__main__" : main() pygame.quit() |
Output: