Creating Your First Game: A Simple Python Pong Adventure!

Hello aspiring game developers and Python enthusiasts! Have you ever wanted to create your very own game? It might sound complicated, but with Python, it’s a lot simpler and more fun than you think. Today, we’re going to dive into the world of game development by creating a classic game: Pong!

Pong is one of the very first video games ever made, a simple “table tennis” style game where two players control paddles to hit a ball back and forth. It’s a fantastic project for beginners because it introduces many core game development concepts in an easy-to-understand way.

What We’ll Learn

By the end of this guide, you’ll have a working Pong game and understand:
* How to set up a basic game window.
* How to create “sprites” (our paddles and ball) using Python’s turtle module.
* How to move objects around the screen.
* How to handle keyboard input to control paddles.
* How to detect collisions between objects.
* How to keep score.

So, let’s get ready to code and have some fun!

Getting Started: What You Need

Before we begin, you’ll need two things:

  • Python: Make sure you have Python installed on your computer. You can download it from the official Python website (python.org). Any recent version (3.x) will work.
  • A Text Editor: You can use any text editor like VS Code, Sublime Text, Notepad++, or even a simple text editor that comes with your operating system.

That’s it! Python’s turtle module, which we’ll use for graphics, comes built-in with Python, so there’s nothing extra to install.

Step 1: Setting Up Our Game Window

The first thing any game needs is a place to play – a window on your screen! We’ll use the turtle module for this.

Let’s write our first lines of code:

import turtle

wn = turtle.Screen()
wn.title("Simple Pong by YourName") # Set the title of the window
wn.bgcolor("black") # Set the background color to black
wn.setup(width=800, height=600) # Set the dimensions of the window (800 pixels wide, 600 pixels high)
wn.tracer(0) # Turns off screen updates automatically, allowing us to update manually for smoother animation

Let’s break down these new terms:

  • import turtle: This line tells Python to load the turtle module, giving us access to its functions and tools.
  • wn = turtle.Screen(): We’re creating a window where our game will appear. We’re calling this window object wn (short for “window”).
  • wn.title(...): Sets the text that appears in the title bar of our game window.
  • wn.bgcolor(...): Changes the background color of the game window. We’re using “black” here.
  • wn.setup(width=800, height=600): This defines the size of our game window in pixels. A pixel is a tiny dot of color on your screen.
  • wn.tracer(0): This is a bit special. Normally, the turtle module updates the screen every time something moves. For games, we want all movements to happen at once, then update the screen, to make animations smoother. tracer(0) turns off these automatic updates, and we’ll manually update the screen later.

If you run this code, you’ll see a black window pop up! That’s a great start.

Step 2: Creating the Paddles

Now that we have our screen, let’s create the two paddles that players will control. We’ll use another turtle object for each paddle. Think of a turtle object as a little character or “sprite” that we can move and shape.

paddle_a = turtle.Turtle() # Create a turtle object for Paddle A
paddle_a.speed(0) # Set the animation speed to the fastest possible (0 means no animation delay)
paddle_a.shape("square") # Give it a square shape
paddle_a.color("white") # Make it white
paddle_a.shapesize(stretch_wid=5, stretch_len=1) # Stretch the square to be a rectangle (5 times wider than default, 1 time longer)
paddle_a.penup() # Lift the pen so it doesn't draw lines when moving
paddle_a.goto(-350, 0) # Position Paddle A on the left side (x=-350, y=0)

paddle_b = turtle.Turtle() # Create a turtle object for Paddle B
paddle_b.speed(0)
paddle_b.shape("square")
paddle_b.color("white")
paddle_b.shapesize(stretch_wid=5, stretch_len=1)
paddle_b.penup()
paddle_b.goto(350, 0) # Position Paddle B on the right side (x=350, y=0)

What these lines mean:

  • paddle_a = turtle.Turtle(): We create a new turtle object and name it paddle_a.
  • paddle_a.speed(0): This sets how fast the turtle animates its movement. 0 means it moves instantly, which is perfect for game sprites.
  • paddle_a.shape("square"): We tell the turtle to look like a “square”.
  • paddle_a.color("white"): We change its color to white.
  • paddle_a.shapesize(stretch_wid=5, stretch_len=1): This is how we turn a default square (which is 20×20 pixels) into a paddle shape. We stretch its width (stretch_wid) by 5 times (making it 100 pixels tall) and its length (stretch_len) by 1 time (making it 20 pixels wide).
  • paddle_a.penup(): When a turtle moves, it usually draws a line. penup() tells it to lift its invisible pen, so it just moves without drawing.
  • paddle_a.goto(-350, 0): This moves the paddle to a specific location on the screen. The coordinates (-350, 0) mean 350 pixels to the left of the center and right in the middle vertically. The center of the screen is (0, 0).

