Linux Format

Code your own Pong

Calvin Robinson takes us through creating a contempora­ry homage to Pong – considered by many to be the very first arcade game.

- Calvin Robinson is a subject matter expert at the National Centre for Computing Education, and a computer science teacher.

Calvin Robinson takes us through creating a contempora­ry homage to

Pong – considered by many to be the very first arcade game.

This series of building retro games in Python has so far seen us coding a lunar landing space module, a side-scrolling platformer, the famous pellet-munching, ghost-chasing Pac-man, and in this issue we’re going to develop our own version of Pong!

To many, Pong is the original arcade game. It’s certainly the first video game to become a commercial success. Released for arcades in 1972 and for the home in 1975, Pong was gained popularity worldwide and is now permanentl­y memorialis­ed in the Smithsonia­n Institute in Washingon DC. Developed and selfpublis­hed by Atari, Pong is a 2D table tennis simulation with arguably the most simplistic game design imaginable, while managing to achieve an incredibly addictive gaming experience.

What gameplay?

Gameplay mechanics in Pong are quite simple. It’s essentiall­y ping-pong seen from a basic birds-eye view. The game makes use of vector graphics to display a rectangula­r shape for the players’ pads and a square (or rough circle, depending on the processing power) for the ball.

In single-player mode, the player moves one paddle and the computer acts as a non-player character, controllin­g the second. The idea is

to get the ball to pass the opponent’s paddle on their side of the screen. You earn a point for each time the opponent misses a rally. Atari’s Home Pong console was set up for twoplayer mode, with a paddle for each player. That’s the version we’re going to create today. Since we don’t have hardware and we’re programmin­g this entirely in Python, we’ll give each player two keys on either side of the keyboard: one for up, one for down.

Python offers us an accessible programmin­g language with straightfo­rward syntax. It’s the perfect language for playing around with vector-based video games. Let’s go!

Ping-pong Python

Without getting into any debates about version 2 versus version 3, we’ll be using Python 3 for this tutorial. If you’re running a Debian based Linux distro you can install Python in Terminal with the command sudo aptget install python3 after a quick update with sudo aptget update , of course. We’ll also be taking advantage of a vector graphics module linuxforma­t.py from last issue. This can be found on the disc, or via the source code link on our Archives page at www.linuxforma­t.com/ archives. Once downloaded or copied, ensure linuxforma­t.py is saved in the same directory as the rest of the files we create in this tutorial.

Now that Python 3 is installed we can create a new Python file. Using your favourite text editor create an empty file and save it in the same folder as your linuxforma­t.py, which we’ll be using as a reference module for vector graphics. To create the file you can use touch pong.py followed by nano pong.py, or open it directly through the Python IDLE.

We’re going to take advantage of the Pygame toolset for this project. Pygame is a useful module designed to assist with, as the name would suggest, the developmen­t of games in Python. Pygame includes a sprite class with shapes such as the rectangle, which we’ll certainly make use of. To install Pygame use pip in command line: pip3 install pygame should cover it. Remember to use pip3 to install modules for Python3, rather than just pip , which can get messy with multiple installati­ons of Python on a single machine.

When it comes to programmin­g, there’s no point re-inventing the wheel every time. Start off by importing the pygame module and initialisi­ng the game engine: import pygame pygame.init()

Let’s declare those constant ‘variables’ and define the colours of our game world using RGB (red green blue) colour coding. It may seem counter-intuitive, but all zeros are black and the full 255 is white. It helps to think of it as the amount of light in that colour range, 255 being the maximum and 0 being none.

BLACK = (0,0,0)

WHITE = (255,255,255)

We’ll want to set the dimensions of our game environmen­t window. We’re using a typical SVGA resolution of 800x600, but you may want to go lower res for a truly retro vibe. VGA for example, is 640x480. size = (800,6500) screen = pygame.display.set_mode(size) pygame.display.set_caption("pong")

We’ve also set the window title to Pong, but you may want to get more creative here.

Now that we have a display window with a resolution set and a coloured background and foreground set up, we can focus on the main loop of the program. Before we do, let’s set the criteria for said loop: carryon = True clock = pygame.time.clock()

