Multiplayer Game Programming for Teens with Python: Part 1

Have you ever wondered how a multiplayer game works? This tutorial will teach teens and adults about multiplayer game programming in python with PyGame. By .

Leave a rating/review
Save for later
Share
You are currently viewing page 2 of 3 of this article. Click here to view the first page.

Drawing the Board and Lines on the Screen

In PyGame, the upper left of the window is coordinate (0, 0). So let’s define a coordinate system for the points in the Boxes grid that is similar, with (0,0) representing the upper left point and (6,6) representing the bottom right point:

Boxes4

Somehow, you need a way to represent the potential line segments in the game. Well, there are two different types of line segments: horizontal and vertical lines. Let’s imagine you make a list of all the potential horizontal and vertical line combinations. It would look something like this:

Boxes5

In programming terms, a list is also known as an array. And when you have a list of lists, like the horizontal and vertical line combinations here, that’s called a 2D array.

For example, to represent the horizontal line from (0, 0) to (1, 1), that would be row 0, column 0 in the “horizontal lines” list.

Note that the horizontal lines list has 6 rows and 7 columns, and the vertical lines list has 7 rows and 6 columns.

Add these two lines to __init__ to define these two arrays:

self.boardh = [[False for x in range(6)] for y in range(7)]
self.boardv = [[False for x in range(7)] for y in range(6)]

A quick way to create an array is to do this: [valuePerItem for x in y]. In this case, you fill an array with an array filled with Falses. False stands for an empty space.

Now that you have the board representation, let’s get to the code of drawing the board.

First of all, create a new method called initGraphics(). This method will be something you call from __init__ but, to keep your code organized, you’re creating a separate method just for the purpose of loading the graphics. Add this right before the __init__ function:

def initGraphics(self):
    self.normallinev=pygame.image.load("normalline.png")
    self.normallineh=pygame.transform.rotate(pygame.image.load("normalline.png"), -90)
    self.bar_donev=pygame.image.load("bar_done.png")
    self.bar_doneh=pygame.transform.rotate(pygame.image.load("bar_done.png"), -90)
    self.hoverlinev=pygame.image.load("hoverline.png")
    self.hoverlineh=pygame.transform.rotate(pygame.image.load("hoverline.png"), -90)

As you can see, you have three main sprites: an normal (empty) line, a done (occupied) line and a hover line. You rotate each of these lines by 90 degrees to draw the horizontal versions of them. These files came with the resources you downloaded earlier and should be in the same directory as your Python file.

You have a method to load all of the graphics, but you have yet to call it. Try to guess where to add what!

Once you have an answer, click the Show button below to see if you’re right.

[spoiler title=”Solution”]Add this at the end of __init__:

#initialize the graphics
self.initGraphics()

[/spoiler]

Next you should add the code that actually draws the board. To loop through every x and y in a grid, you must add a for loop inside of a for loop. (For all of you Inception fans, a for-loop-ception.) You need a loop that loops through the x- and y-values. Add this right after the __init__ method:

def drawBoard(self):
    for x in range(6):
        for y in range(7):
            if not self.boardh[y][x]:
                self.screen.blit(self.normallineh, [(x)*64+5, (y)*64])
            else:
                self.screen.blit(self.bar_doneh, [(x)*64+5, (y)*64])
    for x in range(7):
        for y in range(6):
            if not self.boardv[y][x]:
                self.screen.blit(self.normallinev, [(x)*64, (y)*64+5])
            else:
                self.screen.blit(self.bar_donev, [(x)*64, (y)*64+5])

This code simply loops through the grid and checks whether or not that part on the grid has been clicked. The code does this for both the horizontal and vertical lines. self.boardv[x][y] and self.boardh[x][y] returns either true or false, depending on whether the appropriate line segment has been filled in yes.

Running the program now still won’t do anything. All you’ve done is defined what the game should do if it ever gets that method call.

Now let’s add the method call to the update function. Add this after you clear the screen with screen.fill(0):

#draw the board
self.drawBoard()

And of course, as a good programmer, you remember to add a comment to explain the code.

Run your code now. When you do, you should see the grid drawn on the screen:

Screen Shot 2013-06-13 at 6.28.57 AM

Every time I write map drawing code, I like to test it out, both because it’s fun and because it’s a good way to find bugs. Add this after you initialize the boards by defining self.boardh and self.boardv:

self.boardh[5][3]=True