Step 3: Creating the Ball

Next up, the star of the show: the ball! It’s created very similarly to the paddles. We’ll also give it a starting direction.

ball = turtle.Turtle()
ball.speed(0)
ball.shape("circle") # A circular shape
ball.color("white")
ball.penup()
ball.goto(0, 0) # Start the ball in the center of the screen

ball.dx = 2 # Change in X-coordinate (how many pixels the ball moves horizontally per update)
ball.dy = 2 # Change in Y-coordinate (how many pixels the ball moves vertically per update)
  • ball.dx = 2, ball.dy = 2: These aren’t built-in turtle properties; we’re creating our own variables attached to the ball object. dx stands for “delta x” (change in x) and dy for “delta y” (change in y). These will control how many pixels the ball moves horizontally and vertically in each game frame. A positive dx means it moves right, negative means left. A positive dy means it moves up, negative means down.

Step 4: Moving the Paddles

A game isn’t much fun if you can’t control it! We’ll create functions to move the paddles up and down and then tell the wn (our screen) to listen for keyboard presses.

def paddle_a_up():
    y = paddle_a.ycor() # Get the current y-coordinate of Paddle A
    y += 20 # Add 20 pixels to the y-coordinate
    paddle_a.sety(y) # Set the new y-coordinate

def paddle_a_down():
    y = paddle_a.ycor()
    y -= 20 # Subtract 20 pixels from the y-coordinate
    paddle_a.sety(y)

def paddle_b_up():
    y = paddle_b.ycor()
    y += 20
    paddle_b.sety(y)

def paddle_b_down():
    y = paddle_b.ycor()
    y -= 20
    paddle_b.sety(y)

wn.listen() # Tell the screen to listen for keyboard input
wn.onkeypress(paddle_a_up, "w") # When "w" key is pressed, call paddle_a_up function
wn.onkeypress(paddle_a_down, "s") # When "s" key is pressed, call paddle_a_down function
wn.onkeypress(paddle_b_up, "Up") # When "Up arrow" key is pressed, call paddle_b_up function
wn.onkeypress(paddle_b_down, "Down") # When "Down arrow" key is pressed, call paddle_b_down function
  • def paddle_a_up():: This defines a function named paddle_a_up. Functions are blocks of code that perform a specific task and can be called whenever needed.
  • y = paddle_a.ycor(): ycor() gets the current vertical (y) position of paddle_a.
  • y += 20: This is shorthand for y = y + 20. It adds 20 to the current y value, moving the paddle up.
  • paddle_a.sety(y): This updates the paddle’s vertical position to the new y value.
  • wn.listen(): This line makes the game window responsive to keyboard input.
  • wn.onkeypress(function_name, "key_name"): This is a powerful command! It says: “When the key key_name is pressed, execute the function_name.” We’re binding ‘w’ and ‘s’ for Paddle A, and ‘Up’ (up arrow key) and ‘Down’ (down arrow key) for Paddle B.

Step 5: The Main Game Loop

Games are constantly running, checking for input, updating positions, and redrawing the screen. This continuous cycle is called the “game loop.” This is where all the action happens!