Here we’ve told the program that our Boolean carryon is set to True. In a moment we’ll design our loop to continue until we’ve changed the status of this Boolean to False . The clock controls the speed of our updates. We’ll need to continuous­ly observe user inputs and react to them in real time – for example, when the mouse or keyboard is pressed. When we monitor a change, we’ll have to implement the appropriat­e game logic, such as moving the paddle or ball.

After each change, we’ll need to refresh the screen to ensure that the shapes and sprites are redrawn, and this is where the game clock will come in handy. We can set how often this screen refresh takes place – that’s called the ‘frame rate’. In our code below we’ve set the frame rate at 60 frames per second, which is standard for most video games. The higher the frame rate, the smoother the game’s movement will appear on screen, but also the more computing resources are required to run it because the central processor has got more events to calculate and process in a shorter amount of time. while carryon: for event in pygame.event.get(): if event.type == pygame.quit:

carryon = False screen.fill(black) pygame.draw.line(screen, WHITE, [349, 0], [349, 500], 5) pygame.display.flip() clock.tick(60)

So while our carryon bool is True we’re checking if the user did anything ( pygame.event.get() ), and if what they did was click close ( if event.type == pygame.quit ) we’ll switch the carryon bool to False.

We haven’t yet added the game logic – that’ll all go in the middle here after the while loop. For now, we’ve done our drawing. The screen has been filled in black and we’ve drawn a white net across the screen. Then we’ve updated the screen with pygame.display.flip() and set the fps rate.

Quitters!

As always, we’ll need a way to safely quit our program. After the main program loop it’s best to include a pygame.quit() command.

For our paddles, we’re going to create a new class. That way, we can spawn multiple objects of that class and they’ll both carry the same properties. We’ll need a paddle for each player, but they’ll both have the same height, width, colour and so on. So it’s pragmatic to use an Object Oriented approach here, rather than programmin­g two separate entities. To implement Object Oriented Programmin­g properly, we’ll need to

save our paddle class in its own file. Create a new Python file called paddle.py. import pygame

BLACK = (0,0,0) class Paddle(pygame.sprite.sprite): def __init__(self, color, width, height): super().__init__() self.image = pygame.surface([width, height]) self.image.fill(black) self.image.set_colorkey(black) pygame.draw.rect(self.image, color, [0, 0, width, height])

self.rect = self.image.get_rect()

In our first class we’re setting the basic parameters of height, width and colour, as well as the shape. There’s not much to it, because we’re inheriting pygame’s sprite class and building upon that. We don’t, therefore, have to specify what a rectangle is or how to draw one.

To control the movement of our paddles, we’ll need a couple of methods: one for the x-axis and another for the y-axis. Keeping in mind a pixel check to ensure they never go off the screen: def moveup(self, pixels): self.rect.y -= pixels if self.rect.y < 0: self.rect.y = 0

def movedown(self, pixels): self.rect.y += pixels if self.rect.y > 400: self.rect.y = 400

To implement our new class into our game, switch back to pong.py and import it as we would any other module, by adding from paddle import Paddle to the top of the code.

Creating instances of the paddle class is as simple as creating objects. By inserting the code shown below somewhere after defining our window, we’re able to create two paddles: one for player A and the other for player B.

paddlea = PADDLE(WHITE, 10, 100) paddlea.rect.x = 20 paddlea.rect.y = 200 paddleb = PADDLE(WHITE, 10, 100) paddleb.rect.x = 670 paddleb.rect.y = 200

Immediatel­y after that we’ll want to create a list of all the sprites we’re using, including these two paddles:

all_sprites_list = pygame.sprite.group() all_sprites_list.add(paddlea) all_sprites_list.add(paddleb)

When we introduce additional sprites later we’ll add them to this list. To ensure our sprites are drawn, we’ll then call this list right before we flip (draw) our screen, with all_sprites_list.draw(screen) .

A good paddling

The final step of implementi­ng our paddles class is setting up an event handler, to monitor for key presses. If player A is using S and W, and player B is using Up and Down on the keyboard, we’ll need to add the following to our main program loop, carryon : keys = pygame.key.get_pressed() if keys[pygame.k_w]:

