RL Tutorial Parts 01 and 02 - Introduction and Graphics

The rest of this blog is going to go into extensive detail, but here is the complete code1 as of this tutorial:

Welcome to the first Blog post to support the video tutorial series. The goal of these posts is to provide easier access to the code examples for the game being designed in the youtube series, but also to cover the basic elements of the tutorial overall. Some caveats, the video series is less about 'coding' and more about how to make a roguelike structurally. Ideally, if you have some other language or frame work that you like to use, you'd be able to use this tutorial to make a roguelike in whatever language you're comfortable. This post covers both the first and second videos because the first episode has very little coding in it. I'm not going to walk through the step-by-step process for installing python here, but I will provide the list of links to get all the dependencies.

Prerequisites

If you’re looking to follow along with my code examples (as opposed to using whatever language/graphics framework you’re already comfortable with), the following represents a complete list of what you need. Before you start downloading things, as of writing this post, there have been issues getting libtcod working with python 64bit, so to be safe I recommend you download 32bit2. In addition, pygame is only built for 32bit at this time, so if you use 64bit it’s highly likely that you’ll get a bunch of errors. The vast-majority of build errors related to using these tools is due to using the wrong version.

  • Python version 2.7.XX [Available here]
  • The Doryen Library (libtcod) 1.6.X [Available here]
  • Pygame 1.9.1 for python 2.7 [Available here]
  • Some type of python editor or IDE, I use the text editor Sublime Text 3 [Available here]3. A better option for a newer programmer would be to use IDLE, which comes prepackaged with your python installation.

For most of these things, you simply use the intaller to install the library or program. The doryen library is a little different. A lot of people have trouble using libtcod because the main tutorial out there recommends putting the libtcodpy folder (available in the python folder of the downloaded zip file) into the project folder with your program. This tends to be hit or miss. Instead, I usually recommend copying the libtcodpy folder into the python directory where python is installed. The proper address would look something like this /Python Installation/Lib/site-packages/libtcodpy/

Once you’ve installed the libtcod library, you should copy the SDL2.dll and libtcod.dll files from the libtcod zip download into the project directory with your program.

Game Structure

There are a near infinite number of ways for you to split your project into different files (in python, they’re called modules), but in order to keep things simple we’re only going to have two:

  • main.py which will hold all function definitions and the game loop, itself
  • constants.py which will hold all ‘magic number’ defintions like screen width and height

On your own, you can decide how to split your project up if you want it to be more compartmentalized. But for now, having a file dedicated to easy to change variables that affect the whole game, and a file dedicated to the game itself, will be fine.

Getting Started Coding

The first thing we want to do is boot up our IDE and create our two files, main.py and constants.py. They can be empty for now.

A good practice to get into is to have both the files open on your screen somehow. Sublime Text uses a tab-based system, similar to a modern web-browser, which allows me to easily switch back and forth between project files. Whatever IDE you use will probably have it’s own way of dealing with multiple project files.

First, we’re going to import the required modules into our main.py file. Which is easy enough.

1
2
3
4
5
6
#3rd party modules
import libtcodpy as libtcod 
import pygame

#game files
import constants

A couple of things to note here, you probably noticed that we’re also going to import the empty constants file. We’re going to start adding to that file pretty soon so we might as well import it right now. It’s also important to touch on how the import function works, it sort of copy and pastes the module into the top of the file (it’s a bit more complicated than that, but close enough for our purposes). The import function will make the code for those modules available to us under their respective module names, this will be more obvious in the next part.

The first function we’re goint to write is going to be called game_initialize, and its job will be to do all the start-up business that allows our game to actually run. Things we put into this function will run only once, usually at startup.

1
2
3
4
5
6
7
8
9
def game_initialize():
    '''This function initializes the main window, and pygame'''

    global SURFACE_MAIN

    #initialize pygame
    pygame.init()

    SURFACE_MAIN = pygame.display.set_mode( (constants.GAME_WIDTH, constants.GAME_HEIGHT) )

Let’s unpack this function a little bit. First, we make a new global variable called SURFACE_MAIN. Global variables can be very useful, but they’re dangerous since they exist in every scope. My personal rule of thumb is to minimize using them where possible, but if I do need to use one, make it obvious. The way we do this is by using Google’s style guide regarding global variables. Basically make them all-caps and use the underscore.

The next thing the game_initialize function does is call the init function from within the pygame module. This function prepares the modules for use in your program. If you want a more thorough introduction to the purpose of init you should check the pygame documentation.

The last thing that our game_initialize function does is create a new surface and assigns our global variable to it. This creates what is called the ‘display surface’ which is a special kind of surface.

About Pygame Surfaces

Surfaces are probably the most misunderstood part of pygame for newer programmers. A surface represents any visual element that can be drawn, even text!

The display surface, which we create with the pygame.display.set_mode function above, is a special surface that actually gets displayed to the player through the game window. All other surfaces, at one point or another, are drawn onto the display surface.

The process of drawing one surface onto another is referred to as blitting. In order for anything we draw to be displayed to the player, we’ll need to blit it onto the display surface. More on this below.

The pygame.display.set_mode function takes a tuple, which is a type of data structure, in this case it wants two integers, a game width and height in pixels. Here we pass in our first constant from the constants module.

Which brings us to the constants.py file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  
import pygame

pygame.init()

#Game sizes
GAME_WIDTH = 800
GAME_HEIGHT = 600