while True:
    wn.update() # Manually update the screen (because we set wn.tracer(0))

    # Move the ball
    ball.setx(ball.xcor() + ball.dx)
    ball.sety(ball.ycor() + ball.dy)

    # Border checking (top and bottom)
    if ball.ycor() > 290: # If ball hits the top border (screen height is 600, so half is 300, allowing for ball size)
        ball.sety(290)
        ball.dy *= -1 # Reverse the vertical direction

    if ball.ycor() < -290: # If ball hits the bottom border
        ball.sety(-290)
        ball.dy *= -1 # Reverse the vertical direction

    # Border checking (left and right)
    if ball.xcor() > 390: # If ball goes off the right side
        ball.goto(0, 0) # Reset ball to center
        ball.dx *= -1 # Reverse direction
        # Here we'd add score for player A

    if ball.xcor() < -390: # If ball goes off the left side
        ball.goto(0, 0) # Reset ball to center
        ball.dx *= -1 # Reverse direction
        # Here we'd add score for player B

    # Paddle and ball collisions
    # Collision with Paddle B
    if (ball.xcor() > 340 and ball.xcor() < 350) and (ball.ycor() < paddle_b.ycor() + 50 and ball.ycor() > paddle_b.ycor() - 50):
        ball.setx(340)
        ball.dx *= -1 # Reverse horizontal direction

    # Collision with Paddle A
    if (ball.xcor() < -340 and ball.xcor() > -350) and (ball.ycor() < paddle_a.ycor() + 50 and ball.ycor() > paddle_a.ycor() - 50):
        ball.setx(-340)
        ball.dx *= -1 # Reverse horizontal direction
  • while True:: This creates an “infinite loop.” The code inside this loop will run over and over again until you close the game window.
  • wn.update(): This is crucial! Since we used wn.tracer(0), this line tells the screen to draw all the changes that have happened since the last update, making the animation smooth.
  • ball.setx(ball.xcor() + ball.dx): This moves the ball horizontally. It takes the ball’s current x-coordinate (ball.xcor()), adds its dx value, and sets the ball to that new x-coordinate. The same logic applies to ball.sety.
  • Border Checking:
    • We check if the ball hits the top or bottom of the screen (ball.ycor() > 290 or ball.ycor() < -290). Remember, the screen is 600 pixels high, so the top is around y=300 and the bottom y=-300. We use 290 to prevent the ball from going halfway out due to its size.
    • If it hits, ball.dy *= -1 reverses its vertical direction, making it bounce.
    • If the ball goes beyond the left or right edges (ball.xcor() > 390 or ball.xcor() < -390), it means a player missed. We reset the ball to the center and reverse its horizontal direction.
  • Paddle Collision:
    • This is a bit more complex. We check two things:
      1. Is the ball horizontally in range of a paddle? (ball.xcor() > 340 and ball.xcor() < 350 for paddle B).
      2. Is the ball vertically aligned with the paddle? (ball.ycor() < paddle_b.ycor() + 50 and ball.ycor() > paddle_b.ycor() - 50). Remember our paddles are 100 pixels tall (50 above center, 50 below).
    • If both conditions are true, the ball has collided with the paddle! We reverse its horizontal direction (ball.dx *= -1).

Step 6: Adding Score

To make our Pong game truly complete, let’s add a scoreboard!

score_a = 0
score_b = 0

pen = turtle.Turtle()
pen.speed(0)
pen.color("white")
pen.penup()
pen.hideturtle() # Hide the turtle icon itself
pen.goto(0, 260) # Position the scoreboard near the top of the screen
pen.write("Player A: 0  Player B: 0", align="center", font=("Courier", 24, "normal"))

And then, modify the “Border checking (left and right)” section in the game loop to update the score:

    # Border checking (left and right)
    if ball.xcor() > 390:
        ball.goto(0, 0)
        ball.dx *= -1
        score_a += 1 # Player A scores!
        pen.clear() # Clear the previous score
        pen.write("Player A: {}  Player B: {}".format(score_a, score_b), align="center", font=("Courier", 24, "normal"))

    if ball.xcor() < -390:
        ball.goto(0, 0)
        ball.dx *= -1
        score_b += 1 # Player B scores!
        pen.clear()
        pen.write("Player A: {}  Player B: {}".format(score_a, score_b), align="center", font=("Courier", 24, "normal"))
  • score_a = 0, score_b = 0: Simple variables to keep track of each player’s score.
  • pen = turtle.Turtle(): We create another turtle object, but this one’s job is just to write text on the screen.
  • pen.hideturtle(): We don’t want to see the turtle icon itself, just its text.
  • pen.write(...): This command writes text to the screen.
    • align="center": Centers the text.
    • font=("Courier", 24, "normal"): Sets the font family, size, and style.
  • score_a += 1: Shorthand for score_a = score_a + 1, which adds 1 to Player A’s score.
  • pen.clear(): Before writing the new score, we clear the old one so they don’t overlap.
  • format(score_a, score_b): This is a neat way to insert the values of score_a and score_b into the string. The {} are placeholders.

Putting It All Together (Full Code)

Here’s the complete code for your simple Pong game! You can copy and paste this into a Python file (e.g., pong_game.py) and run it.

import turtle

wn = turtle.Screen()
wn.title("Simple Pong by YourName")
wn.bgcolor("black")
wn.setup(width=800, height=600)
wn.tracer(0)