paddlea.moveup(5) if keys[pygame.k_s]:

paddlea.movedown(5) if keys[pygame.k_up]:

paddleb.moveup(5) if keys[pygame.k_down]:

paddleb.movedown(5)

We now have a working game environmen­t and moveable paddles. The only thing missing is the ball. As with the paddle, we’ll stick to an OOP approach. Create a new file called ball.py: import pygame from random import randint

BLACK = (0,0,0) class Ball(pygame.sprite.sprite): def __init__(self, color, width, height): super().__init__() self.image = pygame.surface([width, height]) self.image.fill(black) self.image.set_colorkey(black) pygame.draw.rect(self.image, color, [0, 0, width, height]) self.velocity = [randint(4,8),randint(-8,8)] self.rect = self.image.get_rect() def update(self): self.rect.x += self.velocity[0] self.rect.y += self.velocity[1] def bounce(self): self.velocity[0] = -self.velocity[0] self.velocity[1] = randint(-8,8)

Without delving too deep into the maths of it, we’re implementi­ng some basic collision detection by checking to see when a ball hits another surface and then bouncing it off in the opposite direction, at a 90 degree angle.

Back in our pong.py file we’ll need to import the new module with from ball import Ball at the top of our code. We’ll then need to add the ball settings below those of the paddle:

ball = BALL(WHITE,10,10) ball.rect.x = 345 ball.rect.y = 195

We made a list of sprites earlier, with an entry for paddlea and paddleb. We’ll need to add a line for the ball, too: all_sprites_list.add(ball) .

Finally, after we’ve updated all the sprites we’ll need to keep checking if our ball is bouncing off the walls. Add the following code to our carryon loop:

if ball.rect.x>=690:

ball.velocity[0] = -ball.velocity[0] if ball.rect.x<=0:

ball.velocity[0] = -ball.velocity[0] if ball.rect.y>490:

ball.velocity[1] = -ball.velocity[1] if ball.rect.y<0:

ball.velocity[1] = -ball.velocity[1]

That’s the lot. Essentiall­y, we now have a working game. Our paddles are controllab­le by two players and we have a moving ball that can bounce around the game environmen­t. The only thing left to do is introduce a scoring system. After all, a game can’t be properly called a game until there’s a way for someone to win or lose!

Each player needs to bounce the ball past the opponent’s paddle to score a point. Let’s start off by declaring a variable for each player and initialisi­ng them at zero. Placing scorea = 0 and scoreb = 0 with the rest of the variables should suffice.

To add points to these new variables we’ll need to make a slight alteration to the ball bouncing code in our carryon loop. Immediatel­y after if ball.rect.x>=690: insert scorea+=1 and after if ball.rect.x<=0: place scoreb+=1 , leaving the rest as it is. That ought to do the trick.

Scores on the doors

Now that we’ve created scores and added them up, we need to display them on the screen. Right before

pygame.display.flip() , insert the following:

font = pygame.font.font(none, 74) text = font.render(str(scorea), 1, WHITE) screen.blit(text, (250,10)) text = font.render(str(scoreb), 1, WHITE) screen.blit(text, (420,10))

Fire it up and give it a go. If all goes to plan, we should now have a working game of Pong.

We’ve included a full version of the game code on the DVD with this issue, as well as the Archive pages on the Linux Format website.

 ??  ??
 ??  ?? One of the first home video game consoles: Atari Pong.
One of the first home video game consoles: Atari Pong.
 ??  ??
 ??  ?? The original upright Pong cabinet from 1972.
The original upright Pong cabinet from 1972.
 ??  ?? Pong with working paddles and bouncing ball.
Pong with working paddles and bouncing ball.
 ??  ?? Fully functionin­g retro Pong, developed with Python.
Fully functionin­g retro Pong, developed with Python.
 ??  ?? Reduce the playing screen so that you can amend the Python code where necessary.
Reduce the playing screen so that you can amend the Python code where necessary.

Newspapers in English

Newspapers from Australia