#Color definitions
COLOR_BLACK = (0, 0, 0)
COLOR_WHITE = (255, 255, 255)
COLOR_GREY = (100, 100, 100)

#Game colors
COLOR_DEFAULT_BG = COLOR_GREY

#SPRITES
S_PLAYER = pygame.image.load("data/python.png")

Let’s go through this line by line. First we’re going to import pygame and then intialize it (lines 2 and 4). We need to do this because we’re going to create a constant that is a pygame surface so we can change it later if we want to.

Next (on lines 7 and 8), we define the two values we’re using in the pygame.display.set_mode function in the game_initialize function of our main.py document. By setting GAME_WIDTH to 800 and GAME_HEIGHT to 600, our game display will be 800 pixels wide and 600 pixels high.

Next we’re going to define some color presets. these are tuples and are integers representing the RGB format. You can use an online color generator to make up your own colors, but for now we don’t need anything too complicated.

On line 16, we’re going to define a ‘game color’. A color definition is just us defining the colors, like black or white. By creating a variable called COLOR_DEFAULT_BG and assigning a color to it, if we later decide we want all the backgrounds to be white instead of grey, we only need to change it in one place.

Finally, on line 19 we’re creating a sprite using the pygame.image.load function. You can use any image you like, the one I’m using there is a sprite I converted from the Dawnlike tileset. Feel free to download a sprite you want to use, or you can use the one I’m using4.

Like I mentioned above, any visual element in pygame is a surface. So we’ll need to draw this sprite onto the display surface whenever we want to show a representation of where the player is. More on that later.

The Last Two Functions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
  
def game_draw():
    '''This function draws the game'''
    #clear the surface
    SURFACE_MAIN.fill(constants.COLOR_DEFAULT_BG)

    #TODO draw the map

    #draw the character
    SURFACE_MAIN.blit(constants.S_PLAYER, ( 200, 200 ))

    #update the display
    pygame.display.flip()
 
def game_main_loop():
    '''In this function, we loop the main game'''
    game_quit = False

    while not game_quit:
        
        #get player input
        events_list = pygame.event.get()

        #TODO process input
        for event in events_list:
            if event.type == pygame.QUIT:
                game_quit = True

        #draw the game
        draw_game()

    #quit the game
    pygame.quit()
    exit()

Ignore the game_draw function for now and let’s talk about the game_main_loop function. This function incorporates a while loop, so the first thing we do is define a way of breaking out of it. By creating a game_quit variable and setting it to false, making our while loop conditional on game_quit staying false gives us an easy way to break out. Which we do if the player pressues the escape button at the upper right of the display window.

In the while loop, the first thing we do is process player input. The events_list = pygame.event.get function returns a list of every thing the player has done since the last time the loop ran. Any keys that were pushed, if the player resizes the screen (which they can’t do), etc. For now, all we do is loop through every event in that list and look to see if the player exited. If they did, we break the loop.

Next in the while loop, we simply call the draw_game function. We’ll define this next.

Finally, if the loop is broken, we want the game to close. We start by calling the pygame.quit function, and then the exit function. Calling these functions, as opposed to simply letting the program end, ensures that everything closes out properly and we don’t have any resources being tied up for no reason.

Next, the draw function.

This function is actually pretty simple. first, we clear the surface by filling it with our default background color. For now we have a comment that we want to draw a map, which we’ll do in another tutorial. Next, we draw the character by blitting our character sprite onto the display surface, SURFACE_MAIN. Finally, we update the display surface with the pygame.display.flip function, which updates the screen to show what we’ve drawn on SURFACE_MAIN.

Finishing Up

Finally, the last thing we need to do are actually call the functions game_initialize and game_main_loop, whereby we’ll run the actual game. This is done simply by adding a couple of lines to the bottom of our main.py file.

1
2
3
4
5
 
##EXECUTE GAME##
if __name__ == '__main__':
    game_initialize()
    game_main_loop()

For now we’re going to ignore the if statement5. By calling these two functions, we’re executing all the code we’ve written up until now. If you’ve followed along (or copied the code from the links at the top of the page), when you run main.py you should get something that looks like this:

resultimage

And that’s everything we’re going to cover this time, next we’ll talk about the map system and how we make everything up of tiles.




Footnotes

  1. These are html pages, so if you want to use them, don’t right-click and ‘save-as’ instead, go to the page and copy and paste. 

  2. If your system is a 64bit system, don’t worry, you can use 32bit installations without a problem. 

  3. Sublime Text is a paid program. You can download and use it to try it out, similar to winrar. But it’s a fantastic program that deserves your support if you intend to use it regularly. The price is a little steep ($70), but a small price to pay if you can swing it. 

  4. The original tileset is called the Dawnlike tileset created by DragonDePlatino and DawnBringer (who created the color palette). So far I’ve only used this tile, but it’s possible I’ll use more. The original set is in 16x16, but this one has been enlarged to 32x32. My edited tile is [Available here]. DrawnLike, the project, lives [here]. 

  5. In truth, you could safely leave this out of the program and probably be fine, however, it’s a good idea to get into the habit of wrapping the execution of your main function up in this if-statement because it prevents that code from running if you were to import this file as a module into a different python program. If all of that seems scary to you, simply take it out if you want (remember to unindent the game_initialize and game_main_loop functions) or ignore it. If you want a more in depth explaination of why python programmers use this, check out this explaination on stack exchange. 

Join My Newsletter

Sign up to receive weekly updates.

x