Pong Game using Python
This article explains how to develop a simple Pong game using Python. To create this, all we need is to know the basics of the Python programming language. A Pong game has a ball that moves all the time within a rectangular space. Ball deflects from the top and the side walls. But, if the ball hits the bottom wall, game is over.
To prevent the ball from hitting the bottom wall, a small base rectangle object is there at the bottom which can be moved horizontally using keyboard. We have to place this object in the right position so that the ball hits this object each time and not the bottom wall.
To develop the game, we have to start with a rectangular GUI which holds the moving ball and the base object and displays the score as well. Python has many such GUI libraries. For this sample, we are going to use TKinter, which is one of the simplest out there and has all the features that we need to develop a pong game.
The most complex part of this pong game is the logic to make the ball move and deflect the ball to right direction after hitting the wall. We also need to find a way to know, when the ball hits the base object. All these can be written using basic Python and is explained in detail in the below sections.
Doing this sample will help you to get a better understanding of Python and Tkinter. Particularly, on how to use canvas, create, shapes in it, move the shapes and bind event to these shapes.
Below are the step by step process to develop this game. Also provided the complete code at the last section.
-
Install Python and Tkinter
Download and install the latest version of python from Python Website.
Set up virtual environment also to avoid any unexpected errors.
Open command prompt and navigate to the folder where you are going to place the code and activate virtual environment.
Install TKinter using command pip install tk. If you get an error 'No module named pip', refer virtual environment setup section.
If you wish to get a quick tutorial of tkinter before starting with the sample, please have a look at the Tkinter Quick Reference. -
Running the game
Create a file name game.py. Here we are going to place our code.
To run the game navigate to the folder in cmd and use commands python game.py or py game.py depending on the os.
If we get any error on using above commands, it is likely because we have set up our virtual environment using older version of Python. Here we have to use commands like python3.9 -m game or py -3.9 -m game
-
Define the main method
__main__ needs to be defined for any Tkinter application. This section of code execute only once when the page loads for the first time.
First define a window of size 400 * 400. 2 variables to set the speed of ball and base rectangle. Also a global variable to see if the ball hits the bottom window (not the base).
Then create a canvas with same size of window. Create a rectangle base and a ball shape and place these at the bottom. An Entry to keep the score. A label which is initially hidden and displays when game fails.
Bind left and right arrow key press to move the base according to the speed set.
Bind enter key press to restart the game.
startBall() method is called to start the game.Copiedif __name__ == "__main__": root = Tk() root.minsize(400,400) basesp = 10 ballsp = 5 global isFailed isFailed = False c = Canvas(width=400, height=400, background='#a0aa00') c.pack() base = c.create_rectangle(150, 385, 250, 400, fill='blue', outline='blue') ball = c.create_oval(190, 365, 210, 385, fill='red', outline='red') txtS = tk.Entry(c, text='0') txtScore = c.create_window(350, 0, anchor='nw', window=txtS) lblM = tk.Label(c, text='Failed!!!Press Enter key to start again') lblID = c.create_window(100, 190, anchor='nw', window=lblM) c.itemconfigure(lblID, state='hidden') root.bind("<KeyPress-Left>", lambda event: moveBaseLR(base, 'l', 0-basesp)) root.bind("<KeyPress-Right>", lambda event: moveBaseLR(base, 'r', basesp)) root.bind("<Return>", lambda event: restart()) startBall(ball, ballsp) root.mainloop()
-
Function to move the base rectangle
This function gets called on left or right arrow key press.
Get the current position of base using c.coords(base)
Then use canvas function c.move(base, x, y) to move the base by a particular value in x and y direction and c.update() to reflect the changes in canvas.Copieddef moveBaseLR(base, dir, x, y = 0): x1, y1, x2, y2 = c.coords(base) if ((x1 > 0 and dir == 'l') or (x2 < 400 and dir == 'r')): c.move(base, x, y) c.update()
-
Function to move the ball
This is the most challenging section of code for this game and is written completely using basic Python.
We are using a for loop to continuously move the ball.
When the ball hits the wall, we have to deflect it by changing the x and y values to positive or negative. Eight scenarios are there which are explained in code itself as comments.
When ball reaches bottom, we have to check if it is hitting base or not. If it hits the base rectangle, ball should go up. Otherwise game fails and fail message is displayed.
Also we are providing a small break between each loop statement using time.sleep(.025) for smooth movement of ball.Copieddef startBall(ball, sp): s = random.randint(-sp, sp) x, y = s, 0-sp #Start. Ball to move in random direction. 0-sp is used to get negative value c.move(ball, x, y) for p in range(1, 500000): l, t, r, b = c.coords(ball) txtS.delete(0, END) txtS.insert(0, str(p)) #Need to change direction on hitting wall. Eight options are there. if(r >= 400 and x >= 0 and y < 0): #Ball moving ↗ and hit right wall x, y = 0-sp, 0-sp elif(r >= 400 and x >= 0 and y >= 0): #Ball moving ↘ and hit right wall x, y = 0-sp, sp elif(l <= 0 and x < 0 and y < 0): #Ball moving ↖ and hit left wall x, y = sp, 0-sp elif(l <= 0 and x < 0 and y >= 0): #Ball moving ↙ and hit left wall x, y = sp, sp elif(t <= 0 and x > 0 and y < 0): #Ball moving ↗ and hit top wall x, y = sp, sp elif(t <= 0 and x < 0 and y < 0): #Ball moving ↖ and hit top wall x, y = 0-sp, sp elif(b >= 385): #Ball reached base level. Check if base touches ball tchPt = l + 10 #Size is 20. Half of it. bsl, bst, bsr, bsb = c.coords(base) if(tchPt >= bsl and tchPt <= bsr): #Ball touch base n = random.randint(-sp, sp) x, y = n, 0-sp else: #Hit bottom. Failed c.itemconfigure(lblID, state='normal') global isFailed isFailed = True break time.sleep(.025) c.move(ball, x, y) c.update()
-
Restarting the Game
Game is restarted on pressing the enter key.
When enter key is pressed, we will check if the game is still running or not. If not, fail message is hidden and shapes are moved to the initial position and game starts.Copieddef restart(): global isFailed if(isFailed == True): isFailed = False c.itemconfigure(lblID, state='hidden') c.moveto(base, 150, 385) c.moveto(ball, 190, 365) startBall(ball, ballsp)
Complete Code
from tkinter import * import tkinter as tk import time import random global isFailed def moveBaseLR(base, dir, x, y = 0): x1, y1, x2, y2 = c.coords(base) if ((x1 > 0 and dir == 'l') or (x2 < 400 and dir == 'r')): c.move(base, x, y) c.update() def startBall(ball, sp): s = random.randint(-sp, sp) x, y = s, 0-sp #Start. Ball to move in random direction. 0-sp is used to get negative value c.move(ball, x, y) for p in range(1, 500000): l, t, r, b = c.coords(ball) txtS.delete(0, END) txtS.insert(0, str(p)) #Need to change direction on hitting wall. Eight options are there. if(r >= 400 and x >= 0 and y < 0): #Ball moving ↗ and hit right wall x, y = 0-sp, 0-sp elif(r >= 400 and x >= 0 and y >= 0): #Ball moving ↘ and hit right wall x, y = 0-sp, sp elif(l <= 0 and x < 0 and y < 0): #Ball moving ↖ and hit left wall x, y = sp, 0-sp elif(l <= 0 and x < 0 and y >= 0): #Ball moving ↙ and hit left wall x, y = sp, sp elif(t <= 0 and x >= 0 and y < 0): #Ball moving ↗ and hit top wall x, y = sp, sp elif(t <= 0 and x < 0 and y < 0): #Ball moving ↖ and hit top wall x, y = 0-sp, sp elif(b >= 385): #Ball reached base level. Check if base touches ball tchPt = l + 10 #Size is 20. Half of it. bsl, bst, bsr, bsb = c.coords(base) if(tchPt >= bsl and tchPt <= bsr): #Ball touch base n = random.randint(-sp, sp) x, y = n, 0-sp else: #Hit bottom. Failed c.itemconfigure(lblID, state='normal') global isFailed isFailed = True break time.sleep(.025) c.move(ball, x, y) c.update() def restart(): global isFailed if(isFailed == True): isFailed = False c.itemconfigure(lblID, state='hidden') c.moveto(base, 150, 385) c.moveto(ball, 190, 365) startBall(ball, ballsp) if __name__ == "__main__": root = Tk() root.minsize(400,400) basesp = 10 ballsp = 5 global isFailed isFailed = False c = Canvas(width=400, height=400, background='#a0aa00') c.pack() base = c.create_rectangle(150, 385, 250, 400, fill='blue', outline='blue') ball = c.create_oval(190, 365, 210, 385, fill='red', outline='red') txtS = tk.Entry(c, text='0') txtScore = c.create_window(350, 0, anchor='nw', window=txtS) lblM = tk.Label(c, text='Failed!!!Press Enter key to start again') lblID = c.create_window(100, 190, anchor='nw', window=lblM) c.itemconfigure(lblID, state='hidden') root.bind("<KeyPress-Left>", lambda event: moveBaseLR(base, 'l', 0-basesp)) root.bind("<KeyPress-Right>", lambda event: moveBaseLR(base, 'r', basesp)) root.bind("<Return>", lambda event: restart()) startBall(ball, ballsp) root.mainloop()