Run the code and as you can see, one horizontal line is lit up – the line from (5, 3) to (5, 4):

Boxes6

Pretty cool, huh? Delete the line of test code you just added.

Good job. You’ve finished drawing your map, which is one of the most difficult things to do in game programming.

Adding Other Types of Lines

Next you need to find the line to which the mouse is closest so that you can draw a hover line at that spot.

First, at the top of the file, add this line to import the math library, which you’ll need soon:

import math

Then, before pygame.display.flip(), add this big chunk of code:

#1
mouse = pygame.mouse.get_pos()

#2
xpos = int(math.ceil((mouse[0]-32)/64.0))
ypos = int(math.ceil((mouse[1]-32)/64.0))

#3
is_horizontal = abs(mouse[1] - ypos*64) < abs(mouse[0] - xpos*64)

#4
ypos = ypos - 1 if mouse[1] - ypos*64 < 0 and not is_horizontal else ypos
xpos = xpos - 1 if mouse[0] - xpos*64 < 0 and is_horizontal else xpos

#5
board=self.boardh if is_horizontal else self.boardv 
isoutofbounds=False

#6
try: 
    if not board[ypos][xpos]: self.screen.blit(self.hoverlineh if is_horizontal else self.hoverlinev, [xpos*64+5 if is_horizontal else xpos*64, ypos*64 if is_horizontal else ypos*64+5])
except:
    isoutofbounds=True
    pass
if not isoutofbounds:
    alreadyplaced=board[ypos][xpos]
else:
    alreadyplaced=False

Wow! That's a lot of code. Let's go over the sections one-by-one:

  1. First you get the mouse position with PyGame's built-in function.
  2. Next you get the position of the mouse on the grid, using the fact that each square is 64x64 pixels.
  3. You check if the mouse is closer to the top and bottom or the left and right, in order to determine whether the user is hovering over a horizontal or vertical line.
  4. You get the new position on the grid based on the is_horizontal variable.
  5. You initialize the variable board as either boardh or boardv, whichever is correct.
  6. Finally, you try drawing the hover line to the screen, taking into consideration whether it is horizontal or vertical and on the top, bottom, left or right. You also check if the line is out of bounds. If it is, or if the line has already been drawn, you don't draw the hover line.

Run the program and you get... drum roll, please... a map where a line lights up as your mouse moves over it!

If you're like me, you probably have your mouse whizzing across the board by now. Take some time to enjoy your results.

OK, now you have a grid that lights up when the player's mouse moves over a line. But this isn't a game where you just have to move your mouse around a bunch. You need to add the click-to-lay-down-line functionality.

To do this, you're going to use PyGame's built-in mouse function, which is simply pygame.mouse.get_pressed()[0]. The function returns either 1 or 0, depending on whether the mouse button is currently pressed down.

Before I tell you how to implement this in your game, try figuring it out yourself. Remember how you used if statements before and how to create a piece on the board.

[spoiler title="Solution"]Add this directly after the last block of code defining hover behavior:

if pygame.mouse.get_pressed()[0] and not alreadyplaced:
    if is_horizontal:
        self.boardh[ypos][xpos]=True
    else:
        self.boardv[ypos][xpos]=True

[/spoiler]

Run the program now and voilà! If you click, you place a line just where you were hovering. As you can see, the code you added checks if the mouse is pressed and if the line should be horizontal or vertical, and places the line accordingly.

Boxes7

One problem, though, is that if you click at the bottom of the screen (below where the boxes are drawn), the game crashes. Let's see why this is. When something crashes, usually it gives you an error report in the Terminal. In this case, the report looks like this:

Traceback (most recent call last):
  File "/Users/school/Desktop/Dropbox/boxes/WIPBoxes.py", line 103, in <module>
    bg.update()
  File "/Users/school/Desktop/Dropbox/boxes/WIPBoxes.py", line 69, in update
    self.boardh[ypos][xpos]=True
IndexError: list index out of range

This error is saying that the array boardh that you tried to access doesn't go as far as where you clicked. Remember that variable called isoutofbounds? That will come in handy here. Simply change this:

if pygame.mouse.get_pressed()[0] and not alreadyplaced:

#-----------to-------------

if pygame.mouse.get_pressed()[0] and not alreadyplaced and not isoutofbounds:

Now if you try clicking outside of the board, the game doesn't crash. Good job – you have just demonstrated the word debugging!

Before you begin implementing the game logic on the server side, let's first add some finishing touches to the client side.