paddle_a = turtle.Turtle()
paddle_a.speed(0)
paddle_a.shape("square")
paddle_a.color("white")
paddle_a.shapesize(stretch_wid=5, stretch_len=1)
paddle_a.penup()
paddle_a.goto(-350, 0)

paddle_b = turtle.Turtle()
paddle_b.speed(0)
paddle_b.shape("square")
paddle_b.color("white")
paddle_b.shapesize(stretch_wid=5, stretch_len=1)
paddle_b.penup()
paddle_b.goto(350, 0)

ball = turtle.Turtle()
ball.speed(0)
ball.shape("circle")
ball.color("white")
ball.penup()
ball.goto(0, 0)
ball.dx = 2
ball.dy = 2

score_a = 0
score_b = 0

pen = turtle.Turtle()
pen.speed(0)
pen.color("white")
pen.penup()
pen.hideturtle()
pen.goto(0, 260)
pen.write("Player A: 0  Player B: 0", align="center", font=("Courier", 24, "normal"))

def paddle_a_up():
    y = paddle_a.ycor()
    y += 20
    if y < 250: # Prevent paddle from going off-screen (top)
        paddle_a.sety(y)

def paddle_a_down():
    y = paddle_a.ycor()
    y -= 20
    if y > -250: # Prevent paddle from going off-screen (bottom)
        paddle_a.sety(y)

def paddle_b_up():
    y = paddle_b.ycor()
    y += 20
    if y < 250:
        paddle_b.sety(y)

def paddle_b_down():
    y = paddle_b.ycor()
    y -= 20
    if y > -250:
        paddle_b.sety(y)

wn.listen()
wn.onkeypress(paddle_a_up, "w")
wn.onkeypress(paddle_a_down, "s")
wn.onkeypress(paddle_b_up, "Up")
wn.onkeypress(paddle_b_down, "Down")

while True:
    wn.update()

    # Move the ball
    ball.setx(ball.xcor() + ball.dx)
    ball.sety(ball.ycor() + ball.dy)

    # Border checking (top and bottom)
    if ball.ycor() > 290:
        ball.sety(290)
        ball.dy *= -1

    if ball.ycor() < -290:
        ball.sety(-290)
        ball.dy *= -1

    # Border checking (left and right - scoring)
    if ball.xcor() > 390:
        ball.goto(0, 0)
        ball.dx *= -1
        score_a += 1
        pen.clear()
        pen.write("Player A: {}  Player B: {}".format(score_a, score_b), align="center", font=("Courier", 24, "normal"))

    if ball.xcor() < -390:
        ball.goto(0, 0)
        ball.dx *= -1
        score_b += 1
        pen.clear()
        pen.write("Player A: {}  Player B: {}".format(score_a, score_b), align="center", font=("Courier", 24, "normal"))

    # Paddle and ball collisions
    # Collision with Paddle B
    # Check if ball is horizontally near paddle B AND vertically aligned with it
    if (ball.xcor() > 340 and ball.xcor() < 350) and (ball.ycor() < paddle_b.ycor() + 50 and ball.ycor() > paddle_b.ycor() - 50):
        ball.setx(340) # Push ball back slightly to prevent it from getting stuck
        ball.dx *= -1 # Reverse direction

    # Collision with Paddle A
    # Check if ball is horizontally near paddle A AND vertically aligned with it
    if (ball.xcor() < -340 and ball.xcor() > -350) and (ball.ycor() < paddle_a.ycor() + 50 and ball.ycor() > paddle_a.ycor() - 50):
        ball.setx(-340) # Push ball back slightly
        ball.dx *= -1 # Reverse direction

Note: I added some extra if y < 250 and if y > -250 checks in the paddle movement functions. These are “boundary checks” to prevent your paddles from moving off the top or bottom of the screen.

Conclusion

Congratulations! You’ve just created a fully functional Pong game using Python! You’ve taken your first steps into game development, learning about game windows, sprites, movement, input handling, collision detection, and scoring.

This simple Pong game is a fantastic foundation. Here are some ideas for how you could expand it:

  • Increase Difficulty: Make the ball faster over time.
  • Sound Effects: Add sounds when the ball hits a paddle or scores.
  • More Complex AI: Instead of just bouncing, could the ball’s angle change based on where it hits the paddle?
  • Player vs. Computer: Implement a simple AI for one of the paddles.
  • Start Screen/Game Over Screen: Add more game states.

Keep experimenting, keep coding, and most importantly, have fun! The world of game development is vast and exciting, and you’ve just unlocked its first level.

Comments

Leave a Reply