DOKK Library

A guide to building a video game in Python

Authors Jess Weichler Seth Kenlon

License CC-BY-SA-4.0

Plaintext
       Opensource.com




  A guide to building
a video game in Python
OPENSOURCE.COM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .




ABOUT OPENSOURCE.COM



                     What is Opensource.com?

                    OPENSOURCE.COM                            publishes stories about creating,
                                                              adopting, and sharing open source
                     solutions. Visit Opensource.com to learn more about how the open source
                     way is improving technologies, education, business, government, health, law,
                     entertainment, humanitarian efforts, and more.

                     Submit a story idea: opensource.com/story

                     Email us: open@opensource.com




      2                                  A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                       . CC BY-SA 4.0 . OPENSOURCE.COM
     . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ABOUT THE AUTHORS




SETH KENLON


               SETH KENLON                     is an independent multimedia artist, free culture
                                               advocate, and UNIX geek. He has worked in the film
               and computing industry, often at the same
               time. He is one of the maintainers of the
               Slackware-based multimedia production
               project, http://slackermedia.info.




JESS WEICHLER


               JESS WEICHLER                       Jess Weichler is a digital artist using open
                                                   source software and hardware to create
               works digitally and in the physical world at CyanideCupcake.com.

               She is also an award-winning educator
               for (and founder of) MakerBox.org.nz
               an organization that teaches kids of
               all ages how to use technology, from
               sewing needles to Arduinos, to make
               their ideas a reality.

               Follow Jess at @jlweich




     A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                    . CC BY-SA 4.0 . OPENSOURCE.COM                                                3
CONTENTS . .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .


                                                 The code for this booklet can be found here:
                                                           https://gitlab.com/makerbox/scratch2python


CHAPTERS

                  Learn how to program in Python by building a simple                                                                                                                                            5
                  dice game
                  Build a game framework with Python using the                                                                                                                                                10
                  Pygame module
                  How to add a player to your Python game                                                                                                                                                     15
                  Using Pygame to move your game character around                                                                                                                                             19
                  What’s a hero without a villain? How to add one to your                                                                                                                                     24
                  Python game
                  Put platforms in a Python game with Pygame                                                                                                                                                  29
                  Simulate gravity in your Python game                                                                                                                                                        37
                  Add jumping to your Python platformer game                                                                                                                                                  41
                  Enable your Python game player to run forward                                                                                                                                               47
                  and backward
                  Put some loot in your Python platformer game                                                                                                                                                52
                  Add scorekeeping to your Python game                                                                                                                                                        57
                  Add throwing mechanics to your Python game                                                                                                                                                  65
                  Add sound to your Python game                                                                                                                                                               72


APPENDICES

                  How to install Python on Windows                                                                                                                                                            74
                  Managing Python packages the right way                                                                                                                                                      77
                  Easily set image transparency using GIMP                                                                                                                                                    80


          4                                                     A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                                                            . CC BY-SA 4.0 . OPENSOURCE.COM
. . . . . . . . . . . . . . . . . . . . LEARN HOW TO PROGRAM IN PYTHON BY BUILDING A SIMPLE DICE GAME




Learn how to program in Python
by building a simple dice game
Python is a good language for young and old, with or without any programming experience.


PYTHON               [1] is an all-purpose programming lan-
                     guage that can be used to create desktop
applications, 3D graphics, video games, and even websites.
                                                                  Windows: Microsoft Windows doesn’t currently ship with
                                                                Python. Install it from python.org/downloads/windows [3]. Be
                                                                sure to select Add Python to PATH in the install wizard.
It’s a great first programming language because it can be       Read my article How to Install Python on Windows [4] for
easy to learn and it’s simpler than complex languages like      instructions specific to Microsoft Windows.
C, C++, or Java. Even so, Python is powerful and robust
enough to create advanced applications, and it’s used in just   Running an IDE
about every industry that uses computers. This makes Py-        To write programs in Python, all you really need is a text editor,
thon a good language for young and old, with or without any     but it’s convenient to have an integrated development environ-
programming experience.                                         ment (IDE). An IDE integrates a text editor with some friendly
                                                                and helpful Python features. IDLE 3 and PyCharm (Communi-
Installing Python                                               ty Edition) are two options among many [5] to consider.
Before learning Python, you may need to install it.
  Linux: If you use Linux, Python is already included,          IDLE 3
but make sure that you have Python 3 specifically. To           Python comes with a basic IDE called IDLE.
check which version is installed, open a terminal window
and type:

python --version


Should that reveal that you have version 2 installed, or no
version at all, try specifying Python 3 instead:

python3 --version


If that command is not found, then you must install Python 3
from your package manager or software center. Which pack-
age manager your Linux distribution uses depends on the
distribution. The most common are dnf on Fedora and apt
on Ubuntu. For instance, on Fedora, you type this:

sudo dnf install python3


MacOS: If you’re on a Mac, follow the instructions for Li-
nux to see if you have Python 3 installed. MacOS does not
have a built-in package manager, so if Python 3 is not found,
install it from python.org/downloads/mac-osx [2]. Although
your version of macOS may already have Python 2 installed,
you should learn Python 3.                                      IDLE



A GUIDE TO BUILDING A VIDEO GAME IN PYTHON             . CC BY-SA 4.0 . OPENSOURCE.COM                                          5
LEARN HOW TO PROGRAM IN PYTHON BY BUILDING A SIMPLE DICE GAME . . . . . . . . . . . . . . . . . . . .


    It has keyword highlighting to help detect typing errors, hints
    for code completion, and a Run button to test code quickly
    and easily. To use it:

    • O
       n Linux or macOS, launch a terminal window and type
      idle3.
    • O
       n Windows, launch Python 3 from the Start menu.
      • If you don’t see Python in the Start menu, launch the
         Windows command prompt by typing cmd in the Start
         menu, and type C:\Windows\py.exe.
      • If that doesn’t work, try reinstalling Python. Be sure to
         select Add Python to PATH in the install wizard. Refer
         to docs.python.org/3/using/windows.html [6] for detailed
         instructions.
      • If that still doesn’t work, just use Linux. It’s free and,
         as long as you save your Python files to a USB thumb          opensource.com
         drive, you don’t even have to install it to use it.           The keyword print tells Python to print out whatever text you
                                                                       give it in parentheses and quotes.
    PyCharm Community Edition                                             That’s not very exciting, though. At its core, Python has
    PyCharm (Community Edition) IDE [7] is an excellent                access to only basic keywords, like print, help, basic math
    open source Python IDE. It has keyword highlighting to             functions, and so on.
    help detect typos, quotation and parenthesis completion               You can use the import keyword to load more keywords.
    to avoid syntax errors, line numbers (helpful when debug-             Turtle is a fun module to use. Type this code into your file
    ging), indentation markers, and a Run button to test code          (replacing the old code), and then run it:
    quickly and easily.
      To use it:                                                       import turtle


    1. Install PyCharm (Community Edition) IDE. On Linux, it’s        turtle.begin_fill()
        easiest to install it with Flatpak [8]. Alternatively, down-   turtle.forward(100)
        load [9] the correct installer version from PyCharm’s          turtle.left(90)
        website and install it manually [10]. On MacOS or Win-         turtle.forward(100)
        dows, download and run the installer from the PyCharm          turtle.left(90)
        website [11].                                                  turtle.forward(100)
    2. L
        aunch PyCharm.                                                turtle.left(90)
    3. Create a new project.                                           turtle.forward(100)
                                                                       turtle.end_fill()
    Telling Python what to do
    Keywords tell Python what you want it to do. In your new           See what shapes you can draw with the turtle module.
    project file, type this into your IDE:                               To clear your turtle drawing area, use the turtle.clear()
                                                                       keyword. What do you think the keyword turtle.color("blue")
    print("Hello world.")                                              does?

    • If you are using IDLE, go to the Run menu and select Run        Advanced turtle
       module option.                                                  You can try some more complex code for similar results. In-
    • If you are using PyCharm, click the Run File button in the      stead of hand-coding every line and every turn, you can use
       left button bar.                                                a while loop, telling Python to do this four times: draw a
                                                                       line and then turn. Python is able to keep track of how many
                                                                       times it’s performed these actions with a variable called
                                                                       counter. You’ll learn more about variables soon, but for now
                                                                       see if you can tell how the counter and while loop interact.

                                                                       import turtle as t
                                                                       import time




    6                             A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                 . CC BY-SA 4.0 . OPENSOURCE.COM
. . . . . . . . . . . . . . . . . . . . LEARN HOW TO PROGRAM IN PYTHON BY BUILDING A SIMPLE DICE GAME



t.color("blue")                                                       A variable is a value that is subject to change, and they
t.begin_fill()                                                      are used a lot in Python. Whenever you need your program
                                                                    to “remember” something, you use a variable. In fact, al-
counter=0                                                           most all the information that code works with is stored in
                                                                    variables. For example, in the math equation x + 5 = 20,
while counter < 4:                                                  the variable is x, because the letter x is a placeholder for
    t.forward(100)                                                  a value.
    t.left(90)                                                        An integer is a number; it can be positive or negative.
    counter = counter+1                                             For example, 1 and -1 are both integers. So are 14, 21, and
                                                                    even 10,947.
t.end_fill()                                                          Variables in Python are easy to create and easy to work
time.sleep(2)                                                       with. This initial version of the dice game uses two variables:
                                                                    player and ai.
Once you have run your script, it’s time to explore an even           Type the following code into a new project called dice_
better module.                                                      alpha:

Learning Python by making a game                                    import random
To learn more about how Python works and prepare for more
advanced programming with graphics, let’s focus on game             player = random.randint(1,6)
logic. In this tutorial, we’ll also learn a bit about how comput-   ai = random.randint(1,6)
er programs are structured by making a text-based game in
which the computer and the player roll a virtual die, and the       if player > ai :
one with the highest roll wins.                                         print("You win")    # notice indentation
                                                                    else:
Planning your game                                                      print("You lose")
Before writing code, it’s important to think about what
you intend to write. Many programmers write simple doc-             Launch your game to make sure it works.
umentation [12] before they begin writing code, so they               This basic version of your dice game works pretty well. It
have a goal to program toward. Here’s how the dice pro-             accomplishes the basic goals of the game, but it doesn’t feel
gram might look if you shipped documentation along with             much like a game. The player never knows what they rolled
the game:                                                           or what the computer rolled, and the game ends even if the
                                                                    player would like to play again.
1. Start the dice game and press Return or Enter to roll.            This is common in the first version of software (called an
2. The results are printed out to your screen.                      alpha version). Now that you are confident that you can ac-
3. You are prompted to roll again or to quit.                       complish the main part (rolling a die), it’s time to add to the
                                                                    program.
It’s a simple game, but the documentation tells you a lot
about what you need to do. For example, it tells you that you       Improving the game
need the following components to write this game:                   In this second version (called a beta) of your game, a few
                                                                    improvements will make it feel more like a game.
• P layer: You need a human to play the game.
• AI: The computer must roll a die, too, or else the player has    1. Describe the results
   no one to win or lose to.                                        Instead of just telling players whether they did or didn’t win,
• Random number: A common six-sided die renders a ran-             it’s more interesting if they know what they rolled. Try making
   dom number between 1 and 6.                                      these changes to your code:
• Operator: Simple math can compare one number to anoth-
   er to see which is higher.                                       player = random.randint(1,6)
• A win or lose message.                                           print("You rolled " + player)
• A prompt to play again or quit.
                                                                    ai = random.randint(1,6)
Making dice game alpha                                              print("The computer rolled " + ai)
Few programs start with all of their features, so the first
version will only implement the basics. First a couple of           If you run the game now, it will crash because Python thinks
definitions:                                                        you’re trying to do math. It thinks you’re trying to add the



A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                 . CC BY-SA 4.0 . OPENSOURCE.COM                                       7
LEARN HOW TO PROGRAM IN PYTHON BY BUILDING A SIMPLE DICE GAME . . . . . . . . . . . . . . . . . . . .


    letters “You rolled” and whatever number is currently stored       Modify your code like this:
    in the player variable.
       You must tell Python to treat the numbers in the player and   if player > ai :
    ai variables as if they were a word in a sentence (a string)         print("You win")     # notice indentation
    rather than a number in a math equation (an integer).            elif player == ai:
       Make these changes to your code:                                  print("Tie game.")
                                                                     else:
    player = random.randint(1,6)                                         print("You lose")
    print("You rolled " + str(player) )
                                                                     Launch your game a few times to try to tie the computer’s
    ai = random.randint(1,6)                                         roll.
    print("The computer rolled " + str(ai) )
                                                                     Programming the final release
    Run your game now to see the result.                             The beta release of your dice game is functional and feels
                                                                     more like a game than the alpha. For the final release, create
    2. Slow it down                                                  your first Python function.
    Computers are fast. Humans sometimes can be fast, but              A function is a collection of code that you can call upon as
    in games, it’s often better to build suspense. You can use       a distinct unit. Functions are important because most appli-
    Python’s time function to slow your game down during the         cations have a lot of code in them, but not all of that code
    suspenseful parts.                                               has to run at once. Functions make it possible to start an
                                                                     application and control what happens and when.
    import random                                                      Change your code to this:
    import time
                                                                     import random
    player = random.randint(1,6)                                     import time
    print("You rolled " + str(player) )
                                                                     def dice():
    ai = random.randint(1,6)                                             player = random.randint(1,6)
    print("The computer rolls...." )                                     print("You rolled " + str(player) )
    time.sleep(2)
    print("The computer has rolled a " + str(player) )                   ai = random.randint(1,6)
                                                                         print("The computer rolls...." )
    if player > ai :                                                     time.sleep(2)
        print("You win")    # notice indentation                         print("The computer has rolled a " + str(ai) )
    else:
        print("You lose")                                                if player > ai :
                                                                             print("You win")     # notice indentation
    Launch your game to test your changes.                               else:
                                                                             print("You lose")
    3. Detect ties
    If you play your game enough, you’ll discover that even              print("Quit? Y/N")
    though your game appears to be working correctly, it actual-         continue = input()
    ly has a bug in it: It doesn’t know what to do when the player
    and the computer roll the same number.                               if continue == "Y" or continue == "y":
       To check whether a value is equal to another value, Py-               exit()
    thon uses ==. That’s two equal signs, not just one. If you           elif continue == "N" or continue == "n":
    use only one, Python thinks you’re trying to create a new                pass
    variable, but you’re actually trying to do math.                     else:
       When you want to have more than just two options (i.e.,               print("I did not understand that. Playing again.")
    win or lose), you can using Python’s keyword elif, which
    means else if. This allows your code to check to see whether     This version of the game asks the player whether they want
    any one of some results are true, rather than just checking      to quit the game after they play. If they respond with a Y or y,
    whether one thing is true.                                       Python’s exit function is called and the game quits.




    8                               A GUIDE TO BUILDING A VIDEO GAME IN PYTHON              . CC BY-SA 4.0 . OPENSOURCE.COM
. . . . . . . . . . . . . . . . . . . . LEARN HOW TO PROGRAM IN PYTHON BY BUILDING A SIMPLE DICE GAME



   More importantly, you’ve created your own function called       stays open long enough for the computer user to use func-
dice. The dice function doesn’t run right away. In fact, if you    tions within the application.
try your game at this stage, it won’t crash, but it doesn’t ex-
actly run, either. To make the dice function actually do some-     Next steps
thing, you have to call it in your code.                           Now you know the basics of Python programming. The next
   Add this loop to the bottom of your existing code. The first    article in this series describes how to write a video game with
two lines are only for context and to emphasize what gets in-      PyGame [13], a module that has more features than turtle,
dented and what does not. Pay close attention to indentation.      but is also a lot more complex.

    else:
        print("I did not understand that. Playing again.")         Links
                                                                   [1] https://www.python.org/
# main loop                                                        [2] https://www.python.org/downloads/mac-osx/
while True:                                                        [3] https://www.python.org/downloads/windows
    print("Press return to roll your die.")                        [4]	 https://opensource.com/article/19/8/how-install-python-
    roll = input()                                                       windows
    dice()                                                         [5] https://opensource.com/resources/python/ides
                                                                   [6] https://docs.python.org/3/using/windows.html
The while True code block runs first. Because True is al-          [7]	 https://www.jetbrains.com/pycharm/download
ways true by definition, this code block always runs until Py-     [8]	 https://flathub.org/apps/details/com.jetbrains.PyCharm-
thon tells it to quit.                                                   Community
   The while True code block is a loop. It first prompts the       [9] https://www.jetbrains.com/pycharm/
user to start the game, then it calls your dice function. That’s         download/#section=linux
how the game starts. When the dice function is over, your          [10]	https://opensource.com/article/18/1/how-install-apps-linux
loop either runs again or it exits, depending on how the play-     [11]	https://www.jetbrains.com/pycharm/download
er answered the prompt.                                            [12]	https://opensource.com/article/17/8/doc-driven-
   Using a loop to run a program is the most common way                  development
to code an application. The loop ensures that the application      [13]	https://www.pygame.org/news




A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                . CC BY-SA 4.0 . OPENSOURCE.COM                                         9
BUILD A GAME FRAMEWORK WITH PYTHON USING THE PYGAME MODULE . . . . . . . . . . . . . . . . . . . .




    Build a game framework with
    Python using the Pygame module
    The first part of this series explored Python by creating a simple dice game. Now it’s time to make
    your own game from scratch.


   IN MY FIRST                 ARTICLE in this series, I explained
                               how to use Python to create a sim-
    ple, text-based dice game. You also used the Turtle module to
                                                                      ground, so don’t put anything too important back there. It’s
                                                                      just set dressing.

    generate some simple graphics. In this article, you start using   Setting up your first Pygame script
    a module called Pygame to create a game with graphics.            To start a new Python project, you would normally create
       The Turtle module is included with Python by default. Any-     a new folder on your computer and place all your game
    one who’s got Python installed also has Turtle. The same is       files go into this directory. It’s vitally important that you
    not true for an advanced module like Pygame. Because it’s         keep all the files needed to run your game inside of your
    a specialized code library, you must install Pygame yourself.     project folder.
    Modern Python development uses the concept of virtual en-            PyCharm (or whatever IDE you use) can do this for you.
    vironments, which provides your Python code its own space            To create a new project in PyCharm, navigate to the File
    to run in, and also helps manage which code libraries your        menu and select New Project. In the window that appears,
    application uses. This ensures that when you pass your Py-        enter a name for your project (such as game_001.) Notice
    thon application to another user to play, you know exactly        that this project is saved into a special PycharmProjects
    what they need to install to make it work.                        folder in your home directory. This ensures that all the files
       You can manage your Python virtual environment man-            your game needs stays in one place.
    ually, or you can let your IDE help you. For now, you can            When creating a new project, let PyCharm create a new
    let PyCharm do all the work. If you’re not using PyCharm,         environment using Virtualenv, and accept all defaults. En-
    read László Kiss Kollár’s article about managing Python           able the option to create a main.py file (and to set up some
    packages [1].                                                     important defaults.)

    Getting started with Pygame
    Pygame is a library, or Python module. It’s a collection of
    common code that prevents you from having to reinvent the
    wheel with every new game you write. You’ve already used
    the Turtle module, and you can imagine how complex that
    could have been if you’d had to write the code to create a
    pen before drawing with it. Pygame offers similar advantag-
    es, but for video games.
      A video game needs a setting, a world in which it takes
    place. In Pygame, there are two different ways to create your
    setting:

    • Set a background color                                          opensource.com
    • Set a background image                                          After you’ve clicked the Create button, a new project ap-
                                                                      pears in your PyCharm window. The project consists of a vir-
    Either way, your background is only an image or a color. Your     tual environment (the venv directory listed in the left column)
    video game characters can’t interact with things in the back-     and a demo file called main.py.



    10                            A GUIDE TO BUILDING A VIDEO GAME IN PYTHON              . CC BY-SA 4.0 . OPENSOURCE.COM
. . . . . . . . . . . . . . . . . . . . BUILD A GAME FRAMEWORK WITH PYTHON USING THE PYGAME MODULE



  Delete all the contents of main.py so you can enter your         ments that Python ignores, but they’re good placeholders
own custom code. A Python script starts with the file type,        for you as you follow along with these lessons. I still use
your name, and the license you want to use. Use an open            placeholders when I code, because it helps me organize
source license so your friends can improve your game and           and plan.
share their changes with you:
                                                                   '''
#!/usr/bin/env python3                                             Variables
# by Seth Kenlon                                                   '''


## GPLv3                                                           # put variables here
# This program is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 3 of   '''
# the License, or (at your option) any later version.              Objects
#                                                                  '''
# This program is distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY; without even the implied       # put Python classes and functions here
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
# PURPOSE. See the GNU General Public License for more details.
#                                                                  '''
# You should have received a copy of the GNU General Public        Setup
# License along with this program.    If not, see                  '''
# <http://www.gnu.org/licenses/>.
                                                                   # put run-once code here
Then tell Python what modules you want to use. In this
code sample, you don’t have to type the # character or
anything after it on each line. The # character in Python          '''
represents a comment, notes in code left for yourself and          Main Loop
other coders.                                                      '''


import pygame   # load pygame keywords                             # put game loop here
import sys      # let    python use your file system
import os       # help python identify your OS                     Next, set the window size for your game. Keep in mind
                                                                   that not everyone has a big computer screen, so it’s
Notice that PyCharm doesn’t understand what Pygame is,             best to use a screen size that fits on “most” people’s
and underlines it to mark it as an error. This is because Pyg-     computers.
ame, unlike sys and os, isn’t included with Python by default.       There is a way to toggle full-screen mode, the way many
You need to include Pygame in your project directory before        modern video games do, but since you’re just starting out,
you can use it successfully in code. To include it, hover your     keep it simple and just set one size.
mouse over the word pygame until you see a notification
popup explaining the error.                                        '''
   Click the Install package pygame link in the alert box,         Variables
and wait for PyCharm to install Pygame into your virtual en-       '''
vironment.                                                         worldx = 960
   Once it’s installed, the error disappears.                      worldy = 720
   Without an IDE like PyCharm, you can install Pygame into
your virtual environment manually using the pip command.           The Pygame engine requires some basic setup before you
                                                                   can use it in a script. You must set the frame rate, start its
Code sections                                                      internal clock, and start (using the keyword init, for initial-
Because you’ll be working a lot with this script file, it helps    ize) Pygame.
to make sections within the file so you know where to put             Add these variables:
stuff. You do this with block comments, which are com-
ments that are visible only when looking at your source            fps     = 40   # frame rate
code. Create four blocks in your code. These are all com-          ani     = 4    # animation cycles




A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                  . CC BY-SA 4.0 . OPENSOURCE.COM                                    11
BUILD A GAME FRAMEWORK WITH PYTHON USING THE PYGAME MODULE . . . . . . . . . . . . . . . . . . . .


    Add instructions to start Pygame’s internal clock in the Setup     Look out for errors
    section:                                                           PyCharm is strict, and that’s pretty typical for programming.
                                                                       Syntax is paramount! As you enter your code into PyCharm,
    '''                                                                you see warnings and errors. The warnings are yellow and
    Setup                                                              the errors are red.
    '''                                                                   PyCharm can be over-zealous sometimes, though, so it’s
                                                                       usually safe to ignore warnings. You may be violating the
    clock = pygame.time.Clock()                                        “Python style”, but these are subtle conventions that you’ll
    pygame.init()                                                      learn in time. Your code will still run as expected.
    Now you can set your background.                                      Errors, on the other hand, prevent your code (and some-
                                                                       times PyCharm) from doing what you expect. For instance,
    Setting the background                                             PyCharm is very insistent that there’s a newline character
    Before you continue, open a graphics application and cre-          at the end of the last line of code, so you must press Enter
    ate a background for your game world. Save it as stage.            or Return on your keyboard at the end of your code. If you
    png inside a folder called images in your project directory.       don’t, PyCharm quietly refuses to run your code.
    If you need a starting point, you can download a set of Cre-
    ative Commons [2] backgrounds from kenny.nl [3].                   Running the game
       There are several free graphic applications you can use to      At this point, you could theoretically start your game. The
    create, resize, or modify graphics for your games.                 problem is, it would only last for a millisecond.
                                                                         To prove this, save and then run your game.
    • P
       inta [4] is a basic, easy to learn paint application.            If you are using IDLE, run your game by selecting Run
    • K rita [5] is a professional-level paint materials emulator     Module from the Run menu.
      that can be used to create beautiful images. If you’re very        If you are using PyCharm, click the Run file button in the
      interested in creating art for video games, you can even         top right toolbar.
      purchase a series of online game art tutorials [6].
    • Inkscape [7] is a vector graphics application. Use it to draw
       with shapes, lines, splines, and Bézier curves.

    Your graphic doesn’t have to be complex, and you can al-
    ways go back and change it later. Once you have a back-
    ground, add this code in the setup section of your file:

    world = pygame.display.set_mode([worldx,worldy])
    backdrop = pygame.image.load(os.path.join('images','stage.png'))
    backdropbox = world.get_rect()


    If you’re just going to fill the background of your game world
    with a color, all you need is:

    world = pygame.display.set_mode([worldx, worldy])                  opensource.com

    You also must define a color to use. In your setup section,        You can also run a Python script straight from a Unix terminal
    create some color definitions using values for red, green,         or a Windows command prompt, as long as you’re in your
    and blue (RGB).                                                    virtual environment.
                                                                          However you launch it, don’t expect much, because your
    '''                                                                game only lasts a few milliseconds right now. You can fix that
    Variables                                                          in the next section.
    '''
                                                                       Looping
    BLUE    = (25, 25, 200)                                            Unless told otherwise, a Python script runs once and only
    BLACK = (23, 23, 23)                                               once. Computers are very fast these days, so your Python
    WHITE = (254, 254, 254)                                            script runs in less than a second.
                                                                         To force your game to stay open and active long enough
                                                                       for someone to see it (let alone play it), use a while loop.



    12                               A GUIDE TO BUILDING A VIDEO GAME IN PYTHON           . CC BY-SA 4.0 . OPENSOURCE.COM
. . . . . . . . . . . . . . . . . . . . BUILD A GAME FRAMEWORK WITH PYTHON USING THE PYGAME MODULE



To make your game remain open, you can set a variable to        Freeze your Python environment
some value, then tell a while loop to keep looping for as       PyCharm is managing your code libraries, but your users
long as the variable remains unchanged.                         aren’t going to run your game from PyCharm. Just as you
  This is often called a “main loop,” and you can use the       save your code file, you also need to preserve your virtual
term main as your variable. Add this anywhere in your Vari-     environment.
ables section:                                                      Go to the Tools menu and select Sync Python Require-
                                                                ments. This saves your library dependencies to a special
main = True                                                     file called requirements.txt. The first time you sync your re-
                                                                quirements, PyCharm prompts you to install a plugin and to
During the main loop, use Pygame keywords to detect if          add dependencies. Click to accept these offers.
keys on the keyboard have been pressed or released. Add
this to your main loop section:

'''
Main loop
'''


while main:
      for event in pygame.event.get():
          if event.type == pygame.QUIT:
              pygame.quit()                                     opensource.com
              try:
                     sys.exit()                                 A requirements.txt is generated for you, and placed into
              finally:                                          your project directory.
                   main = False
                                                                Code
          if event.type == pygame.KEYDOWN:                      Here’s what your code should look like so far:
              if event.key == ord('q'):
                   pygame.quit()                                #!/usr/bin/env python3
              try:                                              # by Seth Kenlon
                     sys.exit()
              finally:                                          # GPLv3
                   main = False                                 # This program is free software: you can redistribute it and/or
                                                                # modify it under the terms of the GNU General Public License as
Be sure to press Enter or Return after the final line of your   # published by the Free Software Foundation, either version 3 of
code so there’s an empty line at the bottom of your file.       # the License, or (at your option) any later version.
  Also in your main loop, refresh your world’s background.      #
  If you are using an image for the background:                 # This program is distributed in the hope that it will be useful,
                                                                # but WITHOUT ANY WARRANTY; without even the implied warranty of
world.blit(backdrop, backdropbox)                               # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.   See the
                                                                # GNU General Public License for more details.
If you are using a color for the background:                    #
                                                                # You should have received a copy of the GNU General Public
world.fill(BLUE)                                                # License along with this program.   If not, see
                                                                # <http://www.gnu.org/licenses/>.
Finally, tell Pygame to refresh everything on the screen and
advance the game’s internal clock.                              import pygame
                                                                import sys
      pygame.display.flip()                                     import os
      clock.tick(fps)
                                                                '''
Save your file, and run it again to see the most boring game    Variables
ever created.                                                   '''
  To quit the game, press q on your keyboard.



A GUIDE TO BUILDING A VIDEO GAME IN PYTHON             . CC BY-SA 4.0 . OPENSOURCE.COM                                             13
BUILD A GAME FRAMEWORK WITH PYTHON USING THE PYGAME MODULE . . . . . . . . . . . . . . . . . . . .


    worldx = 960                                                        while main:
    worldy = 720                                                           for event in pygame.event.get():
    fps = 40    # frame rate                                                    if event.type == pygame.QUIT:
    ani = 4     # animation cycles                                                    pygame.quit()
    main = True                                                                       try:
                                                                                          sys.exit()
    BLUE = (25, 25, 200)                                                              finally:
    BLACK = (23, 23, 23)                                                                  main = False
    WHITE = (254, 254, 254)
                                                                                if event.type == pygame.KEYDOWN:
                                                                                      if event.key == ord('q'):
    '''                                                                                   pygame.quit()
    Objects                                                                           try:
    '''                                                                                   sys.exit()
                                                                                      finally:
    # put Python classes and functions here                                               main = False
                                                                           world.blit(backdrop, backdropbox)
                                                                           pygame.display.flip()
    '''                                                                    clock.tick(fps)
    Setup
    '''                                                                 What to do next
                                                                        In the next article of this series, I’ll show you how to add to
    clock = pygame.time.Clock()                                         your currently empty game world, so start creating or finding
    pygame.init()                                                       some graphics to use!
    world = pygame.display.set_mode([worldx, worldy])
    backdrop = pygame.image.load(os.path.join('images', 'stage.png'))   Links
    backdropbox = world.get_rect()                                      [1]	https://opensource.com/article/19/4/managing-python-
                                                                             packages
                                                                        [2] https://opensource.com/article/20/1/what-creative-
    '''                                                                      commons
    Main Loop                                                           [3]	https://kenney.nl/assets/background-elements-redux
    '''                                                                 [4] https://www.pinta-project.com/
                                                                        [5] http://krita.org/
                                                                        [6] https://gumroad.com/l/krita-game-art-tutorial-1
                                                                        [7]	http://inkscape.org/




    14                               A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                  . CC BY-SA 4.0 . OPENSOURCE.COM
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . HOW TO ADD A PLAYER TO YOUR PYTHON GAME




How to add a player to your
Python game
Part three of a series on building a game from scratch with Python.



IN THE FIRST                   ARTICLE of this series, I explained
                               how to use Python to create a
simple, text-based dice game. In the second part, I showed
                                                                     starting with hero1.png. Save you hero image into a directory
                                                                     called images in your Python project directory.

you how to build a game from scratch, starting with creating         Create a Python class
the game’s environment [1]. But every game needs a player,           In Python, when you create an object that you want to ap-
and every player needs a playable character, so that’s what          pear on screen, you create a class.
you’ll do next in this third part of the series.                       Near the top of your Python script, in the Objects section,
   In Pygame, the icon or avatar that a player controls is           add the code to create a player. If you’re using a static im-
called a sprite. If you don’t have any graphics to use for a         age with no walk cycle, use this code (note that this code
player sprite yet, download the walk-0.png, walk-2.png,              goes in the Objects section of your file):
walk-4.png, and walk-5.png files [2] from the classic open
source game Supertux [3] and rename them hero1.png to                '''
hero4.png Alternately, you can create something for yourself         Objects
using Krita [4] or Inkscape [5], or search OpenGameArt [6].          '''
org for other options. Then, if you didn’t already do so in the
previous article, create a directory called images within your       class Player(pygame.sprite.Sprite):
Python project directory. Put the images you want to use in                """
your game into the images folder.                                          Spawn a player
   To make your game truly exciting, you ought to use an                   """
animated sprite for your hero. If you’re drawing your charac-
ters yourself, this means you have to draw more assets, but                def __init__(self):
it makes a big difference. The most common animation is a                        pygame.sprite.Sprite.__init__(self)
walk cycle, a series of drawings that make it look like your                     self.images = []
sprite is walking. The quick and dirty version of a walk cycle
requires four drawings.                                                          img = 
                                                                                       pygame.image.load(os.path.join('images',
                                                                                       'hero.png')).convert()
                                                                                 self.images.append(img)
                                                                                 self.image = self.images[0]
                                                                                 self.rect = self.image.get_rect()


                                                                     This code block creates a virtual “object” for Python to
                                                                     use when referencing your hero sprite. In object-oriented
                                                                     programming, an “object” is referred to as a class. The
                                                                     object template (specifically, pygame.sprite.Sprite) is
                                                                     provided by Pygame. That’s what makes it possible for
                                                                     you to define an image to represent the player character. If
Ajay Karat, CC BY-SA 3.0                                             you had to program that from scratch, you’d have to learn
                                                                     a lot more about Python before you could start creating a
Note: The code samples in this article allow for both a static       game, and that’s the advantage of using a framework like
player sprite and an animated one.                                   Pygame.
  Name your player sprite hero.png. If you’re creating an an-          If you have a walk cycle for your playable character, save
imated sprite for a walk cycle, append a digit after the name,       each drawing as an individual file called hero1.png to hero4.



A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                  . CC BY-SA 4.0 . OPENSOURCE.COM                                       15
HOW TO ADD A PLAYER TO YOUR PYTHON GAME . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


     png in your project’s images folder. Then use a loop to tell             Change the drawing clause of your main loop to look
     Python to cycle through each file. This is one of the features        like this:
     of object-oriented programming: each class can have tasks
     assigned exclusively to it, which occurs without affecting the            world.blit(backdrop, backdropbox)
     “world” around it. In this case, your player character sprite is          player_list.draw(world) # draw player
     programmed to cycle through four different images to create               pygame.display.flip()
     the illusion of walking, and this can happen regardless of                clock.tick(fps)
     what else is happening around it.
                                                                           Launch your game now. Your player spawns!
     '''
     Objects                                                               Setting the alpha channel
     '''                                                                   Depending on how you created your player sprite, it may
                                                                           have a colored block around it. What you are seeing is the
                                                                           space that ought to be occupied by an alpha channel. It’s
     class Player(pygame.sprite.Sprite):                                   meant to be the “color” of invisibility, but Python doesn’t know
           """                                                             to make it invisible yet. What you are seeing, then, is the
           Spawn a player                                                  space within the bounding box (or “hit box,” in modern gam-
           """                                                             ing terms) around the sprite.

           def __init__(self):
                 pygame.sprite.Sprite.__init__(self)
                 self.images = []
                 for i in range(1, 5):
                    img = 
                          pygame.image.load(os.path.join('images',
                            'hero' + str(i) + '.png')).convert()
                     self.images.append(img)
                     self.image = self.images[0]
                    self.rect = self.image.get_rect()


     Bring the player into the game world
     Now that a Player class exists, you must use it to spawn              You can tell Python what color to make invisible by setting
     a player sprite in your game world. If you never call on the          an alpha channel and using RGB values. If you don’t know
     Player class, it never runs, and there will be no player. You         the RGB values your drawing uses as alpha, open your
     can test this out by running your game now. The game runs             drawing in Pinta or Inkscape and fill the empty space around
     just as well as it did at the end of the previous article, with the   your drawing with a unique color, like #00ff00 (more or less
     exact same results: an empty game world.                              a “greenscreen green”). Take note of the color’s hex value
       To bring a player sprite into your world, you must “call” the       (#00ff00, for greenscreen green) and use that in your Python
     Player class to generate a sprite and then add it to a Pygame         script as the alpha channel.
     sprite group. Add these lines to your Setup section:                    Using alpha requires the addition of two lines in your Sprite
                                                                           creation code. Some version of the first line is already in your
     player = Player()       # spawn player                                code. Add the other two lines:
     player.rect.x = 0       # go to x
     player.rect.y = 0       # go to y                                         img = 
                                                                                     pygame.image.load(os.path.join('images','hero' +
     player_list = pygame.sprite.Group()                                             str(i) + '.png')).convert()
     player_list.add(player)                                                   img.convert_alpha()     # optimise alpha
                                                                               img.set_colorkey(ALPHA) # set alpha
     Try launching your game to see what happens. Warning: it
     won’t do what you expect. When you launch your project, the           Python doesn’t know what to use as alpha unless you tell it.
     player sprite doesn’t spawn. Actually, it spawns, but only for          If you believe your image already has an alpha channel,
     a millisecond. How do you fix something that only happens             you can try setting a variable ALPHA to 0 or 255, both of
     for a millisecond? You might recall from the previous article         which are common places for alpha to be stored. One of
     that you need to add something to the main loop. To make              those may work, but maybe due to my background in film
     the player spawn for longer than a millisecond, tell Python to        production, I prefer to explicitly create and set my own alpha
     draw it once per loop.                                                channel.



     16                                  A GUIDE TO BUILDING A VIDEO GAME IN PYTHON              . CC BY-SA 4.0 . OPENSOURCE.COM
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . HOW TO ADD A PLAYER TO YOUR PYTHON GAME



Setting your own alpha                                            Here’s the code in its entirety so far:
In the Variable section of your code, add this variable
definition:                                                       #!/usr/bin/env python3
                                                                  # by Seth Kenlon
ALPHA = (0, 255, 0)
                                                                  # GPLv3
In this example code, 0,255,0 is used, which is the same          # This program is free software: you can redistribute it and/or
value in RGB as #00ff00 is in hex. You can get all of these       # modify it under the terms of the GNU General Public License as
color values from a good graphics application like GIMP [7],      # published by the Free Software Foundation, either version 3 of
Krita, or Inkscape. Alternately, you can also detect color        # the License, or (at your option) any later version.
values with a good system-wide color chooser, like KColor-        #
Chooser [8] or ColourPicker [9].                                  # This program is distributed in the hope that it will be useful,
                                                                  # but WITHOUT ANY WARRANTY; without even the implied warranty of
                                                                  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.   See the
                                                                  # GNU General Public License for more details.
                                                                  #
                                                                  # You should have received a copy of the GNU General Public
                                                                  # License along with this program.    If not, see
                                                                  # <http://www.gnu.org/licenses/>.
                                                                  from typing import Tuple


                                                                  import pygame
                                                                  import sys
                                                                  import os


                                                                  '''
                                                                  Variables
                                                                  '''


                                                                  worldx = 960
                                                                  worldy = 720
If your graphics application is rendering your sprite’s back-     fps = 40      # frame rate
ground as some other value, adjust the values of your al-         ani = 4      # animation cycles
pha variable as needed. No matter what you set your alpha         world = pygame.display.set_mode([worldx, worldy])
value, it will be made “invisible.” RGB values are very strict,
so if you need to use 000 for alpha, but you need 000 for         BLUE = (25, 25, 200)
the black lines of your drawing, just change the lines of your    BLACK = (23, 23, 23)
drawing to 111, which is close enough to black that nobody        WHITE = (254, 254, 254)
but a computer can tell the difference.                           ALPHA = (0, 255, 0)
   Launch your game to see the results.
                                                                  '''
                                                                  Objects
                                                                  '''




                                                                  class Player(pygame.sprite.Sprite):
                                                                        """
                                                                        Spawn a player
                                                                        """


                                                                        def __init__(self):
If you're having trouble setting an alpha channel for your                    pygame.sprite.Sprite.__init__(self)
character, refer to Appendix 3 for detailed instructions on                   self.images = []
getting it right.                                                             for i in range(1, 5):




A GUIDE TO BUILDING A VIDEO GAME IN PYTHON               . CC BY-SA 4.0 . OPENSOURCE.COM                                             17
HOW TO ADD A PLAYER TO YOUR PYTHON GAME . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


                   img = 
                         pygame.image.load(os.path.join('images',                   pygame.quit()
                         'hero' + str(i) + '.png')).convert()                       try:
                   img.convert_alpha()     # optimise alpha                                sys.exit()
                   img.set_colorkey(ALPHA)     # set alpha                          finally:
                   self.images.append(img)                                               main = False
                   self.image = self.images[0]
                   self.rect = self.image.get_rect()                             if event.type == pygame.KEYDOWN:
                                                                                     if event.key == ord('q'):
                                                                                           pygame.quit()
     '''                                                                             try:
     Setup                                                                                  sys.exit()
     '''                                                                             finally:
                                                                                           main = False
     backdrop = pygame.image.load(os.path.join('images', 'stage.png'))      world.blit(backdrop, backdropbox)
     clock = pygame.time.Clock()                                            player_list.draw(world)
     pygame.init()                                                          pygame.display.flip()
     backdropbox = world.get_rect()                                         clock.tick(fps)
     main = True
                                                                         In the fourth part of this series, I’ll show you how to make
     player = Player()    # spawn player                                 your sprite move. How exciting!
     player.rect.x = 0    # go to x
     player.rect.y = 0    # go to y                                      Links
     player_list = pygame.sprite.Group()                                 [1]	https://opensource.com/article/17/12/program-game-
     player_list.add(player)                                                  python-part-2-creating-game-world
                                                                         [2]	https://github.com/SuperTux/supertux/tree/master/data/
                                                                              images/creatures/tux/small
     '''                                                                 [3] https://www.supertux.org/
     Main Loop                                                           [4]	http://krita.org/
     '''                                                                 [5]	http://inkscape.org/
                                                                         [6] https://opengameart.org/
     while main:                                                         [7]	http://gimp.org/
           for event in pygame.event.get():                              [8] https://github.com/KDE/kcolorchooser
               if event.type == pygame.QUIT:                             [9]	https://github.com/stuartlangridge/ColourPicker




     18                               A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                . CC BY-SA 4.0 . OPENSOURCE.COM
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . USING PYGAME TO MOVE YOUR GAME CHARACTER AROUND




Using Pygame to move
your game character around
In the fourth part of this series, learn how to code the controls needed to move a game character.


IN THE FIRST                  ARTICLE in this series, I explained
                              how to use Python to create a
simple, text-based dice game [1]. In the second part, you
                                                                                  pygame.quit(); sys.exit()
                                                                                  main = False


began building a game from scratch, starting with creating                   if event.type == pygame.KEYDOWN:
the game’s environment [2]. And in the third installment, you                     if event.key == pygame.K_LEFT or event.key == ord('a'):
created a player sprite [3] and made it spawn in your (rather                         print('left')
empty) game world. As you’ve probably noticed, a game isn’t                       if event.key == pygame.K_RIGHT or event.key == ord('d'):
much fun when you can’t move your character around. In                                print('right')
this article, you’ll use Pygame to add keyboard controls so                       if event.key == pygame.K_UP or event.key == ord('w'):
you can direct your character’s movement.                                         print('jump')
   There are functions in Pygame to add other kinds of con-
trols (such as a mouse or game controller), but since you                    if event.type == pygame.KEYUP:
certainly have a keyboard if you’re typing out Python code,                       if event.key == pygame.K_LEFT or event.key == ord('a'):
that’s what this article covers. Once you understand key-                             print('left stop')
board controls, you can explore other options on your own.                        if event.key == pygame.K_RIGHT or event.key == ord('d'):
   You created a key to quit your game in the second article in                       print('right stop')
this series, and the principle is the same for movement. How-                     if event.key == ord('q'):
ever, getting your character to move is a little more complex.                        pygame.quit()
   Start with the easy part: setting up the controller keys.                          sys.exit()
                                                                                      main = False
Setting up keys for controlling your player sprite
Open your Python game script in IDLE, PyCharm, or a text            Some people prefer to control player characters with the
editor.                                                             keyboard characters W, A, S, and D, and others prefer to
   Because the game must constantly “listen” for keyboard           use arrow keys. Be sure to include both options.
events, you’ll be writing code that needs to run continuously.         Note: It’s vital that you consider all of your users when pro-
Can you figure out where to put code that needs to run con-         gramming. If you write code that works only for you, it’s very
stantly for the duration of the game?                               likely that you’ll be the only one who uses your application.
   If you answered “in the main loop,” you’re correct! Re-          More importantly, if you seek out a job writing code for mon-
member that unless code is in a loop, it runs (at most) only        ey, you are expected to write code that works for everyone.
once—and it may not run at all if it’s hidden away in a class       Giving your users choices, such as the option to use either
or function that never gets used.                                   arrow keys or WASD (it’s called accessibility), is a sign of a
   To make Python monitor for incoming key presses, add             good programmer.
this code to the main loop. There’s no code to make anything           Launch your game using Python, and watch the console
happen yet, so use print statements to signal success. This         window for output when you press the right, left, and up ar-
is a common debugging technique.                                    rows, or the A, D, and W keys.

while main:                                                         $ python ./your-name_game.py
   for event in pygame.event.get():                                   left
       if event.type == pygame.QUIT:                                  left stop




A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                 . CC BY-SA 4.0 . OPENSOURCE.COM                                             19
USING PYGAME TO MOVE YOUR GAME CHARACTER AROUND . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


      right                                                              its position, designated by the self.rect.x and self.rect.y
      right stop                                                         properties, to its current position plus whatever amount of
      jump                                                               movex or movey is applied. (The number of pixels the move
                                                                         requires is set later.)
    This confirms that Pygame detects your key presses correctly.
    Now it’s time to do the hard work of making the sprite move.             def update(self):
                                                                                 """
    Coding the player movement function                                          Update sprite position
    To make your sprite move, you must create a property for                     """
    your sprite that represents movement. When your sprite is                    self.rect.x = self.rect.x + self.movex
    not moving, this variable is set to 0.
       If you are animating your sprite, or should you decide to         Do the same thing for the Y position:
    animate it in the future, you also must track frames so the
    walk cycle stays on track.                                                   self.rect.y = self.rect.y + self.movey
       Create these variables in the Player class. The first two
    lines are for context (you already have them in your code, if        For animation, advance the animation frames whenever
    you’ve been following along), so add only the last three:            your sprite is moving, and use the corresponding animation
                                                                         frame as the player image:
         def __init__(self):
              pygame.sprite.Sprite.__init__(self)                                # moving left
              self.movex = 0 # move along X                                      if self.movex < 0:
              self.movey = 0 # move along Y                                            self.frame += 1
              self.frame = 0 # count frames                                            if self.frame > 3*ani:
                                                                                            self.frame = 0
    With those variables set, it’s time to code the sprite’s                           self.image = self.images[self.frame//ani]
    movement.
       The player sprite doesn’t need to respond to control all the              # moving right
    time because sometimes it isn’t being told to move. The code                 if self.movex > 0:
    that controls the sprite, therefore, is only one small part of all                 self.frame += 1
    the things the player sprite can do. When you want to make                         if self.frame > 3*ani:
    an object in Python do something independent of the rest                                self.frame = 0
    of its code, you place your new code in a function. Python                         self.image = self.images[self.frame//ani]
    functions start with the keyword def, which stands for define.
       Make a function in your Player class to add some num-             Tell the code how many pixels to add to your sprite’s position
    ber of pixels to your sprite’s position on screen. Don’t worry       by setting a variable, then use that variable when triggering
    about how many pixels you add yet; that will be decided in           the functions of your Player sprite.
    later code.                                                             First, create the variable in your setup section. In this
                                                                         code, the first two lines are for context, so just add the third
         def control(self,x,y):                                          line to your script:
              """
              control player movement                                    player_list = pygame.sprite.Group()
              """                                                        player_list.add(player)
              self.movex += x                                            steps = 10    # how many pixels to move
              self.movey += y
                                                                         Now that you have the appropriate function and variable, use
    To move a sprite in Pygame, you must tell Python to redraw           your key presses to trigger the function and send the vari-
    the sprite in its new location—and where that new location is.       able to your sprite.
       Since the Player sprite isn’t always moving, make these              Do this by replacing the print statements in your main
    updates a dedicated function within the Player class. Add            loop with the Player sprite’s name (player), the function
    this function after the control function you created earlier.        (.control), and how many steps along the X axis and Y axis
       To make it appear that the sprite is walking (or flying, or       you want the player sprite to move with each loop.
    whatever it is your sprite is supposed to do), you need to
    change its position on screen when the appropriate key is                   if event.type == pygame.KEYDOWN:
    pressed. To get it to move across the screen, you redefine                         if event.key == pygame.K_LEFT or event.key == ord('a'):




    20                              A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                  . CC BY-SA 4.0 . OPENSOURCE.COM
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . USING PYGAME TO MOVE YOUR GAME CHARACTER AROUND



              player.control(-steps,0)                               let Pygame do all the work and just make a call to a funciton
          if event.key == pygame.K_RIGHT or event.key == ord('d'):   that already exists.
              player.control(steps,0)                                   You only need the transform on the instance when your
          if event.key == pygame.K_UP or event.key == ord('w'):      graphic is walking the opposite way it’s facing by default. My
              print('jump')                                          graphic faces right, so I apply the transform to the left code
                                                                     block. The pygame.transform.flip function takes three argu-
       if event.type == pygame.KEYUP:                                ments, according to Pygame documentation [4]: what to flip,
          if event.key == pygame.K_LEFT or event.key == ord('a'):    whether to flip horizontally, and whether to flip vertically. In this
              player.control(steps,0)                                case, those are the graphic (which you’ve already defined in the
          if event.key == pygame.K_RIGHT or event.key == ord('d'):   existing code), True for horizontal, and False for a vertical flip.
              player.control(-steps,0)                                  Update your code:
          if event.key == ord('q'):
              pygame.quit()                                                    # moving left
              sys.exit()                                                       if self.movex < 0:
              main = False                                                         self.frame += 1
                                                                                   if self.frame > 3*ani:
Remember, steps is a variable representing how many pix-                                self.frame = 0
els your sprite moves when a key is pressed. If you add 10                         self.image = pygame.transform.flip
pixels to the location of your player sprite when you press D                                    (self.images[self.frame // ani],
or the right arrow, then when you stop pressing that key you                                     True, False)
must subtract 10 (-steps) to return your sprite’s momentum
back to 0.                                                           Notice that the transform function is inserted into your ex-
  Try your game now. Warning: it won’t do what you expect.           isting code. The variable self.image is still getting defined
                                                                     as an image from your list of hero images, but it’s getting
Updating the sprite graphic                                          “wrapped” in the transform function.
Why doesn’t your sprite move yet? Because the main loop                 Try your code now, and watch as your hero does an about-
doesn’t call the update function.                                    face each time you point it in a different direction.
  Add code to your main loop to tell Python to update the               That’s enough of a lesson for now. Until the next article,
position of your player sprite. Add the line with the comment:       you might try exploring other ways to control your hero. For
                                                                     intance, should you have access to a joystick, try reading
   player.update()   # update player position                        Pygame’s documentation for its joystick [5] module and see
   player_list.draw(world)                                           if you can make your sprite move that way. Alternately, see if
   pygame.display.flip()                                             you can get the mouse [6] to interact with your sprite.
   clock.tick(fps)                                                      Most importantly, have fun!

Launch your game again to witness your player sprite move            All the code used in this tutorial
across the screen at your bidding. There’s no vertical move-         For your reference, here is all the code used in this series of
ment yet because those functions will be controlled by grav-         articles so far.
ity, but that’s another lesson for another article.
   Movement works, but there’s still one small problem:              #!/usr/bin/env python3
your hero graphic doesn’t turn to face the direction it’s            # by Seth Kenlon
walking. In other words, if you designed your hero facing
right, then it looks like it’s walking backwards when you            # GPLv3
press the left arrow key. Normally, you’d expect your hero           # This program is free software: you can redistribute it and/or
to turn left when walking left, and turn right again to walk         # modify it under the terms of the GNU General Public License as
to the right.                                                        # published by the Free Software Foundation, either version 3 of
                                                                     # the License, or (at your option) any later version.
Flipping your sprite                                                 #
You can flip a graphic with Pygame’s transform function.             # This program is distributed in the hope that it will be useful,
This, like all the other functions you’ve been using for this        # but WITHOUT ANY WARRANTY; without even the implied warranty of
game, is a lot of complex code and maths distilled into a            # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.    See the
single, easy to use, Python keyword. This is a great example         # GNU General Public License for more details.
of why a framework helps you code. Instead of having to              #
learn basic principles of drawing pixels on screen, you can          # You should have received a copy of the GNU General Public




A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                 . CC BY-SA 4.0 . OPENSOURCE.COM                                               21
USING PYGAME TO MOVE YOUR GAME CHARACTER AROUND . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


    # License along with this program.       If not, see                     def update(self):
    # <http://www.gnu.org/licenses/>.                                            """
    from typing import Tuple                                                    Update sprite position
                                                                                """
    import pygame
    import sys                                                                   self.rect.x = self.rect.x + self.movex
    import os                                                                    self.rect.y = self.rect.y + self.movey


    '''                                                                          # moving left
    Variables                                                                    if self.movex < 0:
    '''                                                                                self.frame += 1
                                                                                       if self.frame > 3*ani:
    worldx = 960                                                                              self.frame = 0
    worldy = 720                                                                       self.image = 
                                                                                                    pygame.transform.flip(self.images[self.
    fps = 40      # frame rate                                                                         frame // ani], True, False)
    ani = 4      # animation cycles
    world = pygame.display.set_mode([worldx, worldy])                            # moving right
                                                                                 if self.movex > 0:
    BLUE = (25, 25, 200)                                                               self.frame += 1
    BLACK = (23, 23, 23)                                                               if self.frame > 3*ani:
    WHITE = (254, 254, 254)                                                                   self.frame = 0
    ALPHA = (0, 255, 0)                                                                self.image = self.images[self.frame//ani]


    '''
    Objects                                                            '''
    '''                                                                Setup
                                                                       '''


    class Player(pygame.sprite.Sprite):                                backdrop = pygame.image.load(os.path.join('images', 'stage.png'))
          """                                                          clock = pygame.time.Clock()
          Spawn a player                                               pygame.init()
          """                                                          backdropbox = world.get_rect()
                                                                       main = True
          def __init__(self):
                pygame.sprite.Sprite.__init__(self)                    player = Player()        # spawn player
                self.movex = 0                                         player.rect.x = 0        # go to x
                self.movey = 0                                         player.rect.y = 0        # go to y
                self.frame = 0                                         player_list = pygame.sprite.Group()
                self.images = []                                       player_list.add(player)
                for i in range(1, 5):                                  steps = 10
                      img = 
                            pygame.image.load(os.path.join('images',
                            'hero' + str(i) + '.png')).convert()       '''
                      img.convert_alpha()   # optimise alpha           Main Loop
                      img.set_colorkey(ALPHA)   # set alpha            '''
                      self.images.append(img)
                      self.image = self.images[0]                      while main:
                      self.rect = self.image.get_rect()                      for event in pygame.event.get():
                                                                                 if event.type == pygame.QUIT:
          def control(self, x, y):                                                     pygame.quit()
                """                                                                    try:
                control player movement                                                       sys.exit()
                """                                                                    finally:
                self.movex += x                                                            main = False
                self.movey += y




    22                                  A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                . CC BY-SA 4.0 . OPENSOURCE.COM
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . USING PYGAME TO MOVE YOUR GAME CHARACTER AROUND



      if event.type == pygame.KEYDOWN:                                     player_list.draw(world)
          if event.key == ord('q'):                                        pygame.display.flip()
              pygame.quit()                                                clock.tick(fps)
              try:
                  sys.exit()                                         You’ve come far and learned much, but there’s a lot more
              finally:                                               to do. In the next few articles, you’ll add enemy sprites [7],
                  main = False                                       emulated gravity, and lots more. In the mean time, practice
          if event.key == pygame.K_LEFT or event.key == ord('a'):    with Python!
              player.control(-steps, 0)
          if event.key == pygame.K_RIGHT or event.key == ord('d'):
              player.control(steps, 0)                               Links
          if event.key == pygame.K_UP or event.key == ord('w'):      [1]  https://opensource.com/article/17/10/python-101
              print('jump')                                          [2]   https://opensource.com/article/17/12/program-game-
                                                                            python-part-2-creating-game-world
      if event.type == pygame.KEYUP:                                 [3]	  https://opensource.com/article/17/12/program-game-
          if event.key == pygame.K_LEFT or event.key == ord('a'):           python-part-3-spawning-player
              player.control(steps, 0)                               [4] https://www.pygame.org/docs/ref/transform.html#pygame.
          if event.key == pygame.K_RIGHT or event.key == ord('d'):          transform.flip
               player.control(-steps, 0)                             [5]	  http://pygame.org/docs/ref/joystick.html
                                                                     [6]	  http://pygame.org/docs/ref/mouse.html#module-pygame.
   world.blit(backdrop, backdropbox)                                        mouse
   player.update()                                                   [7]	  https://opensource.com/article/18/5/pygame-enemy




A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                  . CC BY-SA 4.0 . OPENSOURCE.COM                                     23
WHAT’S A HERO WITHOUT A VILLAIN? HOW TO ADD ONE TO YOUR PYTHON GAME . . . . . . . . . . . . .



   What’s a hero without a villain?
   How to add one to your Python game
   In part five of this series on building a Python game from scratch, add a bad guy for your good guy
   to battle.


   IN THE PREVIOUS                          ARTICLES in this series
                                            (see part 1, part 2, part 3,
   and part 4), you learned how to use Pygame and Python to
                                                                              def __init__(self,x,y,img):
                                                                                  pygame.sprite.Sprite.__init__(self)
                                                                                  self.image = pygame.image.load(os.path.join('images',img))
   spawn a playable hero character in an as-yet empty video                       self.image.convert_alpha()
   game world. But what’s a hero without a villain?                               self.image.set_colorkey(ALPHA)
      It would make for a pretty boring game if you had no ene-                   self.rect = self.image.get_rect()
   mies, so in this article, you’ll add an enemy to your game and                 self.rect.x = x
   construct a framework for building levels.                                     self.rect.y = y
      It might seem strange to jump ahead to enemies when there’s
   still more to be done to make the player sprite fully functional,       If you want to animate your enemy, do it the same way you
   but you’ve learned a lot already, and creating villains is very         animated your player [3].
   similar to creating a player sprite. So relax, use the knowledge
   you already have, and see what it takes to stir up some trouble.        Spawning an enemy
      For this exercise, you need an enemy sprite. If you haven’t          You can make the class useful for spawning more than just
   downloaded one already, you can find Creative Commons [1]               one enemy by allowing yourself to tell the class which image
   assets on OpenGameArt.org [2].                                          to use for the sprite and where in the world you want the
                                                                           sprite to appear. This means you can use this same enemy
   Creating the enemy sprite                                               class to generate any number of enemy sprites anywhere
   Whether you realize it or not, you already know how to imple-           in the game world. All you have to do is make a call to the
   ment enemies. The process is similar to creating a player sprite:       class, and tell it which image to use, along with the X and Y
                                                                           coordinates of your desired spawn point.
   1. Make a class so enemies can spawn.                                      Ao you did when spawning a player sprite, add code to
   2. Create an update function for the enemy, and update the             designate a spawn point in the setup section of your script:
      enemy in your main loop.
   3. Create a move function so your enemy can roam around.               enemy = Enemy(300,0,'enemy.png')        # spawn enemy
                                                                           enemy_list = pygame.sprite.Group()      # create enemy group
   Start with the class. Conceptually, it’s mostly the same as             enemy_list.add(enemy)                   # add enemy to group
   your Player class. You set an image or series of images, and
   you set the sprite’s starting position.                                 In that sample code, you spawn an enemy by creating a new
     Before continuing, make sure you have placed your enemy               object (called enemy), at 300 pixels on the X axis and 0 on
   graphic in your game project’s images directory (the same di-           the Y axis. Spawning the enemy at 0 on the Y axis means
   rectory where you placed your player image). In this article’s          that its top left corner is located at 0, with the graphic itself
   example code, the enemy graphic is named enemy.png.                     descending down from that point. You might need to adjust
      A game looks a lot better when everything alive is ani-              these numbers, or the numbers for your hero sprite, depend-
   mated. Animating an enemy sprite is done the same way as                ing on how big your sprites are, but try to get it to spawn in
   animating a player sprite. For now, though, keep it simple,             a place you can reach with your player sprite (accounting for
   and use a non-animated sprite.                                          your game’s current lack of vertical movement). In the end, I
      At the top of the objects section of your code, create a             placed my enemy at 0 pixels on the Y axis and my hero at 30
   class called Enemy with this code:                                      pixels to get them boh to appear on the same plane. Experi-
                                                                           ment with the spawn points for yourself, keeping in mind that
   class Enemy(pygame.sprite.Sprite):                                      greater Y axis numbers are lower on the screen.
        """                                                                   Your hero graphic had an image “hard coded” into its class
        Spawn an enemy                                                     because there’s only one hero, but you may want to use dif-
        """                                                                ferent graphics for each enemy, so the image file is some-



   24                               A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                 . CC BY-SA 4.0 . OPENSOURCE.COM
. . . . . . . . . . . . WHAT’S A HERO WITHOUT A VILLAIN? HOW TO ADD ONE TO YOUR PYTHON GAME


thing you can define at sprite creation. The image used for                  enemy = Enemy(eloc[0],eloc[1],'enemy.png') # spawn enemy
this enemy sprite is enemy.png.                                              enemy_list = pygame.sprite.Group() # create enemy group
                                                                             enemy_list.add(enemy)             # add enemy to group
Drawing a sprite on screen                                              if lvl == 2:
If you were to launch your game now, it would run but you                    print("Level " + str(lvl) )
wouldn’t see an enemy. You might recall the same problem
when you created your player sprite. Do you remember how                return enemy_list
to fix it?
   To get a sprite to appear on screen, you must add them to     The return statement ensures that when you use the Level.bad
your main loop. If something is not in your main loop, then it   function, you’re left with an enemy_list containing each enemy
only happens once, and only for a millisecond. If you want       you defined.
something to persist in your game, it must happen in the           Since you are creating enemies as part of each level now,
main loop.                                                       your setup section needs to change, too. Instead of creating
   You must add code to draw all enemies in the enemy            an enemy, you must define where the enemy will spawn and
group (called enemy_list), which you established in your         what level it belongs to.
setup section, on the screen. The middle line in this example
code is the new line you need to add:                            eloc = []
                                                                 eloc = [300,0]
    player_list.draw(world)                                      enemy_list = Level.bad( 1, eloc )
    enemy_list.draw(world)    # refresh enemies
    pygame.display.flip()                                        Run the game again to confirm your level is generating cor-
                                                                 rectly. You should see your player, as usual, and the enemy
Right now, you have only one enemy, but you can add              you added in this chapter.
more later if you want. As long as you add an enemy to
the enemies group, it will be drawn to the screen during         Hitting the enemy
the main loop.                                                   An enemy isn’t much of an enemy if it has no effect on the
  Launch your game. Your enemy appears in the game               player. It’s common for enemies to cause damage when a
world at whatever X and Y coordinate you chose.                  player collides with them.
                                                                   Since you probably want to track the player’s health, the
Level one                                                        collision check happens in the Player class rather than in the
Your game is in its infancy, but you will probably want to add   Enemy class. You can track the enemy’s health, too, if you
a series of levels, eventually. It’s important to plan ahead     want. The logic and code are pretty much the same, but, for
when you program so your game can grow as you learn              now, just track the player’s health.
more about programming. Even though you don’t even have            To track player health, you must first establish a variable
one complete level yet, you should code as if you plan on        for the player’s health. The first line in this code sample is for
having many levels.                                              context, so add the second line to your Player class:
   Think about what a “level” is. How do you know you are at
a certain level in a game?                                               self.frame     = 0
   You can think of a level as a collection of items. In a               self.health = 10
platformer, such as the one you are building here, a level
consists of a specific arrangement of platforms, place-          In the update function of your Player class, add this code
ment of enemies and loot, and so on. You can build a             block:
class that builds a level around your player. Eventually,
when you create more than one level, you can use this                    hit_list = 
                                                                                    pygame.sprite.spritecollide(self, enemy_list,
class to generate the next level when your player reach-                               False)
es a specific goal.                                                      for enemy in hit_list:
   Move the code you wrote to create an enemy and its group                  self.health -= 1
into a new function that gets called along with each new lev-                print(self.health)
el. It requires some modification so that each time you create
a new level, you can create and place several enemies:           This code establishes a collision detector using the Pygame
                                                                 function sprite.spritecollide, called enemy_hit. This col-
class Level():                                                   lision detector sends out a signal any time the hitbox of its
   def bad(lvl,eloc):                                            parent sprite (the player sprite, where this detector has been
       if lvl == 1:                                              created) touches the hitbox of any sprite in enemy_list. The



A GUIDE TO BUILDING A VIDEO GAME IN PYTHON              . CC BY-SA 4.0 . OPENSOURCE.COM                                           25
WHAT’S A HERO WITHOUT A VILLAIN? HOW TO ADD ONE TO YOUR PYTHON GAME . . . . . . . . . . . . .


   for loop is triggered when such a signal is received and de-                     self.rect.x += speed
   ducts a point from the player’s health.                                      elifself.counter >= distance and self.counter
     Since this code appears in the update function of your                         <= distance*2:
   player class and update is called in your main loop, Pygame                      self.rect.x -= speed
   checks for this collision once every clock tick.                             else:
                                                                                    self.counter = 0
   Moving the enemy
   An enemy that stands still is useful if you want, for instance,              self.counter += 1
   spikes or traps that can harm your player, but the game is
   more of a challenge if the enemies move around a little.             After you enter this code, PyCharm will offer to simplify the
      Unlike a player sprite, the enemy sprite is not controlled by     “chained comparison”. You can accept its suggestion to optimize
   the user. Its movements must be automated.                           your code, and to learn some advanced Python syntax. You can
      Eventually, your game world will scroll, so how do you get        also safely ignore PyCharm. The code works, either way.
   an enemy to move back and forth within the game world                   You can adjust the distance and speed as needed.
   when the game world itself is moving?                                   The question is: does this code work if you launch your
      You tell your enemy sprite to take, for example, 10 paces         game now?
   to the right, then 10 paces to the left. An enemy sprite can’t          Of course not! And you know why: you must call the move
   count, so you have to create a variable to keep track of how         function in your main loop.
   many paces your enemy has moved and program your ene-                   The first line in this sample code is for context, so add the
   my to move either right or left depending on the value of your       last two lines:
   counting variable.
      First, create the counter variable in your Enemy class. Add           enemy_list.draw(world) #refresh enemy
   the last line in this code sample:                                       for e in enemy_list:
                                                                                e.move()
            self.rect = self.image.get_rect()
            self.rect.x = x                                             Launch your game and see what happens when you hit your
            self.rect.y = y                                             enemy. You might have to adjust where the sprites spawn
            self.counter = 0 # counter variable                         so that your player and your enemy sprite can collide. When
                                                                        they do collide, look in the console of IDLE or PyCharm to
   Next, create a move function in your Enemy class. Use an             see the health points being deducted.
   if-else loop to create what is called an infinite loop:

   • M ove right if the counter is on any number from 0 to 100.
   • Move left if the counter is on any number from 100 to 200.
   • Reset the counter back to 0 if the counter is greater than 200.

   An infinite loop has no end; it loops forever because nothing
   in the loop is ever untrue. The counter, in this case, is always
   either between 0 and 100 or 100 and 200, so the enemy
   sprite walks right to left and right to left forever.
      The actual numbers you use for how far the enemy will
   move in either direction depending on your screen size, and
   possibly, eventually, the size of the platform your enemy is
   walking on. Start small and work your way up as you get
   used to the results. Try this first:

        def move(self):                                                 You may notice that health is deducted for every moment
            '''                                                         your player and enemy are touching. That’s a problem, but
           enemy movement                                               it’s a problem you’ll solve later, after you’ve had more prac-
           '''                                                          tice with Python.
           distance = 80                                                    For now, try adding some more enemies. Remember to
           speed = 8                                                    add each enemy to the enemy_list. As an exercise, see if
                                                                        you can think of how you can change how far different ene-
            if self.counter >= 0 and self.counter <= distance:          my sprites move.



   26                             A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                . CC BY-SA 4.0 . OPENSOURCE.COM
. . . . . . . . . . . . WHAT’S A HERO WITHOUT A VILLAIN? HOW TO ADD ONE TO YOUR PYTHON GAME


                                                                              self.movey = 0
Code so far
                                                                              self.frame = 0
For you reference, here’s the code so far:
                                                                              self.health = 10
                                                                              self.images = []
#!/usr/bin/env python3
                                                                              for i in range(1, 5):
# by Seth Kenlon
                                                                                    img = 
                                                                                          pygame.image.load(os.path.join('images',
                                                                                          'hero' + str(i) + '.png')).convert()
# GPLv3
                                                                                    img.convert_alpha()
# This program is free software: you can redistribute it and/or
                                                                                    img.set_colorkey(ALPHA)
# modify it under the terms of the GNU General Public License as
                                                                                    self.images.append(img)
# published by the Free Software Foundation, either version 3 of
                                                                                    self.image = self.images[0]
# the License, or (at your option) any later version.
                                                                                    self.rect = self.image.get_rect()
#
# This program is distributed in the hope that it will be useful,
                                                                         def control(self, x, y):
# but WITHOUT ANY WARRANTY; without even the implied warranty of
                                                                              """
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.   See the
                                                                              control player movement
# GNU General Public License for more details.
                                                                              """
#
                                                                              self.movex += x
# You should have received a copy of the GNU General Public
                                                                              self.movey += y
# License along with this program.    If not, see
# <http://www.gnu.org/licenses/>.
                                                                         def update(self):
                                                                              """
from typing import Tuple
                                                                              Update sprite position
                                                                              """
import pygame
import sys
                                                                              self.rect.x = self.rect.x + self.movex
import os                                                                     self.rect.y = self.rect.y + self.movey

'''                                                                           # moving left
Variables                                                                     if self.movex < 0:
'''                                                                                 self.frame += 1
                                                                                    if self.frame > 3*ani:
worldx = 960                                                                            self.frame = 0
worldy = 720                                                                        self.image = 
                                                                                                 pygame.transform.flip(self.images[self.
fps = 40                                                                                           frame // ani], True, False)
ani = 4
world = pygame.display.set_mode([worldx, worldy])                             # moving right
                                                                              if self.movex > 0:
BLUE = (25, 25, 200)                                                                self.frame += 1
BLACK = (23, 23, 23)                                                                if self.frame > 3*ani:
WHITE = (254, 254, 254)                                                                 self.frame = 0
ALPHA = (0, 255, 0)                                                                 self.image = self.images[self.frame//ani]


'''                                                                           hit_list = 
                                                                                         pygame.sprite.spritecollide(self, enemy_list,
Objects                                                                                   False)
'''                                                                           for enemy in hit_list:
                                                                                    self.health -= 1
                                                                                    print(self.health)
class Player(pygame.sprite.Sprite):
      """
      Spawn a player                                                 class Enemy(pygame.sprite.Sprite):
      """                                                               """
                                                                        Spawn an enemy
      def __init__(self):                                               """
            pygame.sprite.Sprite.__init__(self)                         def __init__(self,x,y,img):
            self.movex = 0                                                    pygame.sprite.Sprite.__init__(self)




A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                    . CC BY-SA 4.0 . OPENSOURCE.COM                                        27
WHAT’S A HERO WITHOUT A VILLAIN? HOW TO ADD ONE TO YOUR PYTHON GAME . . . . . . . . . . . . .


             self.image = pygame.image.load(os.path.join('images',img))
             self.image.convert_alpha()                                   eloc = []
             self.image.set_colorkey(ALPHA)                               eloc = [300, 0]
             self.rect = self.image.get_rect()                            enemy_list = Level.bad(1, eloc )
             self.rect.x = x
             self.rect.y = y                                              '''
             self.counter = 0                                             Main Loop
                                                                          '''
         def move(self):
             '''                                                          while main:
             enemy movement                                                     for event in pygame.event.get():
             '''                                                                    if event.type == pygame.QUIT:
             distance = 80                                                              pygame.quit()
             speed = 8                                                                  try:
                                                                                               sys.exit()
             if self.counter >= 0 and self.counter <= distance:                         finally:
                   self.rect.x += speed                                                        main = False
             elifself.counter >= distance and self.counter
                   <= distance*2:                                                  if event.type == pygame.KEYDOWN:
                   self.rect.x -= speed                                                 if event.key == ord('q'):
             else:                                                                          pygame.quit()
                   self.counter = 0                                                         try:
                                                                                                 sys.exit()
             self.counter += 1                                                              finally:
                                                                                                 main = False
                                                                                        if event.key == pygame.K_LEFT or event.key == ord('a'):
   class Level():                                                                           player.control(-steps, 0)
         def bad(lvl, eloc):                                                            if event.key == pygame.K_RIGHT or event.key == ord('d'):
            if lvl == 1:                                                                    player.control(steps, 0)
                   enemy = Enemy(eloc[0],eloc[1],'enemy.png')                           if event.key == pygame.K_UP or event.key == ord('w'):
                   enemy_list = pygame.sprite.Group()                                       print('jump')
                   enemy_list.add(enemy)
            if lvl == 2:                                                           if event.type == pygame.KEYUP:
                   print("Level " + str(lvl) )                                          if event.key == pygame.K_LEFT or event.key == ord('a'):
                                                                                            player.control(steps, 0)
            return enemy_list                                                           if event.key == pygame.K_RIGHT or event.key == ord('d'):
                                                                                             player.control(-steps, 0)


   '''                                                                          world.blit(backdrop, backdropbox)
   Setup                                                                        player.update()
   '''                                                                          player_list.draw(world)
                                                                                enemy_list.draw(world)
   backdrop = pygame.image.load(os.path.join('images', 'stage.png'))            for e in enemy_list:
   clock = pygame.time.Clock()                                                      e.move()
   pygame.init()                                                                pygame.display.flip()
   backdropbox = world.get_rect()                                               clock.tick(fps)
   main = True
                                                                          Links
   player = Player()       # spawn player                                 [1]	https://opensource.com/article/20/1/what-creative-
   player.rect.x = 0       # go to x                                           commons
   player.rect.y = 30       # go to y                                     [2]	https://opengameart.org/content/opp2017-sprites-
   player_list = pygame.sprite.Group()                                         characters-objects-effects
   player_list.add(player)                                                [3] https://opensource.com/article/17/12/game-python-
   steps = 10                                                                  moving-player



   28                                      A GUIDE TO BUILDING A VIDEO GAME IN PYTHON              . CC BY-SA 4.0 . OPENSOURCE.COM
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . PUT PLATFORMS IN A PYTHON GAME WITH PYGAME




Put platforms in a Python game
with Pygame
In part six of this series on building a Python game from scratch, create some platforms for your
characters to travel.


A PLATFORMER                      GAME needs platforms. In
                                  Pygame [1], the platforms
themselves are sprites, just like your playable sprite.
                                                                    image file for texture. It’s very similar to how players or en-
                                                                    emies are drawn onscreen. You probably recognize this
                                                                    same code structure from the Player and Enemy classes.
That’s important because having platforms that are ob-
jects makes it a lot easier for your player sprite to interact      Types of platforms
with them.                                                          The next step is to map out where all your platforms need
  There are two major steps in creating platforms. First, you       to appear.
must code the objects, and then you must map out where
you want the objects to appear.                                     The tile method
                                                                    There are a few different ways to implement a platform
Coding platform objects                                             game world. In the original side-scroller games, such as
To build a platform object, you create a class called Platform.     Mario Super Bros. and Sonic the Hedgehog, the technique
It’s a sprite [2], just like your Player sprite, with many of the   was to use “tiles,” meaning that there were a few blocks
same properties.                                                    to represent the ground and various platforms, and these
    Your Platform class needs to know a lot of information          blocks were used and reused to make a level. You have
about what kind of platform you want, where it should appear        only eight or 12 different kinds of blocks, and you line them
in the game world, and what image it should contain. A lot of       up onscreen to create the ground, floating platforms, and
that information might not even exist yet, depending on how         whatever else your game needs. Some people find this the
much you have planned out your game, but that’s all right.          easier way to make a game since you just have to make (or
Just as you didn’t tell your Player sprite how fast to move         download) a small set of level assets to create many differ-
until the end of the Movement article [3], you don’t have to        ent levels. The code, however, requires a little more math.
tell Platform everything upfront.
    In the objects section of your script, create a new class:

# x location, y location, img width, img height, img file
class Platform(pygame.sprite.Sprite):
    def __init__(self, xloc, yloc, imgw, imgh, img):
        pygame.sprite.Sprite.__init__(self)
        self.image = 
                     pygame.image.load(os.path.join
                     ('images', img)).convert()
        self.image.convert_alpha()
        self.image.set_colorkey(ALPHA)
        self.rect = self.image.get_rect()
        self.rect.y = yloc                                          SuperTux, a tile-based video game.
        self.rect.x = xloc
                                                                    The hand-painted method
When called, this class creates an object onscreen in some          Another method is to make each and every asset as one
X and Y location, with some width and height, using some            whole image. If you enjoy creating assets for your game



A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                  . CC BY-SA 4.0 . OPENSOURCE.COM                                     29
PUT PLATFORMS IN A PYTHON GAME WITH PYGAME . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


     world, this is a great excuse to spend time in a graphics ap-
     plication, building each and every part of your game world.
     This method requires less math, because all the platforms
     are whole, complete objects, and you tell Python where to
     place them onscreen.
        Each method has advantages and disadvantages, and
     the code you must use is slightly different depending on the
     method you choose. I’ll cover both so you can use one or
     the other, or even a mix of both, in your project.

     Level mapping
     Mapping out your game world is a vital part of level design
     and game programming in general. It does involve math, but
     nothing too difficult, and Python is good at math so it can
     help some.
         You might find it helpful to design on paper first. Get a
     sheet of paper and draw a box to represent your game win-
     dow. Draw platforms in the box, labeling each with its X
                                                                     Example of coordinates in Pygame.
     and Y coordinates, as well as its intended width and height.
     The actual positions in the box don’t have to be exact, as      The X axis starts at 0 on the far left and increases infinitely to
     long as you keep the numbers realistic. For instance, if your   the right. The Y axis starts at 0 at the top of the screen and
     screen is 720 pixels wide, then you can’t fit eight platforms   extends down.
     at 100 pixels each all on one screen.
         Of course, not all platforms in your game have to fit in    Image sizes
     one screen-sized box, because your game will scroll as          Mapping out a game world is meaningless if you don’t know
     your player walks through it. So keep drawing your game         how big your players, enemies, and platforms are. You can
     world to the right of the first screen until the end of the     find the dimensions of your platforms or tiles in a graphics
     level.                                                          program. In Krita [5], for example, click on the Image menu
         If you prefer a little more precision, you can use graph    and select Properties. You can find the dimensions at the
     paper. This is especially helpful when designing a game with    very top of the Properties window.
     tiles because each grid square can represent one tile.             Alternately, you can create a simple Python script to tell
                                                                     you the dimensions of an image. To do that, you must in-
                                                                     stall a Python module called Pillow, which provides the
                                                                     Python Image Library (PIL). Add Pillow to your project’s
                                                                     requirements.txt file:

                                                                     pygame~=1.9.6
                                                                     Pillow


                                                                     Create a new Python file in PyCharm and name it identify.
                                                                     Type this code into it:

                                                                     #!/usr/bin/env python3


     Coordinates                                                     # GNU All-Permissive License
     You may have learned in school about the Cartesian coor-        # Copying and distribution of this file, with or without
     dinate system [4]. What you learned applies to Pygame, ex-      # modification, are permitted in any medium without royalty
     cept that in Pygame, your game world’s coordinates place        # provided the copyright notice and this notice are preserved.
     0,0 in the top-left corner of your screen instead of in the     # This file is offered as-is, without any warranty.
     middle, which is probably what you’re used to from Geom-
     etry class.                                                     from PIL import Image
                                                                     import os.path
                                                                     import sys




     30                           A GUIDE TO BUILDING A VIDEO GAME IN PYTHON              . CC BY-SA 4.0 . OPENSOURCE.COM
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . PUT PLATFORMS IN A PYTHON GAME WITH PYGAME



if len(sys.argv) > 1:
    print(sys.argv[1])
else:
    sys.exit('Syntax: identify.py [filename]')


pic = sys.argv[1]
img = Image.open(pic)
X   = img.size[0]
Y   = img.size[1]


print(X, Y)


Click on the Terminal tab at the bottom of the PyCharm
window to open a terminal within your virtual environ-            Your level cannot be one image file.
ment. Now you can install the Pillow module into your
environment:                                                      You might want your game to look like that when you’ve fin-
                                                                  ished, but if you create your level in one big file, there is no
(venv) pip install -r requirements.txt                            way to distinguish a platform from the background, so either
Requirement already satisfied: pygame~=1.9.6 [...]                paint your objects in their own file or crop them from a large
Installed Pillow [...]                                            file and save individual copies.
                                                                      Note: As with your other assets, you can use GIMP [6],
Once that is installed, run your script from within your game     Krita [5], MyPaint [7], or Inkscape [8] to create your game
project directory:                                                assets.
                                                                      Platforms appear on the screen at the start of each level,
(venv) python ./identify.py images/ground.png                     so you must add a platform function in your Level class.
(1080, 97)                                                        The special case here is the ground platform, which is im-
                                                                  portant enough to be treated as its own platform group. By
The image size of the ground platform in this example is          treating the ground as its own special kind of platform, you
1080 pixels wide and 97 high.                                     can choose whether it scrolls or whether it stands still while
                                                                  other platforms float over the top of it. It’s up to you.
Platform blocks                                                      Add these two functions to your Level class:
If you choose to draw each asset individually, you must cre-
ate several platforms and any other elements you want to          def ground(lvl,x,y,w,h):
insert into your game world, each within its own file. In other      ground_list = pygame.sprite.Group()
words, you should have one file per asset, like this:                if lvl == 1:
                                                                         ground = Platform(x,y,w,h,'block-ground.png')
                                                                         ground_list.add(ground)


                                                                     if lvl == 2:
                                                                         print("Level " + str(lvl) )


One image file per object.                                           return ground_list


You can reuse each platform as many times as you want,            def platform( lvl ):
just make sure that each file only contains one platform. You        plat_list = pygame.sprite.Group()
cannot use a file that contains everything, like this:               if lvl == 1:
                                                                         plat = Platform(200, worldy-97-128, 285,67,'block-big.png')
                                                                         plat_list.add(plat)
                                                                         plat = Platform(500, worldy-97-320, 197,54,'block-small.png')
                                                                         plat_list.add(plat)
                                                                     if lvl == 2:
                                                                         print("Level " + str(lvl) )


                                                                     return plat_list




A GUIDE TO BUILDING A VIDEO GAME IN PYTHON               . CC BY-SA 4.0 . OPENSOURCE.COM                                           31
PUT PLATFORMS IN A PYTHON GAME WITH PYGAME . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


     The ground function requires an X and Y location so Pyg-          The simplified-platformer-pack from kenney.nl are 64 pixels
     ame knows where to place the ground platform. It also             square, so that’s the dimension for tiles this article uses.
     requires the width and height of the platform so Pygame           Should you download or create tiles with a different size, ad-
     knows how far the ground extends in each direction. The           just the code as needed.
     function uses your Platform class to generate an object              The Platform class is the same as the one provided in the
     onscreen, and then adds that object to the ground_list            previous sections.
     group.                                                               The ground and platform in the Level class, however,
        The platform function is essentially the same, except that     must use loops to calculate how many blocks to use to cre-
     there are more platforms to list. In this example, there are      ate each platform.
     only two, but you can have as many as you like. After en-            If you intend to have one solid ground in your game
     tering one platform, you must add it to the plat_list before      world, the ground is simple. You just “clone” your ground
     listing another. If you don’t add a platform to the group, then   tile across the whole window. For instance, you could
     it won’t appear in your game.                                     create a list of X and Y values to dictate where each tile
                                                                       should be placed, and then use a loop to take each value
       Tip: It can be difficult to think of your game                  and draw one tile. This is just an example, so don’t add
       world with 0 at the top, since the opposite is                  this to your code:
       what happens in the real world; when figuring
                                                                       # Do not add this to your code
       out how tall you are, you don’t measure yourself
                                                                       gloc = [0,656,64,656,128,656,192,656,256,656,320,656,384,656]
       from the sky down, you measure yourself from
       your feet to the top of your head.
                                                                       If you look carefully, though, you can see all the Y values
       If it’s easier for you to build your game world from the        are always the same (656, to be specific), and the X values
       “ground” up, it might help to express Y-axis values as          increase steadily in increments of 64, which is the size of
       negatives. For instance, you know that the bottom of            the tile. That kind of repetition is exactly what computers are
       your game world is the value of worldy. So worldy               good at, so you can use a little bit of math logic to have the
       minus the height of the ground (97, in this example)            computer do all the calculations for you:
                                                                          Add this to the setup part of your script:
       is where your player is normally standing. If your
       character is 64 pixels tall, then the ground minus
                                                                       gloc = []
       128 is exactly twice as tall as your player. Effectively,       tx     = 64
       a platform placed at 128 pixels is about two stories            ty     = 64
       tall, relative to your player. A platform at -320 is three
       more stories. And so on.                                        i=0
                                                                       while i <= (worldx/tx)+tx:
     As you probably know by now, none of your classes and                   gloc.append(i*tx)
     functions are worth much if you don’t use them. Add this                i=i+1
     code to your setup section:
                                                                       ground_list = Level.ground( 1,gloc,tx,ty )
     ground_list = Level.ground(1, 0, worldy-97, 1080, 97)
     plat_list = Level.platform(1)                                     With this code, regardless of the size of your window, Py-
                                                                       thon divides the width of the game world by the width of the
     And add these lines to your main loop (again, the first line is   tile and creates an array listing each X value. This doesn’t
     just for context):                                                calculate the Y value, but that never changes on flat ground
                                                                       anyway.
     enemy_list.draw(world)    # refresh enemies                           To use the array in a function, use a while loop that looks
     ground_list.draw(world)   # refresh ground                        at each entry and adds a ground tile at the appropriate loca-
     plat_list.draw(world)     # refresh platforms                     tion. Add this function to your Level class:

     Tiled platforms                                                   def ground(lvl,gloc,tx,ty):
     Tiled game worlds are considered easier to make because                 ground_list = pygame.sprite.Group()
     you just have to draw a few blocks upfront and can use them             i=0
     over and over to create every platform in the game. There are           if lvl == 1:
     sets of tiles with a Creative Commons license [9] for you to                  while i < len(gloc):
     use on sites like kenney.nl [10] and OpenGameArt.org [11].                       ground = 
                                                                                               Platform(gloc[i],worldy-ty,tx,ty,




     32                              A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                . CC BY-SA 4.0 . OPENSOURCE.COM
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . PUT PLATFORMS IN A PYTHON GAME WITH PYGAME


                        'tile-ground.png')                               In the setup section of your program, add this line:
             ground_list.add(ground)
             i=i+1                                                   plat_list = Level.platform(1, tx, ty)


    if lvl == 2:                                                     To get the platforms to appear in your game world, they must
          print("Level " + str(lvl) )                                be in your main loop. If you haven’t already done so, add
                                                                     these lines to your main loop (again, the first line is just for
    return ground_list                                               context):

This is nearly the same code as the ground function for the                    enemy_list.draw(world)   # refresh enemies
block-style platformer, provided in the previous section,                      ground_list.draw(world) # refresh ground
aside from the while loop.                                                     plat_list.draw(world)    # refresh platforms
   For moving platforms, the principle is similar, but there are
some tricks you can use to make your life easier.                    Launch your game, and adjust the placement of your plat-
   Rather than mapping every platform by pixels, you can             forms as needed. Don’t worry that you can’t see the plat-
define a platform by its starting pixel (its X value), the height    forms that are spawned offscreen; you’ll fix that soon.
from the ground (its Y value), and how many tiles to draw.
That way, you don’t have to worry about the width and height
of every platform.
   The logic for this trick is a little more complex, so copy this
code carefully. There is a while loop inside of another while
loop because this function must look at all three values within
each array entry to successfully construct a full platform. In
this example, there are only three platforms defined as ploc.
append statements, but your game probably needs more, so
define as many as you need. Of course, some won’t appear
yet because they’re far offscreen, but they’ll come into view
once you implement scrolling.

def platform(lvl,tx,ty):
    plat_list = pygame.sprite.Group()
    ploc = []
    i=0
    if lvl == 1:                                                     Applying what you know
          ploc.append((200,worldy-ty-128,3))                         I haven’t demonstrated how to place your enemy in your
          ploc.append((300,worldy-ty-256,3))                         game world, but apply what you’ve learnt so far to position
          ploc.append((500,worldy-ty-128,4))                         the enemy sprite either on a platform or down on the ground.
          while i < len(ploc):                                         Don’t position your hero sprite yet. That must be managed
             j=0                                                     by the forces of gravity (or at least an emulation of it), which
              while j <= ploc[i][2]:                                 you’ll learn in the next two articles.
                   plat = 
                          Platform((ploc[i][0]+(j*tx)),ploc[i]         For now, here’s the code so far:
                           [1],tx,ty,'tile.png')
                   plat_list.add(plat)                               #!/usr/bin/env python3
                   j=j+1                                             # by Seth Kenlon
              print('run' + str(i) + str(ploc[i]))
              i=i+1                                                  # GPLv3
                                                                     # This program is free software: you can redistribute it and/or
    if lvl == 2:                                                     # modify it under the terms of the GNU General Public License as
          print("Level " + str(lvl) )                                # published by the Free Software Foundation, either version 3 of
                                                                     # the License, or (at your option) any later version.
    return plat_list                                                 #
                                                                     # This program is distributed in the hope that it will be useful,
Of course, this has only created a function to calculate plat-       # but WITHOUT ANY WARRANTY; without even the implied warranty of
forms for each level. You code doesn’t invoke the function yet.      # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.   See the




A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                  . CC BY-SA 4.0 . OPENSOURCE.COM                                             33
PUT PLATFORMS IN A PYTHON GAME WITH PYGAME . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


     # GNU General Public License for more details.                         for i in range(1, 5):
     #                                                                            img = 
                                                                                        pygame.image.load(os.path.join('images',
     # You should have received a copy of the GNU General Public                        'hero' + str(i) + '.png')).convert()
     # License along with this program.       If not, see                         img.convert_alpha()
     # <http://www.gnu.org/licenses/>.                                            img.set_colorkey(ALPHA)
                                                                                  self.images.append(img)
     import pygame                                                                self.image = self.images[0]
     import sys                                                                   self.rect = self.image.get_rect()
     import os
                                                                       def control(self, x, y):
     '''                                                                    """
     Variables                                                              control player movement
     '''                                                                    """
                                                                            self.movex += x
                                                                            self.movey += y
     worldx = 960
     worldy = 720                                                      def update(self):
     fps = 40                                                               """
     ani = 4                                                                Update sprite position
     world = pygame.display.set_mode([worldx, worldy])                      """


     BLUE = (25, 25, 200)                                                   self.rect.x = self.rect.x + self.movex
     BLACK = (23, 23, 23)                                                   self.rect.y = self.rect.y + self.movey
     WHITE = (254, 254, 254)
     ALPHA = (0, 255, 0)                                                    # moving left
                                                                            if self.movex < 0:
     '''                                                                          self.frame += 1
     Objects                                                                      if self.frame > 3*ani:
     '''                                                                              self.frame = 0
                                                                                  self.image = 
                                                                                               pygame.transform.flip(self.images[self.
     # x location, y location, img width, img height, img file                                   frame // ani], True, False)
     class Platform(pygame.sprite.Sprite):
           def __init__(self, xloc, yloc, imgw, imgh, img):                 # moving right
                 pygame.sprite.Sprite.__init__(self)                        if self.movex > 0:
                 self.image = 
                              pygame.image.load(os.path.join                      self.frame += 1
                              ('images', img)).convert()                          if self.frame > 3*ani:
                 self.image.convert_alpha()                                           self.frame = 0
                 self.image.set_colorkey(ALPHA)                                   self.image = self.images[self.frame//ani]
                 self.rect = self.image.get_rect()
                 self.rect.y = yloc                                         hit_list = 
                                                                                       pygame.sprite.spritecollide(self, enemy_list,
                 self.rect.x = xloc                                                     False)
                                                                            for enemy in hit_list:
     class Player(pygame.sprite.Sprite):                                          self.health -= 1
           """                                                                    print(self.health)
           Spawn a player
           """                                                     class Enemy(pygame.sprite.Sprite):
                                                                      """
           def __init__(self):                                        Spawn an enemy
                 pygame.sprite.Sprite.__init__(self)                  """
                 self.movex = 0                                       def __init__(self,x,y,img):
                 self.movey = 0                                             pygame.sprite.Sprite.__init__(self)
                 self.frame = 0                                             self.image = pygame.image.load(os.path.join('images',img))
                 self.health = 10                                           self.image.convert_alpha()
                 self.images = []                                           self.image.set_colorkey(ALPHA)




     34                                A GUIDE TO BUILDING A VIDEO GAME IN PYTHON         . CC BY-SA 4.0 . OPENSOURCE.COM
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . PUT PLATFORMS IN A PYTHON GAME WITH PYGAME


          self.rect = self.image.get_rect()                                  if lvl == 1:
          self.rect.x = x                                                       ploc.append((200,worldy-ty-128,3))
          self.rect.y = y                                                       ploc.append((300,worldy-ty-256,3))
          self.counter = 0                                                      ploc.append((500,worldy-ty-128,4))
                                                                                 while i < len(ploc):
    def move(self):                                                                  j=0
          '''                                                                        while j <= ploc[i][2]:
          enemy movement                                                                   plat = 
                                                                                                  Platform((ploc[i][0]+(j*tx)),ploc[i]
          '''                                                                                      [1],tx,ty,'tile.png')
          distance = 80                                                                    plat_list.add(plat)
          speed = 8                                                                        j=j+1
                                                                                     print('run' + str(i) + str(ploc[i]))
          if self.counter >= 0 and self.counter <= distance:                         i=i+1
                self.rect.x += speed
          elifself.counter >= distance and self.counter                     if lvl == 2:
                <= distance*2:                                                   print("Level " + str(lvl) )
                self.rect.x -= speed
          else:                                                              return plat_list
                self.counter = 0


          self.counter += 1                                            '''
                                                                       Setup
                                                                       '''
class Level():
    def ground(lvl, gloc, tx, ty):                                     backdrop = pygame.image.load(os.path.join('images', 'stage.png'))
          ground_list = pygame.sprite.Group()                          clock = pygame.time.Clock()
          i = 0                                                        pygame.init()
          if lvl == 1:                                                 backdropbox = world.get_rect()
                while i < len(gloc):                                   main = True
                     ground = 
                              Platform(gloc[i], worldy - ty, tx, ty,
                               'tile-ground.png')                      player = Player()      # spawn player
                     ground_list.add(ground)                           player.rect.x = 0      # go to x
                     i = i + 1                                         player.rect.y = 30       # go to y
                                                                       player_list = pygame.sprite.Group()
          if lvl == 2:                                                 player_list.add(player)
                print("Level " + str(lvl) )                            steps = 10


          return ground_list                                           eloc = []
                                                                       eloc = [300, 0]
   def bad(lvl, eloc):                                                 enemy_list = Level.bad(1, eloc )
          if lvl == 1:
                enemy = Enemy(eloc[0],eloc[1],'enemy.png')             gloc = []
                enemy_list = pygame.sprite.Group()                     tx     = 64
                enemy_list.add(enemy)                                  ty     = 64
          if lvl == 2:
                print("Level " + str(lvl) )                            i = 0
                                                                       while i <= (worldx / tx) + tx:
          return enemy_list                                                  gloc.append(i * tx)
                                                                             i = i + 1
    # x location, y location, img width, img height, img file
    def platform(lvl,tx,ty):                                           ground_list = Level.ground(1, gloc, tx, ty)
    plat_list = pygame.sprite.Group()                                  plat_list = Level.platform(1, tx, ty)
    ploc = []
    i=0




A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                    . CC BY-SA 4.0 . OPENSOURCE.COM                                            35
PUT PLATFORMS IN A PYTHON GAME WITH PYGAME . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


     '''                                                                                 if event.key == pygame.K_RIGHT or event.key == ord('d'):
     Main Loop                                                                                player.control(-steps, 0)
     '''
                                                                                 world.blit(backdrop, backdropbox)
     while main:                                                                 player.update()
           for event in pygame.event.get():                                      player_list.draw(world)
               if event.type == pygame.QUIT:                                     enemy_list.draw(world)
                   pygame.quit()                                                 ground_list.draw(world)
                   try:                                                          plat_list.draw(world)
                          sys.exit()                                             for e in enemy_list:
                   finally:                                                           e.move()
                        main = False                                             pygame.display.flip()
                                                                                 clock.tick(fps)
              if event.type == pygame.KEYDOWN:
                   if event.key == ord('q'):
                       pygame.quit()                                          Links
                       try:                                                   [1]	https://www.pygame.org/news
                            sys.exit()                                        [2] https://opensource.com/article/17/12/game-python-add-a-
                       finally:                                                    player
                            main = False                                      [3]	https://opensource.com/article/17/12/game-python-
                   if event.key == pygame.K_LEFT or event.key == ord('a'):         moving-player
                       player.control(-steps, 0)                              [4]	https://en.wikipedia.org/wiki/Cartesian_coordinate_system
                   if event.key == pygame.K_RIGHT or event.key == ord('d'):   [5]	https://krita.org/en/
                       player.control(steps, 0)                               [6] https://www.gimp.org/
                   if event.key == pygame.K_UP or event.key == ord('w'):      [7] http://mypaint.org/about/
                       print('jump')                                          [8] https://inkscape.org/en/
                                                                              [9]	https://opensource.com/article/20/1/what-creative-
              if event.type == pygame.KEYUP:                                       commons
                   if event.key == pygame.K_LEFT or event.key == ord('a'):    [10] https://kenney.nl/assets/simplified-platformer-pack
                       player.control(steps, 0)                               [11]	
                                                                                   https://opengameart.org/content/simplified-platformer-pack




     36                                    A GUIDE TO BUILDING A VIDEO GAME IN PYTHON              . CC BY-SA 4.0 . OPENSOURCE.COM
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . SIMULATE GRAVITY IN YOUR PYTHON GAME




Simulate gravity in your Python game
Learn how to program video games with Python’s Pygame module and start manipulating gravity.


THE REAL WORLD                             is full of movement
                                           and life. The thing
that makes the real world so busy and dynamic is physics.
                                                                    other words, you have programmed your player to always be
                                                                    falling. That’s basically gravity.
                                                                       For the gravity function to have an effect, you must call it in
Physics is the way matter moves through space. Since a              your main loop. This way, Python applies the falling motion to
video game world has no matter, it also has no physics, so          your player once every clock tick.
game programmers have to simulate physics.                             In this code, add the first line to your loop:
   In terms of most video games, there are basically only two
aspects of physics that are important: gravity and collision.           player.gravity() # check gravity
   You implemented some collision detection when you add-               player.update()
ed an enemy [1] to your game, but this article adds more
because gravity requires collision detection. Think about           Launch your game to see what happens. Look sharp, be-
why gravity might involve collisions. If you can’t think of any     cause it happens fast: your player falls out of the sky, right
reasons, don’t worry—it’ll become apparent as you work              off your game screen.
through the sample code.                                               Your gravity simulation is working, but maybe too well.
   Gravity in the real world is the tendency for objects with          As an experiment, try changing the rate at which your
mass to be drawn toward one another. The larger the ob-             player falls.
ject, the more gravitational influence it exerts. In video game
physics, you don’t have to create objects with mass great           Adding a floor to gravity
enough to justify a gravitational pull; you can just program        The problem with your character falling off the world is that there’s
a tendency for objects to fall toward the presumed largest          no way for your game to detect it. In some games, if a player falls
object in the video game world: the world itself.                   off the world, the sprite is deleted and respawned somewhere
                                                                    new. In other games, the player loses points or a life. Whatever
Adding a gravity function                                           you want to happen when a player falls off the world, you have
Remember that your player already has a property to de-             to be able to detect when the player disappears offscreen.
termine motion. Use this property to pull the player sprite            In Python, to check for a condition, you can use an if
toward the bottom of the screen.                                    statement.
   In Pygame, higher numbers are closer to the bottom edge             You must check to see if your player is falling and how far
of the screen.                                                      your player has fallen. If your player falls so far that it reach-
   In the real world, gravity affects everything. In platformers,   es the bottom of the screen, then you can do something. To
however, gravity is selective—if you add gravity to your entire     keep things simple, set the position of the player sprite to 20
game world, all of your platforms would fall to the ground. In-     pixels above the bottom edge.
stead, you add gravity just to your player and enemy sprites.          Make your gravity function look like this:
   First, add a gravity function in your Player class:
                                                                        def gravity(self):
    def gravity(self):                                                      self.movey += 3.2 # how fast player falls
        self.movey += 3.2 # how fast player falls
                                                                            if self.rect.y > worldy and self.movey >= 0:
This is a simple function. First, you set your player in vertical                self.movey = 0
motion, whether your player wants to be in motion or not. In                     self.rect.y = worldy-ty




A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                 . CC BY-SA 4.0 . OPENSOURCE.COM                                            37
SIMULATE GRAVITY IN YOUR PYTHON GAME. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


     Then launch your game. Your sprite still falls, but it stops at     BLUE = (25, 25, 200)
     the bottom of the screen. You may not be able to see your           BLACK = (23, 23, 23)
     sprite behind the ground layer, though. An easy fix is to make      WHITE = (254, 254, 254)
     your player sprite bounce higher by adding another -ty to its       ALPHA = (0, 255, 0)
     new Y position after it hits the bottom of the game world:
                                                                         '''
           def gravity(self):                                            Objects
               self.movey += 3.2 # how fast player falls                 '''


               if self.rect.y > worldy and self.movey >= 0:              # x location, y location, img width, img height, img file
                    self.movey = 0                                       class Platform(pygame.sprite.Sprite):
                    self.rect.y = worldy-ty-ty                                 def __init__(self, xloc, yloc, imgw, imgh, img):
                                                                                     pygame.sprite.Sprite.__init__(self)
     Now your player bounces at the bottom of the screen, just                       self.image = 
                                                                                                  pygame.image.load(os.path.join
     behind your ground sprites.                                                                   ('images', img)).convert()
       What your player really needs is a way to fight gravity. The                  self.image.convert_alpha()
     problem with gravity is, you can’t fight it unless you have                     self.image.set_colorkey(ALPHA)
     something to push off of. So, in the next article, you’ll add                   self.rect = self.image.get_rect()
     ground and platform collision and the ability to jump. In the                   self.rect.y = yloc
     meantime, try applying gravity to the enemy sprite.                             self.rect.x = xloc
       Here’s all the code so far:
                                                                         class Player(pygame.sprite.Sprite):
     #!/usr/bin/env python3                                                    """
     # by Seth Kenlon                                                          Spawn a player
                                                                               """
     # GPLv3
     # This program is free software: you can redistribute it and/or           def __init__(self):
     # modify it under the terms of the GNU General Public License as                pygame.sprite.Sprite.__init__(self)
     # published by the Free Software Foundation, either version 3 of                self.movex = 0
     # the License, or (at your option) any later version.                           self.movey = 0
     #                                                                               self.frame = 0
     # This program is distributed in the hope that it will be useful,               self.health = 10
     # but WITHOUT ANY WARRANTY; without even the implied warranty of                self.images = []
     # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.   See the                for i in range(1, 5):
     # GNU General Public License for more details.                                        img = 
                                                                                                 pygame.image.load(os.path.join('images',
     #                                                                                           'hero' + str(i) + '.png')).convert()
     # You should have received a copy of the GNU General Public                           img.convert_alpha()
     # License along with this program.   If not, see                                      img.set_colorkey(ALPHA)
     # <http://www.gnu.org/licenses/>.                                                     self.images.append(img)
                                                                                           self.image = self.images[0]
     import pygame                                                                         self.rect = self.image.get_rect()
     import sys
     import os                                                                 def gravity(self):
                                                                                     self.movey += 3.2
     '''                                                                             if self.rect.y > worldy and self.movey >= 0:
     Variables                                                                             self.movey = 0
     '''                                                                                   self.rect.y = worldy-ty-ty


     worldx = 960                                                              def control(self, x, y):
     worldy = 720                                                                    """
     fps = 40                                                                        control player movement
     ani = 4                                                                         """
     world = pygame.display.set_mode([worldx, worldy])                               self.movex += x
                                                                                     self.movey += y




     38                               A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                   . CC BY-SA 4.0 . OPENSOURCE.COM
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . SIMULATE GRAVITY IN YOUR PYTHON GAME


                                                                                elifself.counter >= distance and self.counter
    def update(self):                                                               <= distance*2:
         """                                                                        self.rect.x -= speed
         Update sprite position                                                 else:
         """                                                                        self.counter = 0


         self.rect.x = self.rect.x + self.movex                                 self.counter += 1
         self.rect.y = self.rect.y + self.movey


         # moving left                                                class Level():
         if self.movex < 0:                                               def ground(lvl, gloc, tx, ty):
               self.frame += 1                                                  ground_list = pygame.sprite.Group()
               if self.frame > 3*ani:                                           i = 0
                   self.frame = 0                                               if lvl == 1:
               self.image = 
                            pygame.transform.flip(self.images[self.                 while i < len(gloc):
                              frame // ani], True, False)                                 ground = 
                                                                                                   Platform(gloc[i], worldy - ty, tx, ty,
                                                                                                     'tile-ground.png')
         # moving right                                                                   ground_list.add(ground)
         if self.movex > 0:                                                               i = i + 1
               self.frame += 1
               if self.frame > 3*ani:                                           if lvl == 2:
                   self.frame = 0                                                  print("Level " + str(lvl) )
               self.image = self.images[self.frame//ani]
                                                                                return ground_list
         hit_list = 
                    pygame.sprite.spritecollide(self, enemy_list,
                     False)                                              def bad(lvl, eloc):
         for enemy in hit_list:                                                 if lvl == 1:
               self.health -= 1                                                    enemy = Enemy(eloc[0],eloc[1],'enemy.png')
               print(self.health)                                                  enemy_list = pygame.sprite.Group()
                                                                                   enemy_list.add(enemy)
                                                                                if lvl == 2:
class Enemy(pygame.sprite.Sprite):                                                 print("Level " + str(lvl) )
   """
   Spawn an enemy                                                               return enemy_list
   """
   def __init__(self, x, y, img):                                         # x location, y location, img width, img height, img file
         pygame.sprite.Sprite.__init__(self)                              def platform(lvl,tx,ty):
         self.image = pygame.image.load(os.path.join('images',img))       plat_list = pygame.sprite.Group()
         self.image.convert_alpha()                                       ploc = []
         self.image.set_colorkey(ALPHA)                                   i=0
         self.rect = self.image.get_rect()                                if lvl == 1:
         self.rect.x = x                                                        ploc.append((200,worldy-ty-128,3))
         self.rect.y = y                                                        ploc.append((300,worldy-ty-256,3))
         self.counter = 0                                                       ploc.append((500,worldy-ty-128,4))
                                                                                while i < len(ploc):
    def move(self):                                                                 j=0
         '''                                                                        while j <= ploc[i][2]:
         enemy movement                                                                   plat = 
                                                                                                 Platform((ploc[i][0]+(j*tx)),ploc[i]
         '''                                                                                      [1],tx,ty,'tile.png')
         distance = 80                                                                    plat_list.add(plat)
         speed = 8                                                                        j=j+1
                                                                                    print('run' + str(i) + str(ploc[i]))
         if self.counter >= 0 and self.counter <= distance:                         i=i+1
               self.rect.x += speed




A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                    . CC BY-SA 4.0 . OPENSOURCE.COM                                           39
SIMULATE GRAVITY IN YOUR PYTHON GAME. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


           if lvl == 2:                                                  while main:
               print("Level " + str(lvl) )                                  for event in pygame.event.get():
                                                                                 if event.type == pygame.QUIT:
           return plat_list                                                            pygame.quit()
                                                                                       try:
                                                                                              sys.exit()
     '''                                                                               finally:
     Setup                                                                                  main = False
     '''
                                                                                 if event.type == pygame.KEYDOWN:
     backdrop = pygame.image.load(os.path.join('images', 'stage.png'))                 if event.key == ord('q'):
     clock = pygame.time.Clock()                                                           pygame.quit()
     pygame.init()                                                                         try:
     backdropbox = world.get_rect()                                                             sys.exit()
     main = True                                                                           finally:
                                                                                                main = False
     player = Player()    # spawn player                                               if event.key == pygame.K_LEFT or event.key == ord('a'):
     player.rect.x = 0    # go to x                                                        player.control(-steps, 0)
     player.rect.y = 30       # go to y                                                if event.key == pygame.K_RIGHT or event.key == ord('d'):
     player_list = pygame.sprite.Group()                                                   player.control(steps, 0)
     player_list.add(player)                                                           if event.key == pygame.K_UP or event.key == ord('w'):
     steps = 10                                                                            print('jump')


     eloc = []                                                                   if event.type == pygame.KEYUP:
     eloc = [300, 0]                                                                   if event.key == pygame.K_LEFT or event.key == ord('a'):
     enemy_list = Level.bad(1, eloc )                                                      player.control(steps, 0)
                                                                                       if event.key == pygame.K_RIGHT or event.key == ord('d'):
     gloc = []                                                                              player.control(-steps, 0)
     tx     = 64
     ty     = 64                                                            world.blit(backdrop, backdropbox)
                                                                            player.update()
     i = 0                                                                  player_list.draw(world)
     while i <= (worldx / tx) + tx:                                         enemy_list.draw(world)
           gloc.append(i * tx)                                              ground_list.draw(world)
           i = i + 1                                                        plat_list.draw(world)
                                                                            for e in enemy_list:
     ground_list = Level.ground(1, gloc, tx, ty)                                 e.move()
     plat_list = Level.platform(1, tx, ty)                                  pygame.display.flip()
                                                                            clock.tick(fps)
     '''
     Main Loop                                                           Links
     '''                                                                 [1]	
                                                                             https://opensource.com/article/18/5/pygame-enemy




     40                                   A GUIDE TO BUILDING A VIDEO GAME IN PYTHON              . CC BY-SA 4.0 . OPENSOURCE.COM
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ADD JUMPING TO YOUR PYTHON PLATFORMER GAME




Add jumping to your
Python platformer game
Learn how to fight gravity with jumping in this installment on programming
video games with Python’s Pygame module.



IN THE PREVIOUS                          ARTICLE in this series,
                                         you simulated gravity,
but now you need to give your player a way to fight against
                                                                      hero sprite can either be falling or not falling, and it can be
                                                                      jumping or not jumping.
                                                                         The first variable (is_jumping) is set to True because I’m
gravity by jumping.                                                   spawing the hero in the sky and need it to fall immediately
   A jump is a temporary reprieve from gravity. For a few             to the ground, as if it were in mid-jump. This is a little count-
moments, you jump up instead of falling down, the way                 er-intuitive, because the hero isn’t actually jumping. The
gravity is pulling you. But once you hit the peak of your             hero has only just spawned. This is theoretically an abuse
jump, gravity kicks in again and pulls you back down to               of this Boolean value, and it is admittedly “cleaner” code to
earth.                                                                have True and False statements that actually reflect reality.
   In code, this translates to variables. First, you must estab-      However, I find it easier to let gravity help the hero find the
lish variables for the player sprite so that Python can track         ground rather than having to hard code a spawn position
whether or not the sprite is jumping. Once the player sprite          every level. It also evokes classic platformers, and gives
is jumping, then gravity is applied to the player sprite again,       the player the sense of “jumping into” the game world. In
pulling it back down to the nearest object.                           other words, this is a small initial lie that serves the pro-
                                                                      gram, so set it to True.
Setting jump state variables                                             The other variable (is_falling) is also set to True because
You must add two new variables to your Player class:                  the hero does indeed need to descend to the ground.

• O
   ne to track whether your player is jumping or not, deter-         Conditional gravity
  mined by whether or not your player sprite is standing on           In the real world, jumping is an act of moving against gravity.
  solid ground                                                        In your game, though, gravity only needs to be “on” when
• O
   ne to bring the player back down to the ground                    the hero sprite isn’t standing on solid ground. When you
                                                                      have gravity on all the time (in Pygame), you risk getting a
Add these variables to your Player class. In the following            bounce-effect on your hero sprite as gravity constantly tries
code, the lines above the comment are for context, so just            to force the hero down while the collision with the ground re-
add the final two lines:                                              sists. Not all game engines require this much interaction with
                                                                      gravity, but Pygame isn’t designed exclusively for platform-
        self.frame = 0                                                ers (you could write a top-down game instead, for example)
        self.health = 10                                              so gravity isn’t managed by the engine.
        # jump code below                                                Your code is only emulating gravity in your game world.
        self.is_jumping = True                                        The hero sprite isn’t actually falling when it appears to fall, it’s
        self.is_falling = False                                       being moved by your gravity function. To permit your hero
                                                                      sprite to fight gravity and jump, or to collide with solid objects
These new values are called Boolean values, which is a term           (like the ground and floating platforms), you must modify
(named after mathematician George Boole) meaning either               your gravity function to activate only when the hero is jump-
true or false. In programming, this is a special data type in-        ing. This code replaces the entire gravity function you wrote
dicating that a variable is either “on” or “off”. In this case, the   for the previous article:



A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                   . CC BY-SA 4.0 . OPENSOURCE.COM                                           41
ADD JUMPING TO YOUR PYTHON PLATFORMER GAME . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


          def gravity(self):                                               health points and respawn for falling off the world. That’s
              if self.is_jumping:                                          not strictly necessary; it’s just a common convention in plat-
                  self.movey += 3.2                                        formers.

     This causes your hero sprite to fall right through the bottom         Jumping in Pygame
     of the screen, but you can fix that with some collision detec-        The code to jump happens in several places. First, create a
     tion on the ground.                                                   jump function to “flip” the is_jumping and is_falling values:

     Programming solid ground                                                 def jump(self):
     In the previous article, a quick hack was implemented to                     if self.is_jumping is False:
     keep the hero sprite from falling through the bottom of the                      self.is_falling = False
     screen. It kept the hero on screen, but only by creating an                      self.is_jumping = True
     invisible wall across the bottom of the screen. It’s cleaner to
     use objects as objects, and besides it’s pretty common in             The actual lift-off from the jump action happens in the up-
     platformers to allow players to fall off the world as a penalty       date function of your Player class:
     for a poorly timed jump.
       In the update function of your Player class, add this code:                if self.is_jumping and self.is_falling is False:
                                                                                      self.is_falling = True
             ground_hit_list = 
                               pygame.sprite.spritecollide(self,                      self.movey -= 33    # how high to jump
                                   ground_list, False)
              for g in ground_hit_list:                                    This code executes only when the is_jumping variable
                  self.movey = 0                                           is True while the is_falling variable is False. When these
                  self.rect.bottom = g.rect.top                            conditions are satisfied, the hero sprite’s Y position is ad-
                  self.is_jumping = False     # stop jumping               justed to 33 pixels in the “air”. It’s negative 33 because a
                                                                           lower number on the Y axis in Pygame means it’s closer
             # fall off the world                                          to the top of the screen. That’s effectively a jump. You can
              if self.rect.y > worldy:                                     adjust the number of pixels for a lower or higher jump. This
                  self.health -=1                                          clause also sets is_falling to True, which prevents another
                  print(self.health)                                       jump from being registered. If you set it to False, a jump
                  self.rect.x = tx                                         action would compound on itself, shooting your hero into
                 self.rect.y = ty                                          space, which is fun to witness but not ideal for gameplay.

     This code block checks for collisions happening between               Calling the jump function
     ground sprites and the hero sprite. This is the same princi-          The problem is that nothing in your main loop is calling the
     ple you used when detecting a hit against your hero by an             jump function yet. You made a placeholder keypress for it
     enemy.                                                                early on, but right now, all the jump key does is print jump
        In the event of a collision, it uses builtin information provid-   to the terminal.
     ed by Pygame to find the bottom of the hero sprite (self.rect.           In your main loop, change the result of the Up arrow from
     bottom), and set its position to the top of the ground sprite         printing a debug statement to calling the jump function.
     (p.rect.top). This provides the illusion that the hero sprite is
     “standing” on the ground, and prevents it from falling through                   if event.key == 
                                                                                                      pygame.K_UP or event.key ==
     the ground.                                                                                         ord('w'):
        It also sets self.is_falling to 0 so that the program is                          player.jump()
     aware that the hero is not in mid-jump. Additionally, it sets
     self.movey to 0 so the hero is not pulled by gravity (it’s            If you would rather use the Spacebar for jumping, set the key
     a quirk of game physics that you don’t need to contin-                to pygame.K_SPACE instead of pygame.K_UP. Alternate-
     ue to pull a sprite toward Earth once the sprite has been             ly, you can use both (as separate if statements) so that the
     grounded).                                                            player has a choice.
        The if statement at the end detects whether the player
     has descended below the level of the ground; if so, it de-            Landing on a platform
     ducts health points as a penalty, and then respawns the               So far, you’ve defined an anti-gravity condition for when the
     hero sprite back at the top left of the screen (using the val-        player sprite hits the ground, but the game code keeps plat-
     ues of tx and ty, the size of tiles. as quick and easy starting       forms and the ground in separate lists. (As with so many
     values.) This assumes that you want your player to lose               choices made in this article, that’s not strictly necessary,



     42                                A GUIDE TO BUILDING A VIDEO GAME IN PYTHON               . CC BY-SA 4.0 . OPENSOURCE.COM
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ADD JUMPING TO YOUR PYTHON PLATFORMER GAME



and you can experiment with treating the ground as just an-              in the update function of your Player class, adding a state-
other platform.) To enable a player sprite to stand on top of            ment to activate gravity during movement. The two lines you
a platform, you must detect a collision between the player               need to add are commented:
sprite and a platform sprite, and stop gravity from “pulling”
it downward.                                                                       if self.movex < 0:
   Place this code into your update function:                                          self.is_jumping = True     # turn gravity on
                                                                                       self.frame += 1
        plat_hit_list = 
                        pygame.sprite.spritecollide(self,                              if self.frame > 3 * ani:
                          plat_list, False)                                                 self.frame = 0
        for p in plat_hit_list:                                                        self.image = 
                                                                                                    pygame.transform.flip(self.images[self.
             self.is_jumping = False    # stop jumping                                               frame // ani], True, False)
             self.movey = 0
                                                                                   if self.movex > 0:
             # approach from below                                                    self.is_jumping = True      # turn gravity on
             if self.rect.bottom <= p.rect.bottom:                                    self.frame += 1
                self.rect.bottom = p.rect.top                                         if self.frame > 3 * ani:
             else:                                                                          self.frame = 0
                self.movey += 3.2                                                     self.image = self.images[self.frame // ani]


This code scans through the list of platforms for any colli-             This activates gravity long enough to cause the hero sprite
sions with your hero sprite. If one is detected, then is_jump-           to fall to the ground upon a failed platform collision check.
ing is set to False and any movement in the sprite’s Y posi-                Try your game now. Everything works as expected, but try
tion is cancelled.                                                       changing some variables to see what’s possible.
   Platforms hang in the air, meaning the player can interact               In the next article, you’ll make your world scroll.
with them by approaching them from either above or below.                   Here’s all the code so far:
It’s up to you how you want the platforms to react to your
hero sprite, but it’s not uncommon to block a sprite from ac-            #!/usr/bin/env python3
cessing a platform from below. The code in the second code               # by Seth Kenlon
block treats platforms as a sort of ceiling or pergola, such
that the hero can jump onto a platform as long as it jumps               # GPLv3
higher than the platform’s topside, but obstructs the sprite             # This program is free software: you can redistribute it and/or
when it tries to jump from beneath:                                      # modify it under the terms of the GNU General Public License as
   The first clause of the if statement detects whether the              # published by the Free Software Foundation, either version 3 of
bottom of the hero sprite is less than (higher on the screen)            # the License, or (at your option) any later version.
than the platform. If it is, then the hero “lands” on the plat-          #
form, because the value of the bottom of the hero sprite is              # This program is distributed in the hope that it will be useful,
made equal to the top of the platform sprite. Otherwise, the             # but WITHOUT ANY WARRANTY; without even the implied warranty of
hero sprite’s Y position is increased, causing it to “fall” away         # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.       See the
from the platform.                                                       # GNU General Public License for more details.
                                                                         #
Falling                                                                  # You should have received a copy of the GNU General Public
If you try your game now, you find that jumping works mostly             # License along with this program.     If not, see
as expected, but falling isn’t consistent. For instance, after           # <http://www.gnu.org/licenses/>.
your hero jumps onto a platform, it can’t walk off of a platform
to fall to the ground. It just stays in the air, as if there was still   import pygame
a platform beneath it. However, you are able to cause the                import sys
hero to jump off of a platform.                                          import os
   The reason for this is the way gravity has been implement-
ed. Colliding with a platform turns gravity “off” so the hero            '''
sprite doesn’t fall through the platform. The problem is, noth-          Variables
ing turns gravity back on when the hero walks off the edge               '''
of a platform.
   You can force gravity to reactivate by activating gravity             worldx = 960
during the hero sprite’s movement. Edit the movement code                worldy = 720




A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                     . CC BY-SA 4.0 . OPENSOURCE.COM                                                  43
ADD JUMPING TO YOUR PYTHON PLATFORMER GAME . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


     fps = 40                                                               """
     ani = 4                                                               control player movement
     world = pygame.display.set_mode([worldx, worldy])                     """
                                                                            self.movex += x
     BLUE = (25, 25, 200)
     BLACK = (23, 23, 23)                                               def jump(self):
     WHITE = (254, 254, 254)                                                if self.is_jumping is False:
     ALPHA = (0, 255, 0)                                                          self.is_falling = False
                                                                                  self.is_jumping = True
     '''
     Objects                                                            def update(self):
     '''                                                                    """
                                                                           Update sprite position
     # x location, y location, img width, img height, img file             """
     class Platform(pygame.sprite.Sprite):
           def __init__(self, xloc, yloc, imgw, imgh, img):                # moving left
                 pygame.sprite.Sprite.__init__(self)                        if self.movex < 0:
                 self.image = 
                              pygame.image.load(os.path.join                      self.is_jumping = True
                              ('images', img)).convert()                          self.frame += 1
                 self.image.convert_alpha()                                       if self.frame > 3 * ani:
                 self.image.set_colorkey(ALPHA)                                       self.frame = 0
                 self.rect = self.image.get_rect()                                self.image = 
                                                                                               pygame.transform.flip(self.images[self.
                 self.rect.y = yloc                                                            frame // ani], True, False)
                 self.rect.x = xloc
                                                                           # moving right
                                                                           if self.movex > 0:
     class Player(pygame.sprite.Sprite):                                          self.is_jumping = True
           """                                                                    self.frame += 1
           Spawn a player                                                         if self.frame > 3 * ani:
           """                                                                        self.frame = 0
                                                                                  self.image = self.images[self.frame // ani]
           def __init__(self):
                 pygame.sprite.Sprite.__init__(self)                       # collisions
                 self.movex = 0                                            enemy_hit_list = 
                                                                                            pygame.sprite.spritecollide(self,
                 self.movey = 0                                                                enemy_list, False)
                 self.frame = 0                                            for enemy in enemy_hit_list:
                 self.health = 10                                                 self.health -= 1
                 self.is_jumping = True                                           # print(self.health)
                 self.is_falling = True
                 self.images = []                                          ground_hit_list = 
                                                                                             pygame.sprite.spritecollide(self,
                 for i in range(1, 5):                                                             ground_list, False)
                     img = 
                           pygame.image.load(os.path.join('images',        for g in ground_hit_list:
                            'hero' + str(i) + '.png')).convert()                  self.movey = 0
                     img.convert_alpha()                                          self.rect.bottom = g.rect.top
                     img.set_colorkey(ALPHA)                                      self.is_jumping = False     # stop jumping
                     self.images.append(img)
                     self.image = self.images[0]                           # fall off the world
                     self.rect = self.image.get_rect()                     if self.rect.y > worldy:
                                                                                  self.health -=1
           def gravity(self):                                                     print(self.health)
                 if self.is_jumping:                                              self.rect.x = tx
                    self.movey += 3.2                                             self.rect.y = ty


           def control(self, x, y):                                        plat_hit_list = 
                                                                                           pygame.sprite.spritecollide(self,




     44                                   A GUIDE TO BUILDING A VIDEO GAME IN PYTHON      . CC BY-SA 4.0 . OPENSOURCE.COM
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ADD JUMPING TO YOUR PYTHON PLATFORMER GAME


                             plat_list, False)                                         while i < len(gloc):
          for p in plat_hit_list:                                                            ground = 
                                                                                                      Platform(gloc[i], worldy - ty, tx, ty,
                self.is_jumping = False    # stop jumping                                             'tile-ground.png')
                self.movey = 0                                                               ground_list.add(ground)
                if self.rect.bottom <= p.rect.bottom:                                        i = i + 1
                   self.rect.bottom = p.rect.top
                else:                                                           if lvl == 2:
                   self.movey += 3.2                                                   print("Level " + str(lvl) )


          if self.is_jumping and self.is_falling is False:                      return ground_list
                self.is_falling = True
                self.movey -= 33   # how high to jump                        def bad(lvl, eloc):
                                                                                if lvl == 1:
          self.rect.x += self.movex                                                    enemy = Enemy(eloc[0],eloc[1],'enemy.png')
          self.rect.y += self.movey                                                    enemy_list = pygame.sprite.Group()
                                                                                       enemy_list.add(enemy)
class Enemy(pygame.sprite.Sprite):                                              if lvl == 2:
    """                                                                                print("Level " + str(lvl) )
    Spawn an enemy
    """                                                                         return enemy_list


    def __init__(self, x, y, img):                                           # x location, y location, img width, img height, img file
          pygame.sprite.Sprite.__init__(self)                                def platform(lvl,tx,ty):
          self.image = pygame.image.load(os.path.join('images',img))             plat_list = pygame.sprite.Group()
          self.image.convert_alpha()                                             ploc = []
          self.image.set_colorkey(ALPHA)                                         i=0
          self.rect = self.image.get_rect()                                      if lvl == 1:
          self.rect.x = x                                                        ploc.append((200, worldy - ty - 128, 3))
          self.rect.y = y                                                        ploc.append((300, worldy - ty - 256, 3))
          self.counter = 0                                                       ploc.append((500, worldy - ty - 128 , 4))
                                                                                 while i < len(ploc):
    def move(self):                                                                    j=0
          '''                                                                          while j <= ploc[i][2]:
          enemy movement                                                                     plat = 
                                                                                                    Platform((ploc[i][0] + (j*tx)),ploc[i]
          '''                                                                                       [1], tx, ty, 'tile.png')
          distance = 80                                                                      plat_list.add(plat)
          speed = 8                                                                          j = j + 1
                                                                                       print('run' + str(i) + str(ploc[i]))
          if self.counter >= 0 and self.counter <= distance:                           i = i + 1
                self.rect.x += speed
          elifself.counter >= distance and self.counter                     if lvl == 2:
                <= distance*2:                                                   print("Level " + str(lvl) )
                self.rect.x -= speed
          else:                                                              return plat_list
                self.counter = 0


          self.counter += 1                                            '''
                                                                       Setup
                                                                       '''
class Level():
    def ground(lvl, gloc, tx, ty):                                     backdrop = pygame.image.load(os.path.join('images', 'stage.png'))
          ground_list = pygame.sprite.Group()                          clock = pygame.time.Clock()
          i = 0                                                        pygame.init()
          if lvl == 1:                                                 backdropbox = world.get_rect()




A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                     . CC BY-SA 4.0 . OPENSOURCE.COM                                           45
ADD JUMPING TO YOUR PYTHON PLATFORMER GAME . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


     main = True                                                                    sys.exit()
                                                                             finally:
     player = Player()    # spawn player                                         main = False
     player.rect.x = 0    # go to x
     player.rect.y = 30    # go to y                                    if event.type == pygame.KEYDOWN:
     player_list = pygame.sprite.Group()                                    if event.key == ord('q'):
     player_list.add(player)                                                    pygame.quit()
     steps = 10                                                                 try:
                                                                                      sys.exit()
     eloc = []                                                                  finally:
     eloc = [300, 0]                                                                  main = False
     enemy_list = Level.bad(1, eloc )                                       if event.key == pygame.K_LEFT or event.key == ord('a'):
                                                                                player.control(-steps, 0)
     gloc = []                                                              if event.key == pygame.K_RIGHT or event.key == ord('d'):
     tx     = 64                                                                player.control(steps, 0)
     ty     = 64                                                            if event.key == pygame.K_UP or event.key == ord('w'):
                                                                                print('jump')
     i = 0
     while i <= (worldx / tx) + tx:                                     if event.type == pygame.KEYUP:
           gloc.append(i * tx)                                              if event.key == pygame.K_LEFT or event.key == ord('a'):
           i = i + 1                                                            player.control(steps, 0)
                                                                            if event.key == pygame.K_RIGHT or event.key == ord('d'):
     ground_list = Level.ground(1, gloc, tx, ty)                                 player.control(-steps, 0)
     plat_list = Level.platform(1, tx, ty)
                                                                     world.blit(backdrop, backdropbox)
     '''                                                             player.update()
     Main Loop                                                       player_list.draw(world)
     '''                                                             enemy_list.draw(world)
                                                                     ground_list.draw(world)
     while main:                                                     plat_list.draw(world)
           for event in pygame.event.get():                          for e in enemy_list:
               if event.type == pygame.QUIT:                             e.move()
                   pygame.quit()                                     pygame.display.flip()
                   try:                                              clock.tick(fps)




     46                                A GUIDE TO BUILDING A VIDEO GAME IN PYTHON       . CC BY-SA 4.0 . OPENSOURCE.COM
. . . . . . . . . . . . . . . . . . . ENABLE YOUR PYTHON GAME PLAYER TO RUN FORWARD AND BACKWARD




Enable your Python game player
to run forward and backward
Let your player run free by enabling the side-scroller effect in your Python platformer using the
Pygame module.


IN PREVIOUS                   ENTRIES of this series about creat-
                              ing video games in Python 3 [1]
using the Pygame module [2], you designed your level-de-
                                                                           # scroll the world backward
                                                                           if player.rect.x <= backwardx:
                                                                                   scroll = backwardx - player.rect.x
sign layout, but some portion of your level probably extended                      player.rect.x = backwardx
past your viewable screen. The ubiquitous solution to that                         for p in plat_list:
problem in platformer games is, as the term “side-scroller”                                p.rect.x += scroll
suggests, scrolling.
   The key to scrolling is to make the platforms around the            # scrolling code above
player sprite move when the player sprite gets close to the            world.blit(backdrop, backdropbox)
edge of the screen. This provides the illusion that the screen         player.gravity() # check gravity
is a “camera” panning across the game world.                           player.update()
   This scrolling trick requires two dead zones at either edge
of the screen, at which point your avatar stands still while the    Launch your game and try it out.
world scrolls by.

Putting the scroll in side-scroller
You need one trigger point to go forward and another if you
want your player to be able to go backward. These two
points are simply two variables. Set them each about 100 or
200 pixels from each screen edge. Create the variables in
your variables section:

forwardx    = 600
backwardx = 230


In the main loop, check to see whether your hero sprite is at the
forwardx or backwardx scroll point. If so, move all platforms
either left or right, depending on whether the world is moving      Scrolling works as expected, but you may notice a small
forward or backward. In the following code, the final three lines   problem that happens when you scroll the world around your
of code are only for your reference (be careful not to place this   player and non-player sprites: the enemy sprite doesn't scroll
code in the for loop checking for keyboard events):                 along with the world. Unless you want your enemy sprite to
                                                                    pursue your player endlessly, you need to modify the enemy
        # scroll the world forward                                  code so that when your player makes an expeditious retreat,
           if player.rect.x >= forwardx:                            the enemy is left behind.
                    scroll = player.rect.x - forwardx
                    player.rect.x = forwardx                        Enemy scroll
                    for p in plat_list:                             In your main loop, you must apply the same rules for scroll-
                            p.rect.x -= scroll                      ing platforms to your enemy’s position. Because your game



A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                 . CC BY-SA 4.0 . OPENSOURCE.COM                                     47
ENABLE YOUR PYTHON GAME PLAYER TO RUN FORWARD AND BACKWARD . . . . . . . . . . . . . . . . . . . .


    world will (presumably) have more than one enemy in it, the         '''
    rules are applied to your enemy list rather than an individual      Variables
    enemy sprite. That’s one of the advantages of grouping sim-         '''
    ilar elements into lists.
       The first two lines are for context, so just add the final two   worldx = 960
    to your main loop:                                                  worldy = 720
                                                                        fps = 40
         # scroll the world forward                                     ani = 4
         if player.rect.x >= forwardx:                                  world = pygame.display.set_mode([worldx, worldy])
              scroll =   ΩAAAAAAAAAAA player.rect.x - forwardx          forwardx     = 600
              player.rect.x = forwardx                                  backwardx = 230
              for p in plat_list:
                  p.rect.x -= scroll                                    BLUE = (25, 25, 200)
              for e in enemy_list:       # enemy scroll                 BLACK = (23, 23, 23)
                  e.rect.x -= scroll     # enemy scroll                 WHITE = (254, 254, 254)
                                                                        ALPHA = (0, 255, 0)
    To scroll in the other direction (again, only add the final two
    lines to your existing code):                                       '''
                                                                        Objects
         # scroll the world backward                                    '''
         if player.rect.x <= backwardx:
              scroll = backwardx - player.rect.x                        # x location, y location, img width, img height, img file
              player.rect.x = backwardx                                 class Platform(pygame.sprite.Sprite):
              for p in plat_list:                                             def __init__(self, xloc, yloc, imgw, imgh, img):
                  p.rect.x += scroll                                                pygame.sprite.Sprite.__init__(self)
              for e in enemy_list:       # enemy scroll                             self.image = 
                                                                                                 pygame.image.load(os.path.join
                  e.rect.x += scroll     # enemy scroll                                          ('images', img)).convert()
                                                                                    self.image.convert_alpha()
    Launch the game again and see what happens.                                     self.image.set_colorkey(ALPHA)
      Here’s all the code you’ve written for this Python platform-                  self.rect = self.image.get_rect()
    er so far:                                                                      self.rect.y = yloc
                                                                                    self.rect.x = xloc
    #!/usr/bin/env python3
    # by Seth Kenlon
                                                                        class Player(pygame.sprite.Sprite):
    # GPLv3                                                                   """
    # This program is free software: you can redistribute it and/or           Spawn a player
    # modify it under the terms of the GNU General Public License as          """
    # published by the Free Software Foundation, either version 3 of
    # the License, or (at your option) any later version.                     def __init__(self):
    #                                                                               pygame.sprite.Sprite.__init__(self)
    # This program is distributed in the hope that it will be useful,               self.movex = 0
    # but WITHOUT ANY WARRANTY; without even the implied warranty of                self.movey = 0
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.   See the                self.frame = 0
    # GNU General Public License for more details.                                  self.health = 10
    #                                                                               self.is_jumping = True
    # You should have received a copy of the GNU General Public                     self.is_falling = True
    # License along with this program.     If not, see                              self.images = []
    # <http://www.gnu.org/licenses/>.                                               for i in range(1, 5):
                                                                                        img = 
                                                                                              pygame.image.load(os.path.join('images',
    import pygame                                                                              'hero' + str(i) + '.png')).convert()
    import sys                                                                          img.convert_alpha()
    import os                                                                           img.set_colorkey(ALPHA)
                                                                                        self.images.append(img)




    48                                 A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                . CC BY-SA 4.0 . OPENSOURCE.COM
. . . . . . . . . . . . . . . . . . . ENABLE YOUR PYTHON GAME PLAYER TO RUN FORWARD AND BACKWARD



            self.image = self.images[0]                                     # fall off the world
            self.rect = self.image.get_rect()                               if self.rect.y > worldy:
                                                                                  self.health -=1
  def gravity(self):                                                              print(self.health)
      if self.is_jumping:                                                         self.rect.x = tx
            self.movey += 3.2                                                     self.rect.y = ty


  def control(self, x, y):                                                  plat_hit_list = 
                                                                                            pygame.sprite.spritecollide(self,
      """                                                                                      plat_list, False)
     control player movement                                                for p in plat_hit_list:
     """                                                                          self.is_jumping = False    # stop jumping
      self.movex += x                                                             self.movey = 0
                                                                                  if self.rect.bottom <= p.rect.bottom:
  def jump(self):                                                                    self.rect.bottom = p.rect.top
      if self.is_jumping is False:                                                else:
            self.is_falling = False                                                  self.movey += 3.2
            self.is_jumping = True
                                                                            if self.is_jumping and self.is_falling is False:
  def update(self):                                                               self.is_falling = True
      """                                                                         self.movey -= 33   # how high to jump
     Update sprite position
     """                                                                    self.rect.x += self.movex
                                                                            self.rect.y += self.movey
     # moving left
      if self.movex < 0:                                           class Enemy(pygame.sprite.Sprite):
            self.is_jumping = True                                    """
            self.frame += 1                                           Spawn an enemy
            if self.frame > 3 * ani:                                  """
                self.frame = 0
            self.image = 
                         pygame.transform.flip(self.images[self.      def __init__(self, x, y, img):
                         frame // ani], True, False)                        pygame.sprite.Sprite.__init__(self)
                                                                            self.image = pygame.image.load(os.path.join('images',img))
     # moving right                                                         self.image.convert_alpha()
     if self.movex > 0:                                                     self.image.set_colorkey(ALPHA)
            self.is_jumping = True                                          self.rect = self.image.get_rect()
            self.frame += 1                                                 self.rect.x = x
            if self.frame > 3 * ani:                                        self.rect.y = y
                self.frame = 0                                              self.counter = 0
            self.image = self.images[self.frame // ani]
                                                                       def move(self):
     # collisions                                                           '''
     enemy_hit_list = 
                      pygame.sprite.spritecollide(self,                     enemy movement
                         enemy_list, False)                                 '''
     for enemy in enemy_hit_list:                                           distance = 80
            self.health -= 1                                                speed = 8
            # print(self.health)
                                                                            if self.counter >= 0 and self.counter <= distance:
     ground_hit_list = 
                       pygame.sprite.spritecollide(self,                          self.rect.x += speed
                             ground_list, False)                            elifself.counter >= distance and self.counter
     for g in ground_hit_list:                                                    <= distance*2:
            self.movey = 0                                                        self.rect.x -= speed
            self.rect.bottom = g.rect.top                                   else:
            self.is_jumping = False     # stop jumping                            self.counter = 0




A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                . CC BY-SA 4.0 . OPENSOURCE.COM                                           49
ENABLE YOUR PYTHON GAME PLAYER TO RUN FORWARD AND BACKWARD . . . . . . . . . . . . . . . . . . . .


             self.counter += 1                                             '''
                                                                           Setup
                                                                           '''
    class Level():
         def ground(lvl, gloc, tx, ty):                                    backdrop = pygame.image.load(os.path.join('images', 'stage.png'))
             ground_list = pygame.sprite.Group()                           clock = pygame.time.Clock()
             i = 0                                                         pygame.init()
             if lvl == 1:                                                  backdropbox = world.get_rect()
                   while i < len(gloc):                                    main = True
                         ground = 
                                  Platform(gloc[i], worldy - ty, tx, ty,
                                  'tile-ground.png')                       player = Player()        # spawn player
                         ground_list.add(ground)                           player.rect.x = 0        # go to x
                         i = i + 1                                         player.rect.y = 30        # go to y
                                                                           player_list = pygame.sprite.Group()
            if lvl == 2:                                                   player_list.add(player)
                   print("Level " + str(lvl) )                             steps = 10


            return ground_list                                             eloc = []
                                                                           eloc = [300, 0]
         def bad(lvl, eloc):                                               enemy_list = Level.bad(1, eloc )
            if lvl == 1:
                   enemy = Enemy(eloc[0],eloc[1],'enemy.png')              gloc = []
                   enemy_list = pygame.sprite.Group()                      tx     = 64
                   enemy_list.add(enemy)                                   ty     = 64
            if lvl == 2:
                   print("Level " + str(lvl) )                             i = 0
                                                                           while i <= (worldx / tx) + tx:
            return enemy_list                                                    gloc.append(i * tx)
                                                                                 i = i + 1
         # x location, y location, img width, img height, img file
         def platform(lvl,tx,ty):                                          ground_list = Level.ground(1, gloc, tx, ty)
             plat_list = pygame.sprite.Group()                             plat_list = Level.platform(1, tx, ty)
             ploc = []
             i=0                                                           '''
             if lvl == 1:                                                  Main Loop
             ploc.append((200, worldy - ty - 128, 3))                      '''
             ploc.append((300, worldy - ty - 256, 3))
             ploc.append((500, worldy - ty - 128 , 4))                     while main:
             while i < len(ploc):                                                for event in pygame.event.get():
                   j=0                                                               if event.type == pygame.QUIT:
                   while j <= ploc[i][2]:                                                pygame.quit()
                         plat = 
                                Platform((ploc[i][0] + (j*tx)),ploc[i]                   try:
                                [1], tx, ty, 'tile.png')                                        sys.exit()
                         plat_list.add(plat)                                             finally:
                         j = j + 1                                                            main = False
                   print('run' + str(i) + str(ploc[i]))
                   i = i + 1                                                        if event.type == pygame.KEYDOWN:
                                                                                         if event.key == ord('q'):
         if lvl == 2:                                                                        pygame.quit()
             print("Level " + str(lvl) )                                                     try:
                                                                                                  sys.exit()
         return plat_list                                                                    finally:
                                                                                                  main = False
                                                                                         if event.key == pygame.K_LEFT or event.key == ord('a'):




    50                                     A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                . CC BY-SA 4.0 . OPENSOURCE.COM
. . . . . . . . . . . . . . . . . . . ENABLE YOUR PYTHON GAME PLAYER TO RUN FORWARD AND BACKWARD



             player.control(-steps, 0)                                            scroll = backwardx - player.rect.x
         if event.key == pygame.K_RIGHT or event.key == ord('d'):                 player.rect.x = backwardx
             player.control(steps, 0)                                             for p in plat_list:
         if event.key == pygame.K_UP or event.key == ord('w'):                           p.rect.x += scroll
             print('jump')                                                    for e in enemy_list:      # enemy scroll
                                                                                  e.rect.x += scroll    # enemy scroll
     if event.type == pygame.KEYUP:
         if event.key == pygame.K_LEFT or event.key == ord('a'):          world.blit(backdrop, backdropbox)
             player.control(steps, 0)                                     player.update()
         if event.key == pygame.K_RIGHT or event.key == ord('d'):         player.gravity()
              player.control(-steps, 0)                                   player_list.draw(world)
                                                                          enemy_list.draw(world)
  # scroll the world forward                                              ground_list.draw(world)
  if player.rect.x >= forwardx:                                           plat_list.draw(world)
          scroll = player.rect.x - forwardx                               for e in enemy_list:
          player.rect.x = forwardx                                            e.move()
          for p in plat_list:                                             pygame.display.flip()
                  p.rect.x -= scroll                                      clock.tick(fps)
      for e in enemy_list:      # enemy scroll
          e.rect.x -= scroll    # enemy scroll
                                                                    Links
  # scroll the world backward                                       [1]   https://www.python.org/
  if player.rect.x <= backwardx:                                    [2]   https://www.pygame.org/news




A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                 . CC BY-SA 4.0 . OPENSOURCE.COM                               51
PUT SOME LOOT IN YOUR PYTHON PLATFORMER GAME . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .




     Put some loot in your Python
     platformer game
     Give your players some treasures to collect and boost their score in this installment
     on programming video games with Python's Pygame module.


    IF YOU’VE                FOLLOWED along with the previous ar-
                             ticles in this series, then you know
     all the basics of programming video game mechanics. You
                                                                         def loot(lvl):
                                                                             if lvl == 1:
                                                                                 loot_list = pygame.sprite.Group()
     can build upon these basics to create a fully functional vid-               loot = Platform(tx*9, ty*5, tx, ty, 'loot_1.png')
     eo game all your own. Following a “recipe” like the code                    loot_list.add(loot)
     samples in this series is helpful when you’re first learning,
     but eventually, the recipe becomes a constraint. It’s time              if lvl == 2:
     to use the principles you’ve learned and apply them in                      print(lvl)
     new ways.
        If that sounds easier said than done, this article demon-            return loot_list
     strates an example of how to leverage what you already
     know for new purposes. Specifically, it covers how to imple-    In this code, I express the location of the loot as multiples of
     ment a looting system using what you have already learned       the tile size: tx on the X axis and ty for the Y axis. I do this
     about platforms from previous lessons.                          because i mapped my level on graph paper, so it’s easy to
        In most video games, you have the opportunity to “loot,”     just count the squares on my map and then multiply it by the
     or collect treasures and other items within the game world.     tile size, rather than calculating the pixel count. This is espe-
     Loot usually increases your score or your health or provides    cially true for very long levels. You can hard code the pixel
     information leading to your next quest.                         count, if you prefer.
        Including loot in your game is similar to programming            You can add as many loot objects as you like; just remem-
     platforms. Like platforms, loot has no user controls, scrolls   ber to add each one to your loot list. The arguments for the
     with the game world, and must check for collisions with the     Platform class are the X position, the Y position, the width
     player sprite.                                                  and height of the loot sprite (it’s usually easiest to keep your
        Before you begin, you must have a loot graphic, such as      loot sprite the same size as all other tiles), and the image you
     a coin or a treasure chest. If you’ve already downloaded my     want to use as loot. Placement of loot can be just as complex
     recommended tile set, the simplified-platformer-pack from       as mapping platforms, so use the level design document you
     Kenney.nl [1], then you can use a diamond or key from that.     created when creating the level.
                                                                         Call your new loot function in the Setup section of your
     Creating the loot function                                      script. In the following code, the first three lines are for con-
     Loot is so similar to platforms that you don’t even need a      text, so just add the fourth:
     Loot class. You can just reuse the Platform class and call
     the results loot.                                               loot_list = Level.loot(1)
        Since loot type and placement probably differ from lev-
     el to level, create a new function called loot in your Lev-     As you know by now, the loot won’t get drawn to the screen un-
     el class, if you don’t already have one. Since loot items       less you include it in your main loop. Add this line to your loop:
     are not platforms, you must also create a new loot_list
     group and then add loot objects to it. As with platforms,           loot_list.draw(world)
     ground, and enemies, this group is used when checking
     for collisions:                                                 Launch your game to see what happens.



     52                           A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                . CC BY-SA 4.0 . OPENSOURCE.COM
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . PUT SOME LOOT IN YOUR PYTHON PLATFORMER GAME



                                                                                        loot_hit_list = 
                                                                                                        pygame.sprite.spritecollide(self,
                                                                                                         loot_list, False)
                                                                                        for loot in loot_hit_list:
                                                                                                  loot_list.remove(loot)
                                                                                                  self.score += 1
                                                                                        print(self.score)


                                                                                 plat_hit_list = 
                                                                                                 pygame.sprite.spritecollide(self,
                                                                                                  plat_list, False)


                                                                       Not only do you remove the loot object from its group
                                                                       when a collision happens, but you also award your player
                                                                       a bump in score. You haven’t created a score variable yet,
                                                                       so add that to your player’s properties, created in the __
                                                                       init__ function of the Player class. In the following code,
                                                                       the first two lines are for context, so just add the score
Your loot objects are spawned, but they don’t do anything              variable:
when your player runs into them, nor do they scroll when
your player runs past them. Fix these issues next.                               self.frame = 0
                                                                                 self.health = 10
Scrolling loot                                                                   self.score = 0
Like platforms, loot has to scroll when the player moves
through the game world. The logic is identical to platform             Applying what you know
scrolling. To scroll the loot forward, add the last two lines:         As you can see, you’ve got all the basics. All you have to
                                                                       do now is use what you know in new ways. For instance,
        for e in enemy_list:                                           if you haven’t already placed your enemies in a sensible
            e.rect.x -= scroll                                         place, take some time to do that now using the same method
        for l in loot_list:       # loot scroll                        you’ve used to place platforms and loot.
            l.rect.x -= scroll    # loot scroll                           There are a few more tips in the next article, but in the
                                                                       meantime, use what you’ve learned to make a few simple,
To scroll it backward, add the last two lines:                         single-level games. Limiting the scope of what you are trying
                                                                       to create is important so that you don’t overwhelm yourself.
        for e in enemy_list:                                           It also makes it easier to end up with a finished product that
            e.rect.x += scroll                                         looks and feels finished.
        for l in loot_list:       # loot scroll                           Here’s all the code you’ve written for this Python platform-
            l.rect.x += scroll    # loot scroll                        er so far:

Launch your game again to see that your loot objects now act           #!/usr/bin/env python3
like they’re in the game world instead of just painted on top of it.   # by Seth Kenlon


Detecting collisions                                                   # GPLv3
As with platforms and enemies, you can check for collisions            # This program is free software: you can redistribute it and/or
between loot and your player. The logic is the same as other           # modify it under the terms of the GNU General Public License as
collisions, except that a hit doesn’t (necessarily) affect grav-       # published by the Free Software Foundation, either version 3 of
ity or health. Instead, a hit causes the loot to disappear and         # the License, or (at your option) any later version.
increment the player’s score.                                          #
   When your player touches a loot object, you can remove              # This program is distributed in the hope that it will be useful,
that object from the loot_list. This means that when your              # but WITHOUT ANY WARRANTY; without even the implied warranty of
main loop redraws all loot items in loot_list, it won’t re-            # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.   See the
draw that particular object, so it will look like the player has       # GNU General Public License for more details.
grabbed the loot.                                                      #
   Add the following code above the platform collision detec-          # You should have received a copy of the GNU General Public
tion in the update function of your Player class (the last line        # License along with this program.    If not, see
is just for context):                                                  # <http://www.gnu.org/licenses/>.




A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                   . CC BY-SA 4.0 . OPENSOURCE.COM                                              53
PUT SOME LOOT IN YOUR PYTHON PLATFORMER GAME . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


     import pygame                                                                self.image = self.images[0]
     import sys                                                                   self.rect = self.image.get_rect()
     import os
                                                                        def gravity(self):
     '''                                                                    if self.is_jumping:
     Variables                                                                    self.movey += 3.2
     '''
                                                                        def control(self, x, y):
     worldx = 960                                                           """
     worldy = 720                                                          control player movement
     fps = 40                                                              """
     ani = 4                                                                self.movex += x
     world = pygame.display.set_mode([worldx, worldy])
     forwardx     = 600                                                 def jump(self):
     backwardx = 120                                                        if self.is_jumping is False:
                                                                                  self.is_falling = False
     BLUE = (25, 25, 200)                                                         self.is_jumping = True
     BLACK = (23, 23, 23)
     WHITE = (254, 254, 254)                                            def update(self):
     ALPHA = (0, 255, 0)                                                    """
                                                                           Update sprite position
     '''                                                                   """
     Objects
     '''                                                                   # moving left
                                                                            if self.movex < 0:
     # x location, y location, img width, img height, img file                    self.is_jumping = True
     class Platform(pygame.sprite.Sprite):                                        self.frame += 1
           def __init__(self, xloc, yloc, imgw, imgh, img):                       if self.frame > 3 * ani:
                 pygame.sprite.Sprite.__init__(self)                                  self.frame = 0
                 self.image = 
                              pygame.image.load(os.path.join                      self.image = 
                                                                                               pygame.transform.flip(self.images[self.
                              ('images', img)).convert()                                       frame // ani], True, False)
                 self.image.convert_alpha()
                 self.image.set_colorkey(ALPHA)                            # moving right
                 self.rect = self.image.get_rect()                         if self.movex > 0:
                 self.rect.y = yloc                                               self.is_jumping = True
                 self.rect.x = xloc                                               self.frame += 1
                                                                                  if self.frame > 3 * ani:
                                                                                      self.frame = 0
     class Player(pygame.sprite.Sprite):                                          self.image = self.images[self.frame // ani]
           """
           Spawn a player                                                  # collisions
           """                                                             enemy_hit_list = 
                                                                                            pygame.sprite.spritecollide(self,
                                                                                               enemy_list, False)
           def __init__(self):                                             for enemy in enemy_hit_list:
                 pygame.sprite.Sprite.__init__(self)                              self.health -= 1
                 self.movex = 0                                                   # print(self.health)
                 self.movey = 0
                 self.frame = 0                                            ground_hit_list = 
                                                                                             pygame.sprite.spritecollide(self,
                 self.health = 10                                                                  ground_list, False)
                 self.is_jumping = True                                    for g in ground_hit_list:
                 self.is_falling = True                                           self.movey = 0
                 self.images = []                                                 self.rect.bottom = g.rect.top
                 for i in range(1, 5):                                            self.is_jumping = False     # stop jumping
                     img = 
                           pygame.image.load(os.path.join('images',
                            'hero' + str(i) + '.png')).convert()           # fall off the world
                     img.convert_alpha()                                   if self.rect.y > worldy:
                     img.set_colorkey(ALPHA)                                      self.health -=1
                     self.images.append(img)                                      print(self.health)




     54                                   A GUIDE TO BUILDING A VIDEO GAME IN PYTHON      . CC BY-SA 4.0 . OPENSOURCE.COM
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . PUT SOME LOOT IN YOUR PYTHON PLATFORMER GAME


               self.rect.x = tx                                                     self.counter = 0
               self.rect.y = ty
                                                                              self.counter += 1
         plat_hit_list = 
                         pygame.sprite.spritecollide(self,
                            plat_list, False)
         for p in plat_hit_list:                                      class Level():
               self.is_jumping = False     # stop jumping                 def ground(lvl, gloc, tx, ty):
               self.movey = 0                                                 ground_list = pygame.sprite.Group()
               if self.rect.bottom <= p.rect.bottom:                          i = 0
                  self.rect.bottom = p.rect.top                               if lvl == 1:
               else:                                                                while i < len(gloc):
                  self.movey += 3.2                                                      ground = 
                                                                                                  Platform(gloc[i], worldy - ty, tx, ty,
                                                                                                    'tile-ground.png')
         if self.is_jumping and self.is_falling is False:                                ground_list.add(ground)
               self.is_falling = True                                                    i = i + 1
               self.movey -= 33     # how high to jump
                                                                             if lvl == 2:
         loot_hit_list = 
                         pygame.sprite.spritecollide(self, loot_                    print("Level " + str(lvl) )
                            list, False)
         for loot in loot_hit_list:                                          return ground_list
                  loot_list.remove(loot)
                  self.score += 1                                        def bad(lvl, eloc):
                  print(self.score)                                          if lvl == 1:
                                                                                    enemy = Enemy(eloc[0],eloc[1],'enemy.png')
         plat_hit_list = 
                         pygame.sprite.spritecollide(self,                          enemy_list = pygame.sprite.Group()
                            plat_list, False)                                       enemy_list.add(enemy)
                                                                             if lvl == 2:
         self.rect.x += self.movex                                                  print("Level " + str(lvl) )
         self.rect.y += self.movey
                                                                             return enemy_list
class Enemy(pygame.sprite.Sprite):
   """                                                                    # x location, y location, img width, img height, img file
   Spawn an enemy                                                         def platform(lvl,tx,ty):
   """                                                                        plat_list = pygame.sprite.Group()
                                                                              ploc = []
   def __init__(self, x, y, img):                                             i=0
         pygame.sprite.Sprite.__init__(self)                                  if lvl == 1:
         self.image = pygame.image.load(os.path.join('images',img))                 ploc.append((200, worldy - ty - 128, 3))
         self.image.convert_alpha()                                                 ploc.append((300, worldy - ty - 256, 3))
         self.image.set_colorkey(ALPHA)                                             ploc.append((500, worldy - ty - 128 , 4))
         self.rect = self.image.get_rect()                                          while i < len(ploc):
         self.rect.x = x                                                                 j=0
         self.rect.y = y                                                                 while j <= ploc[i][2]:
         self.counter = 0                                                                       plat = 
                                                                                                       Platform((ploc[i][0] + (j*tx)),
                                                                                                       ploc[i][1], tx, ty, 'tile.png')
    def move(self):                                                                            plat_list.add(plat)
         '''                                                                                   j = j + 1
         enemy movement                                                                  print('run' + str(i) + str(ploc[i]))
         '''                                                                             i = i + 1
         distance = 80
         speed = 8                                                            if lvl == 2:
                                                                                    print("Level " + str(lvl) )
         if self.counter >= 0 and self.counter <= distance:
               self.rect.x += speed                                           return plat_list
         elifself.counter >= distance and self.counter
               <= distance*2:                                             def loot(lvl):
               self.rect.x -= speed                                           if lvl == 1:
         else:                                                                      loot_list = pygame.sprite.Group()




A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                    . CC BY-SA 4.0 . OPENSOURCE.COM                                            55
PUT SOME LOOT IN YOUR PYTHON PLATFORMER GAME . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


                   loot = Platform(tx*5, ty*5, tx, ty, 'loot_1.png')              if event.type == pygame.KEYDOWN:
                   loot_list.add(loot)                                                if event.key == ord('q'):
                                                                                          pygame.quit()
               if lvl == 2:                                                               try:
                   print(lvl)                                                                 sys.exit()
                                                                                          finally:
               return loot_list                                                               main = False
                                                                                      if event.key == pygame.K_LEFT or event.key == ord('a'):
                                                                                          player.control(-steps, 0)
     '''                                                                              if event.key == pygame.K_RIGHT or event.key == ord('d'):
     Setup                                                                                player.control(steps, 0)
     '''                                                                              if event.key == pygame.K_UP or event.key == ord('w'):
                                                                                          print('jump')
     backdrop = pygame.image.load(os.path.join('images', 'stage.png'))
     clock = pygame.time.Clock()                                                  if event.type == pygame.KEYUP:
     pygame.init()                                                                    if event.key == pygame.K_LEFT or event.key == ord('a'):
     backdropbox = world.get_rect()                                                       player.control(steps, 0)
     main = True                                                                      if event.key == pygame.K_RIGHT or event.key == ord('d'):
                                                                                           player.control(-steps, 0)
     player = Player()      # spawn player
     player.rect.x = 0      # go to x                                          # scroll the world forward
     player.rect.y = 30       # go to y                                        if player.rect.x >= forwardx:
     player_list = pygame.sprite.Group()                                               scroll = player.rect.x - forwardx
     player_list.add(player)                                                           player.rect.x = forwardx
     steps = 10                                                                        for p in plat_list:
                                                                                                 p.rect.x -= scroll
     eloc = []                                                                     for e in enemy_list:
     eloc = [300, 0]                                                                   e.rect.x -= scroll
     enemy_list = Level.bad(1, eloc )
                                                                               # scroll the world backward
     gloc = []                                                                 if player.rect.x <= backwardx:
     tx    = 64                                                                        scroll = backwardx - player.rect.x
     ty    = 64                                                                        player.rect.x = backwardx
                                                                                       for p in plat_list:
     i = 0                                                                                       p.rect.x += scroll
     while i <= (worldx / tx) + tx:                                                for e in enemy_list:
           gloc.append(i * tx)                                                         e.rect.x += scroll
           i = i + 1                                                               for l in loot_list:
                                                                                       l.rect.x += scroll
     ground_list = Level.ground(1, gloc, tx, ty)
     plat_list = Level.platform(1, tx, ty)                                     world.blit(backdrop, backdropbox)
     enemy_list = Level.bad( 1, eloc )                                         player.update()
     loot_list = Level.loot(1)                                                 player.gravity()
                                                                               player_list.draw(world)
                                                                               enemy_list.draw(world)
     '''                                                                       loot_list.draw(world)
     Main Loop                                                                 ground_list.draw(world)
     '''                                                                       plat_list.draw(world)
                                                                               for e in enemy_list:
     while main:                                                                   e.move()
           for event in pygame.event.get():                                    pygame.display.flip()
               if event.type == pygame.QUIT:                                   clock.tick(fps)
                   pygame.quit()
                   try:
                          sys.exit()
                   finally:                                              Links
                       main = False                                      [1]   https://kenney.nl/assets/simplified-platformer-pack




     56                                   A GUIDE TO BUILDING A VIDEO GAME IN PYTHON              . CC BY-SA 4.0 . OPENSOURCE.COM
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ADD SCOREKEEPING TO YOUR PYTHON GAME




Add scorekeeping to your
Python game
In the eleventh article in this series on programming with Python’s Pygame module,
display your game player’s score when they collect loot or take damage.


IF YOU’VE               FOLLOWED along with this series, you’ve
                        learned all the essential syntax and
patterns you need to create a video game with Python. How-
                                                                        The first thing to do, before bothering with a library’s docu-
                                                                     mentation, is to think about what you are trying to achieve. In
                                                                     this case, you want to display the player’s score and health
ever, it still lacks one vital component. This component isn’t       on the screen.
important just for programming games in Python; it’s some-              Once you’ve determined your desired outcome, think
thing you must master no matter what branch of computing             about what components are required for it. You can
you explore: Learning new tricks as a programmer by read-            think of this in terms of variables and functions or, if that
ing a language’s or library’s documentation.                         doesn’t come naturally to you yet, you can think generi-
   Luckily, the fact that you’re reading this article is a sign      cally. You probably recognize that displaying a score re-
that you’re comfortable with documentation. For the practi-          quires some text, which you want Pygame to draw on
cal purpose of making your platform game more polished, in           the screen. If you think it through, you might realize that
this article, you will add a score and health display to your        it’s not very different from rendering a player or loot or a
game screen. But the not-so-secret agenda of this lesson is          platform on screen.
to teach you how to find out what a library offers and how you          Technically, you could use graphics of numbers and
can use new features.                                                have Pygame display those. It’s not the easiest way to
                                                                     achieve your goal, but if it’s the only way you know, then
Displaying the score in Pygame                                       it’s a valid way. However, if you refer to Pygame’s docs,
Now that you have loot that your player can collect, there’s every   you see that one of the modules listed is font, which is
reason to keep score so that your player sees just how much          Pygame’s method for making printing text on the screen
loot they’ve collected. You can also track the player’s health so    as easy as typing.
that when they hit one of the enemies, it has a consequence.
   You already have variables that track score and health, but       Deciphering technical documentation
it all happens in the background. This article teaches you to        The font documentation page starts with pygame.font.init(),
display these statistics in a font of your choice on the game        which it lists as the function that is used to initialize the font
screen during gameplay.                                              module. It’s called automatically by pygame.init(), which
                                                                     you already call in your code. Once again, you’ve reached
Read the docs                                                        a point that that’s technically good enough. While you don’t
Most Python modules have documentation, and even those               know how yet, you know that you can use the pygame.font
that do not can be minimally documented by Python’s Help             functions to print text on the screen.
function. Pygame’s main page [1] links to its documentation.            If you read further, however, you find that there’s yet an
However, Pygame is a big module with a lot of documen-               even better way to print fonts. The pygame.freetype module
tation, and its docs aren’t exactly written in the same ap-          is described in the docs this way:
proachable (and friendly and elucidating and helpful) narra-
tive style as articles on Opensource.com. They’re technical            The pygame.freetype module is a replacement
documents, and they list each class and function available in
                                                                       for pygame.fontpygame module for loading and
the module, what kind of inputs each expects, and so on. If
                                                                       rendering fonts. It has all of the functionality of
you’re not comfortable referring to descriptions of code com-
                                                                       the original, plus many new features.
ponents, this can be overwhelming.



A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                  . CC BY-SA 4.0 . OPENSOURCE.COM                                         57
ADD SCOREKEEPING TO YOUR PYTHON GAME . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


     Further down the pygame.freetype documentation page,                     Sites that specialize in free and legal fonts include:
     there’s some sample code:
                                                                            • Font Library
     import pygame                                                          • Font Squirrel
     import pygame.freetype                                                 • League of Moveable Type

     Your code already imports Pygame, but modify your import               When you find a font that you like, download it. Extract the
     statements to include the Freetype module:                             ZIP or TAR [2] file and move the .ttf or .otf file into the fonts
                                                                            folder in your game project directory.
     import pygame                                                             You aren’t installing the font on your computer. You’re
     import sys                                                             just placing it in your game’s fonts folder so that Pygame
     import os                                                              can use it. You can install the font on your computer if you
     import pygame.freetype                                                 want, but it’s not necessary. The important thing is to have
                                                                            it in your game directory, so Pygame can “trace” it onto
     Using a font in Pygame                                                 the screen.
     From the description of the font modules, it’s clear that Pyg-            If the font file has a complicated name with spaces or spe-
     ame uses a font, whether it’s one you provide or a default             cial characters, just rename it. The filename is completely
     font built into Pygame, to render text on the screen. Scroll           arbitrary, and the simpler it is, the easier it is for you to type
     through the pygame.freetype documentation to find the                  into your code.
     pygame.freetype.Font function:
                                                                            Using a font in Pygame
     pygame.freetype.Font                                                   Now tell Pygame about your font. From the documentation,
     Create a new Font instance from a supported font file.                 you know that you’ll get a font object in return when you pro-
                                                                            vide at least the path to a font file to pygame.freetype.Font
     Font(file, size=0, font_index=0, resolution=0, ucs4=False) -> Font     (the docs state explicitly that all remaining attributes are op-
                                                                            tional):
     pygame.freetype.Font.name
       Proper font name.                                                    Font(file, size=0, font_index=0, resolution=0, ucs4=False) -> Font


     pygame.freetype.Font.path                                              Create a new variable called myfont to serve as your font
       Font file path                                                       in the game, and place the results of the Font function into
                                                                            that variable. This example uses the amazdoom.ttf font, but
     pygame.freetype.Font.size                                              you can use whatever font you want. Place this code in your
       The default point size used in rendering                             Setup section:

     This describes how to construct a font “object” in Pygame. It may      f
                                                                             ont_path = os.path.join(os.path.dirname(os.path.realpath
     not feel natural to you to think of a simple object onscreen as         (__file__)),"fonts","amazdoom.ttf")
     the combination of several code attributes, but it’s very similar to   font_size = tx
     how you built your hero and enemy sprites. Instead of an image         pygame.freetype.init()
     file, you need a font file. Once you have a font file, you can cre-    myfont = pygame.freetype.Font(font_path, font_size)
     ate a font object in your code with the pygame.freetype.Font
     function and then use that object to render text on the screen.        Displaying text in Pygame
                                                                            Now that you’ve created a font object, you need a function to
     Asset management                                                       draw the text you want onto the screen. This is the same principle
     Because not everyone in the world has the exact same fonts             you used to draw the background and platforms in your game.
     on their computers, it’s important to bundle your chosen font             First, create a function, and use the myfont object to cre-
     with your game. To bundle a font, first create a new directory         ate some text, setting the color to some RGB value. This
     in your game folder, right along with the directory you creat-         must be a global function; it does not belong to any specific
     ed for your images. Call it fonts.                                     class. Place it in the objects section of your code, but keep
        Even though several fonts come with your computer, it’s not         it as a stand-alone function:
     legal to give those fonts away. It seems strange, but that’s
     how the law works. If you want to ship a font with your game,          def stats(score,health):
     you must find an open source or Creative Commons font that                 myfont.render_to(
                                                                                                 world, (4, 4), "Score:"+str(score), BLACK,
     permits you to give the font away along with your game.                                      None, size=64)




     58                              A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                  . CC BY-SA 4.0 . OPENSOURCE.COM
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ADD SCOREKEEPING TO YOUR PYTHON GAME



    myfont.render_to(
                     world, (4, 72), "Health:"+str(health),
                     BLACK, None, size=64)


Of course, you know by now that nothing happens in your
game if it’s not in the Main loop, so add a call to your stats
function near the bottom of the file:

    stats(player.score,player.health) # draw text


Try your game. If you’ve been following the sample code in
this article exactly, you’ll get an error when you try to launch
the game now.

Interpreting errors
Errors are important to programmers. When something fails
in your code, one of the best ways to understand why is
by reading the error output. Unfortunately, Python doesn’t
communicate the same way a human does. While it does                There is one problem, though. When a player gets hit by an
have relatively friendly errors, you still have to interpret        enemy, health goes way down, and that’s not fair. You have
what you’re seeing.                                                 just discovered a non-fatal bug. Non-fatal bugs are those lit-
   In this case, launching the game produces this output:           tle problems in applications that don’t keep the application
                                                                    from starting up or even from working (mostly), but they ei-
Traceback (most recent call last):                                  ther don’t make sense, or they annoy the user. Here’s how
  Fi
    le "/home/tux/PycharmProjects/game_001/main.py", line 41,       to fix this one.
    in <module>
    font_size = tx                                                  Fixing the health counter
NameError: name 'tx' is not defined                                 The problem with the current health point system is that
                                                                    health is subtracted for every tick of the Pygame clock
Python is aserting that the variable tx is not defined. You         that the enemy is touching the player. That means that
know this isn’t true, because you’ve used tx in several plac-       a slow-moving enemy can take a player down to –200
es by now and it’s worked as expected.                              health in just one encounter, and that’s not fair. You could,
   But Python also cites a line number. This is the line that       of course, just give your player a starting health score of
caused Python to stop executing the code. It is not necessar-       10,000 and not worry about it; that would work, and possi-
ily the line containing the error.                                  bly no one would mind. But there is a better way.
   Armed with this knowledge, you can look at your code in            Currently, your code detects when a player and an enemy
an attempt to understand what has failed.                           collide. The fix for the health-point problem is to detect two
   Line 41 attempts to set the font size to the value of tx.        separate events: when the player and enemy collide and,
However, reading through the file in reverse, up from line 41,      once they have collided, when they stop colliding.
you might notice that tx (and ty) are not listed. In fact, tx and     First, in your Player class, create a variable to represent
ty were placed haphazardly in your setup section because,           when a player and enemy have collided:
at the time, it seemed easy and logical to place them along
with other important tile information.                                     self.frame = 0
   Moving the tx and ty lines from your setup section to some              self.health = 10
line above line 41 fixes the error.                                        self.damage = 0
   When you entcounter errors in Python, take note of the
hints it provides, and then read your source code carefully.        In the update function of your Player class, remove this block
It can take time to find an error, even for experienced pro-        of code:
grammers, but the better you understand Python the easier
it becomes.                                                                for enemy in enemy_hit_list:
                                                                               self.health -= 1
Running the game                                                               #print(self.health)
When the player collects loot, the score goes up. When
the player gets hit by an enemy, health goes down.                  And in its place, check for collision as long as the player is
Success!                                                            not currently being hit:



A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                 . CC BY-SA 4.0 . OPENSOURCE.COM                                     59
ADD SCOREKEEPING TO YOUR PYTHON GAME . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


            if self.damage == 0:
                for enemy in enemy_hit_list:
                    if not self.rect.contains(enemy):
                        self.damage = self.rect.colliderect(enemy)


     You might see similarities between the block you deleted
     and the one you just added. They’re both doing the same
     job, but the new code is more complex. Most importantly,
     the new code runs only if the player is not currently being
     hit. That means that this code runs once when a player
     and enemy collide and not constantly for as long as the
     collision happens, the way it used to.
        The new code uses two new Pygame functions. The self.
     rect.contains function checks to see if an enemy is cur-
     rently within the player’s bounding box, and self.rect.colli-
     derect sets your new self.damage variable to one when it         Now that you have a way for your player to know their score
     is true, no matter how many times it is true.                    and health, you can make certain events occur when your
        Now even three seconds of getting hit by an enemy still       player reaches certain milestones. For instance, maybe
     looks like one hit to Pygame.                                    there’s a special loot item that restores some health points.
        I discovered these functions by reading through Pyg-          And maybe a player who reaches zero health points has to
     ame’s documentation. You don’t have to read all the docs         start back at the beginning of a level.
     at once, and you don’t have to read every word of each              You can check for these events in your code and manipu-
     function. However, it’s important to spend time with the         late your game world accordingly.
     documentation of a new library or module that you’re
     using; otherwise, you run a high risk of reinventing the         Level up
     wheel. Don’t spend an afternoon trying to hack together          You already know how to do so much. Now it’s time to
     a solution to something that’s already been solved by the        level up your skills. Go skim the documentation for new
     framework you’re using. Read the docs, find the functions,       tricks and try them out on your own. Programming is a
     and benefit from the work of others!                             skill you develop, so don’t stop with this project. Invent
        Finally, add another block of code to detect when the play-   another game, or a useful application, or just use Python
     er and the enemy are no longer touching. Then and only           to experiment around with crazy ideas. The more you use
     then, subtract one point of health from the player.              it, the more comfortable you get with it, and eventually it’ll
                                                                      be second nature.
            if self.damage == 1:                                          Keep it going, and keep it open!
                idx = self.rect.collidelist(enemy_hit_list)               Here’s all the code so far:
                if idx == -1:
                    self.damage = 0    # set damage back to 0         #!/usr/bin/env python3
                    self.health -= 1   # subtract 1 hp                # by Seth Kenlon


     Notice that this new code gets triggered only when the player    # GPLv3
     has been hit. That means this code doesn’t run while your        # This program is free software: you can redistribute it and/or
     player is running around your game world exploring or col-       # modify it under the terms of the GNU General Public License as
     lecting loot. It only runs when the self.damage variable gets    # published by the Free Software Foundation, either version 3 of
     activated.                                                       # the License, or (at your option) any later version.
        When the code runs, it uses self.rect.collidelist to          #
     see whether or not the player is still touching an enemy         # This program is distributed in the hope that it will be useful,
     in your enemy list (collidelist returns negative one when        # but WITHOUT ANY WARRANTY; without even the implied warranty of
     it detects no collision). Once it is not touching an enemy,      # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.   See the
     it’s time to pay the self.damage debt: deactivate the self.      # GNU General Public License for more details.
     damage variable by setting it back to zero and subtract          #
     one point of health.                                             # You should have received a copy of the GNU General Public
        Try your game now.                                            # License along with this program.   If not, see
                                                                      # <http://www.gnu.org/licenses/>.




     60                            A GUIDE TO BUILDING A VIDEO GAME IN PYTHON              . CC BY-SA 4.0 . OPENSOURCE.COM
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ADD SCOREKEEPING TO YOUR PYTHON GAME



import pygame                                                       class Player(pygame.sprite.Sprite):
import pygame.freetype                                                 """
import sys                                                             Spawn a player
import os                                                              """


'''                                                                    def __init__(self):
Variables                                                                    pygame.sprite.Sprite.__init__(self)
'''                                                                          self.movex = 0
                                                                             self.movey = 0
worldx = 960                                                                 self.frame = 0
worldy = 720                                                                 self.health = 10
fps = 40                                                                     self.damage = 0
ani = 4                                                                      self.score = 0
world = pygame.display.set_mode([worldx, worldy])                            self.is_jumping = True
forwardx    = 600                                                            self.is_falling = True
backwardx = 120                                                              self.images = []
BLUE = (80, 80, 155)                                                         for i in range(1, 5):
BLACK = (23, 23, 23)                                                               img = 
                                                                                         pygame.image.load(os.path.join('images',
WHITE = (254, 254, 254)                                                                  'hero' + str(i) + '.png')).convert()
ALPHA = (0, 255, 0)                                                                img.convert_alpha()
                                                                                   img.set_colorkey(ALPHA)
tx = 64                                                                            self.images.append(img)
ty = 64                                                                            self.image = self.images[0]
                                                                                   self.rect = self.image.get_rect()
font_path = os.path.join(os.path.dirname(os.path.realpath
 (__file__)),"fonts","amazdoom.ttf")                                   def gravity(self):
font_size = tx                                                               if self.is_jumping:
pygame.freetype.init()                                                             self.movey += 3.2
myfont = pygame.freetype.Font(font_path, font_size)
                                                                       def control(self, x, y):
                                                                             """
'''                                                                          control player movement
Objects                                                                      """
'''                                                                          self.movex += x


def stats(score,health):                                               def jump(self):
      myfont.render_to(
                       world, (4, 4), "Score:"+str(score), BLUE,             if self.is_jumping is False:
                       None, size=64)                                              self.is_falling = False
      myfont.render_to(
                       world, (4, 72), "Health:"+str(health),                      self.is_jumping = True
                       BLUE, None, size=64)
                                                                       def update(self):
# x location, y location, img width, img height, img file                    """
class Platform(pygame.sprite.Sprite):                                        Update sprite position
      def __init__(self, xloc, yloc, imgw, imgh, img):                       """
           pygame.sprite.Sprite.__init__(self)
           self.image = 
                        pygame.image.load(os.path.join                       # moving left
                        ('images', img)).convert()                           if self.movex < 0:
           self.image.convert_alpha()                                              self.is_jumping = True
           self.image.set_colorkey(ALPHA)                                          self.frame += 1
           self.rect = self.image.get_rect()                                       if self.frame > 3 * ani:
           self.rect.y = yloc                                                          self.frame = 0
           self.rect.x = xloc                                                      self.image = 
                                                                                                pygame.transform.flip(self.images[self.
                                                                                                frame // ani], True, False)




A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                   . CC BY-SA 4.0 . OPENSOURCE.COM                                        61
ADD SCOREKEEPING TO YOUR PYTHON GAME . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


            # moving right                                                                  print(self.score)
            if self.movex > 0:
                self.is_jumping = True                                            plat_hit_list = 
                                                                                                  pygame.sprite.spritecollide(self,
                self.frame += 1                                                                      plat_list, False)
                if self.frame > 3 * ani:
                    self.frame = 0                                                self.rect.x += self.movex
                self.image = self.images[self.frame // ani]                       self.rect.y += self.movey


            # collisions                                                class Enemy(pygame.sprite.Sprite):
            enemy_hit_list = 
                             pygame.sprite.spritecollide(self,              """
                               enemy_list, False)                           Spawn an enemy
            if self.damage == 0:                                            """
                for enemy in enemy_hit_list:
                    if not self.rect.contains(enemy):                       def __init__(self, x, y, img):
                           self.damage = self.rect.colliderect(enemy)             pygame.sprite.Sprite.__init__(self)
            if self.damage == 1:                                                  self.image = pygame.image.load(os.path.join('images',img))
                idx = self.rect.collidelist(enemy_hit_list)                       self.image.convert_alpha()
                if idx == -1:                                                     self.image.set_colorkey(ALPHA)
                    self.damage = 0      # set damage back to 0                   self.rect = self.image.get_rect()
                    self.health -= 1     # subtract 1 hp                          self.rect.x = x
                                                                                  self.rect.y = y
            ground_hit_list = 
                              pygame.sprite.spritecollide(self,                   self.counter = 0
                                 ground_list, False)
            for g in ground_hit_list:                                       def move(self):
                self.movey = 0                                                    '''
                self.rect.bottom = g.rect.top                                     enemy movement
                self.is_jumping = False      # stop jumping                       '''
                                                                                  distance = 80
            # fall off the world                                                  speed = 8
            if self.rect.y > worldy:
                self.health -=1                                                   if self.counter >= 0 and self.counter <= distance:
                print(self.health)                                                      self.rect.x += speed
                self.rect.x = tx                                                  elifself.counter >= distance and self.counter
                self.rect.y = ty                                                        <= distance*2:
                                                                                        self.rect.x -= speed
            plat_hit_list = 
                            pygame.sprite.spritecollide(self,                     else:
                              plat_list, False)                                         self.counter = 0
            for p in plat_hit_list:
                self.is_jumping = False      # stop jumping                       self.counter += 1
                self.movey = 0
                if self.rect.bottom <= p.rect.bottom:
                   self.rect.bottom = p.rect.top                        class Level:
                else:                                                       def ground(lvl, gloc, tx, ty):
                   self.movey += 3.2                                              ground_list = pygame.sprite.Group()
                                                                                  i = 0
            if self.is_jumping and self.is_falling is False:                      if lvl == 1:
                self.is_falling = True                                                  while i < len(gloc):
                self.movey -= 33     # how high to jump                                      ground = 
                                                                                                      Platform(gloc[i], worldy - ty, tx, ty,
                                                                                                      'tile-ground.png')
            loot_hit_list = 
                            pygame.sprite.spritecollide(self, loot_                          ground_list.add(ground)
                              list, False)                                                   i = i + 1
            for loot in loot_hit_list:
                   loot_list.remove(loot)                                         if lvl == 2:
                   self.score += 1                                                      print("Level " + str(lvl) )




     62                              A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                  . CC BY-SA 4.0 . OPENSOURCE.COM
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ADD SCOREKEEPING TO YOUR PYTHON GAME



                                                                       clock = pygame.time.Clock()
         return ground_list                                            pygame.init()
                                                                       backdropbox = world.get_rect()
      def bad(lvl, eloc):                                              main = True
         if lvl == 1:
                enemy = Enemy(eloc[0],eloc[1],'enemy.png')             player = Player()        # spawn player
                enemy_list = pygame.sprite.Group()                     player.rect.x = 0        # go to x
                enemy_list.add(enemy)                                  player.rect.y = 30        # go to y
         if lvl == 2:                                                  player_list = pygame.sprite.Group()
                print("Level " + str(lvl) )                            player_list.add(player)
                                                                       steps = 10
         return enemy_list
                                                                       eloc = []
      # x location, y location, img width, img height, img file        eloc = [300, worldy-ty-80]
      def platform(lvl,tx,ty):                                         enemy_list = Level.bad(1, eloc )
          plat_list = pygame.sprite.Group()                            gloc = []
          ploc = []
          i=0                                                          i = 0
          if lvl == 1:                                                 while i <= (worldx / tx) + tx:
                ploc.append((200, worldy - ty - 128, 3))                     gloc.append(i * tx)
                ploc.append((300, worldy - ty - 256, 3))                     i = i + 1
                ploc.append((500, worldy - ty - 128 , 4))
                while i < len(ploc):                                   ground_list = Level.ground(1, gloc, tx, ty)
                     j=0                                               plat_list = Level.platform(1, tx, ty)
                     while j <= ploc[i][2]:                            enemy_list = Level.bad( 1, eloc )
                            plat = 
                                   Platform((ploc[i][0] + (j*tx)),     loot_list = Level.loot(1)
                                   ploc[i][1], tx, ty, 'tile.png')
                           plat_list.add(plat)
                           j = j + 1                                   '''
                     print('run' + str(i) + str(ploc[i]))              Main Loop
                     i = i + 1                                         '''


          if lvl == 2:                                                 while main:
                print("Level " + str(lvl))                                   for event in pygame.event.get():
                                                                                 if event.type == pygame.QUIT:
          return plat_list                                                           pygame.quit()
                                                                                     try:
      def loot(lvl):                                                                        sys.exit()
          if lvl == 1:                                                               finally:
                loot_list = pygame.sprite.Group()                                         main = False
                loot = Platform(tx*5, ty*5, tx, ty, 'loot_1.png')
                loot_list.add(loot)                                             if event.type == pygame.KEYDOWN:
                                                                                     if event.key == ord('q'):
          if lvl == 2:                                                                   pygame.quit()
                print(lvl)                                                               try:
                                                                                              sys.exit()
          return loot_list                                                               finally:
                                                                                              main = False
                                                                                     if event.key == pygame.K_LEFT or event.key == ord('a'):
'''                                                                                      player.control(-steps, 0)
Setup                                                                                if event.key == pygame.K_RIGHT or event.key == ord('d'):
'''                                                                                      player.control(steps, 0)
                                                                                     if event.key == pygame.K_UP or event.key == ord('w'):
backdrop = pygame.image.load(os.path.join('images', 'stage.png'))                        print('jump')




A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                      . CC BY-SA 4.0 . OPENSOURCE.COM                                           63
ADD SCOREKEEPING TO YOUR PYTHON GAME . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


                                                                                               p.rect.x += scroll
             if event.type == pygame.KEYUP:                                         for e in enemy_list:
                 if event.key == pygame.K_LEFT or event.key == ord('a'):                e.rect.x += scroll
                     player.control(steps, 0)                                       for l in loot_list:
                 if event.key == pygame.K_RIGHT or event.key == ord('d'):               l.rect.x += scroll
                      player.control(-steps, 0)
                                                                                world.blit(backdrop, backdropbox)
          # scroll the world forward                                            player.update()
          if player.rect.x >= forwardx:                                         player.gravity()
                  scroll = player.rect.x - forwardx                             player_list.draw(world)
                  player.rect.x = forwardx                                      enemy_list.draw(world)
                  for p in plat_list:                                           loot_list.draw(world)
                      p.rect.x -= scroll                                        ground_list.draw(world)
              for e in enemy_list:                                              plat_list.draw(world)
                  e.rect.x -= scroll                                            for e in enemy_list:
              for l in loot_list:                                                   e.move()
                  l.rect.x -= scroll                                            stats(player.score, player.health)
                                                                                pygame.display.flip()
          # scroll the world backward                                           clock.tick(fps)
          if player.rect.x <= backwardx:
                  scroll = backwardx - player.rect.x                        Links
                  player.rect.x = backwardx                                 [1] http://pygame.org/news
                  for p in plat_list:                                       [2]	https://opensource.com/article/17/7/how-unzip-targz-file




     64                                A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                  . CC BY-SA 4.0 . OPENSOURCE.COM
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ADD THROWING MECHANICS TO YOUR PYTHON GAME




Add throwing mechanics
to your Python game
Running around avoiding enemies is one thing. Fighting back is another. Learn
how in the 12th article in this series on creating a platformer in Pygame.


MY PREVIOUS                        ARTICLE was meant to be the fi-
                                   nal article in this series, and it
encouraged you to go program your own additions to this
                                                                         class Throwable(pygame.sprite.Sprite):
                                                                            """
                                                                            Spawn a throwable object
game. Many of you did! I got emails asking for help with a                  """
common mechanic that I hadn’t yet covered: combat. After                    def __init__(self, x, y, img, throw):
all, jumping to avoid baddies is one thing, but sometimes it’s                    pygame.sprite.Sprite.__init__(self)
awfully satisfying to just make them go away. It’s common in                      self.image = pygame.image.load(os.path.join('images',img))
video games to throw something at your enemies, whether                           self.image.convert_alpha()
it’s a ball of fire, an arrow, a bolt of lightning, or whatever else              self.image.set_colorkey(ALPHA)
fits the game.                                                                    self.rect   = self.image.get_rect()
    Unlike anything you have programmed for your platformer                       self.rect.x = x
game in this series so far, throwable items have a time to                        self.rect.y = y
live. Once you throw an object, it’s expected to travel some                      self.firing = throw
distance and then disappear. If it’s an arrow or something
like that, it may disappear when it passes the edge of the               The primary difference in this function compared to your
screen. If it’s a fireball or a bolt of lightning, it might fizzle out   Player class or Enemy class __init__ function is that it has
after some amount of time.                                               a self.firing variable. This variable keeps track of whether
    That means each time a throwable item is spawned, a                  or not a throwable object is currently alive on screen, so it
unique measure of its lifespan must also be spawned. To in-              stands to reason that when a throwable object is created, the
troduce this concept, this article demonstrates how to throw             variable is set to 1.
only one item at a time. (In other words, only one throwable
item may exist at a time.) On the one hand, this is a game               Measure time to live
limitation, but on the other hand, it is a game mechanic in              Next, just as with Player and Enemy, you need an update
itself. Your player won’t be able to throw 50 fireballs at once,         function so that the throwable object moves on its own once
since you only allow one at a time, so it becomes a challenge            it’s thrown into the air toward an enemy.
for your player to time when they release a fireball to try to               The easiest way to determine the lifespan of a throwable
hit an enemy. And behind the scenes, this also keeps your                object is to detect when it goes off-screen. Which screen
code simple.                                                             edge you need to monitor depends on the physics of your
    If you want to enable more throwable items at once, chal-            throwable object.
lenge yourself after you finish this tutorial by building on the
knowledge you gain.                                                      • If your player is throwing something that travels quickly
                                                                            along the horizontal axis, like a crossbow bolt or arrow or a
Create the throwable class                                                  very fast magical force, then you want to monitor the hori-
If you followed along with the other articles in this series,               zontal limit of your game screen. This is defined by worldx.
you should be familiar with the basic __init__ function when             • If your player is throwing something that travels vertically
spawning a new object on the screen. It’s the same function                 or both horizontally and vertically, then you must monitor
you used for spawning your player [1] and your enemies [2].                 the vertical limit of your game screen. This is defined by
Here’s an __init__ function to spawn a throwable object:                   worldy.



A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                     . CC BY-SA 4.0 . OPENSOURCE.COM                                           65
ADD THROWING MECHANICS TO YOUR PYTHON GAME . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


     This example assumes your throwable object goes a little               First, add player controls. Currently, you have no firepower
     forward and eventually falls to the ground. The object does         trigger. There are two states for a key on a keyboard: the key
     not bounce off the ground, though, and continues to fall off        can be down, or the key can be up. For movement, you use
     the screen. You can try different settings to see what fits your    both: pressing down starts the player moving, and releasing
     game best:                                                          the key (the key is up) stops the player. Firing needs only
                                                                         one signal. It’s a matter of taste as to which key event (a key
          def update(self,worldy):                                       press or a key release) you use to trigger your throwable
             '''                                                         object.
             throw physics                                                  In this code block, the first two lines are for context:
             '''
              if self.rect.y < worldy: #vertical axis                                  if event.key == pygame.K_UP or event.key == ord('w'):
                   self.rect.x   += 15 #how fast it moves forward                         player.jump(platform_list)
                   self.rect.y   += 5   #how fast it falls                             if event.key == pygame.K_SPACE:
             else:                                                                        if not fire.firing:
                   self.kill()     #remove throwable object                                     fire = 
                                                                                                       Throwable(player.rect.x,player.
                   self.firing = 0 #free up firing slot                                               rect.y,'fire.png',1)
                                                                                                firepower.add(fire)
     To make your throwable object move faster, increase the
     momentum of the self.rect values.                                   Unlike the fireball you created in your setup section, you use
        If the throwable object is off-screen, then the object is de-    a 1 to set self.firing as unavailable.
     stroyed, freeing up the RAM that it had occupied. In addition,         Finally, you must update and draw your throwable object.
     self.firing is set back to 0 to allow your player to take an-       The order of this matters, so put this code between your ex-
     other shot.                                                         isting enemy.move and player_list.draw lines:

     Set up your throwable object                                            enemy.move()      # context
     Just like with your player and enemies, you must create a
     sprite group in your setup section to hold the throwable ob-            if fire.firing:
     ject.                                                                       fire.update(worldy)
        Additionally, you must create an inactive throwable object               firepower.draw(world)
     to start the game with. If there isn’t a throwable object when          player_list.draw(screen)       # context
     the game starts, the first time a player attempts to throw a            enemy_list.draw(screen)        # context
     weapon, it will fail.
        This example assumes your player starts with a fireball as       Notice that these updates are performed only if the self.
     a weapon, so each instance of a throwable object is desig-          firing variable is set to 1. If it is set to 0, then fire.firing is
     nated by the fire variable. In later levels, as the player ac-      not true, and the updates are skipped. If you tried to do these
     quires new skills, you could introduce a new variable using         updates, no matter what, your game would crash because
     a different image but leveraging the same Throwable class.          there wouldn’t be a fire object to update or draw.
        In this block of code, the first two lines are already in your      Launch your game and try to throw your weapon.
     code, so don’t retype them:
                                                                         Detect collisions
     player_list = pygame.sprite.Group() #context                        If you played your game with the new throwing mechan-
     player_list.add(player)                 #context                    ic, you probably noticed that you can throw objects, but it
     fire = Throwable(player.rect.x,player.rect.y,'fire.png',0)          doesn’t have any effect on your foes.
     firepower = pygame.sprite.Group()                                      The reason is that your enemies do not check for a col-
                                                                         lision. An enemy can be hit by your throwable object and
     Notice that a throwable item starts at the same location as         never know about it.
     the player. That makes it look like the throwable item is com-         You’ve already done collision detection in your Player
     ing from the player. The first time the fireball is generated, a    class, and this is very similar. In your Enemy class, add a new
     0 is used so that self.firing shows as available.                   update function:

     Get throwing in the main loop                                           def update(self,firepower, enemy_list):
     Code that doesn’t appear in the main loop will not be used in               """
     the game, so you need to add a few things in your main loop                 detect firepower collision
     to get your throwable object into your game world.                          """




     66                                 A GUIDE TO BUILDING A VIDEO GAME IN PYTHON               . CC BY-SA 4.0 . OPENSOURCE.COM
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ADD THROWING MECHANICS TO YOUR PYTHON GAME



        fire_hit_list =   
                          pygame.sprite.                               Finally, change the update function of your Throwable class
                          spritecollide(self,firepower,False)          to check whether the hero is facing right or not and to add
        for fire in fire_hit_list:                                     or subtract pixels from the fireball’s position as appropriate:
             enemy_list.remove(self)
                                                                                 if self.rect.y < worldy:
The code is simple. Each enemy object checks to see if it                            if player.facing_right:
has been hit by the firepower sprite group. If it has, then the                           self.rect.x += 15
enemy is removed from the enemy group and disappears.                                else:
   To integrate that function into your game, call the function                           self.rect.x -= 15
in your new firing block in the main loop:                                           self.rect.y += 5


    if fire.firing:                               # context            Try your game again and clear your world of some baddies.
        fire.update(worldy)                       # context
        firepower.draw(screen)                    # context
        enemy_list.update(firepower,enemy_list) # update enemy


You can try your game now, and most everything works as
expected. There’s still one problem, though, and that’s the
direction of the throw.

Change the throw mechanic direction
Currently, your hero’s fireball moves only to the right. This
is because the update function of the Throwable class adds
pixels to the position of the fireball, and in Pygame, a larger
number on the X-axis means movement toward the right of
the screen. When your hero turns the other way, you proba-
bly want it to throw its fireball to the left.
   By this point, you know how to implement this, at least
technically. However, the easiest solution uses a variable in          (Seth Kenlon, CC BY-SA 4.0)
what may be a new way for you. Generically, you can “set
a flag” (sometimes also termed “flip a bit”) to indicate the di-       As a bonus challenge, try incrementing your player’s score
rection your hero is facing. Once you do that, you can check           whenever an enemy is vanquished.
that variable to learn whether the fireball needs to move left           The complete code:
or right.
   First, create a new variable in your Player class to repre-         #!/usr/bin/env python3
sent which direction your hero is facing. Because my hero              # by Seth Kenlon
faces right naturally, I treat that as the default:
                                                                       # GPLv3
        self.score = 0                                                 # This program is free software: you can redistribute it and/or
        self.facing_right = True     # add this                        # modify it under the terms of the GNU General Public License as
        self.is_jumping = True                                         # published by the Free Software Foundation, either version 3 of
                                                                       # the License, or (at your option) any later version.
When this variable is True, your hero sprite is facing right. It       #
must be set anew every time the player changes the hero’s              # This program is distributed in the hope that it will be useful,
direction, so do that in your main loop on the relevant keyup          # but WITHOUT ANY WARRANTY; without even the implied warranty of
events:                                                                # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.   See the
                                                                       # GNU General Public License for more details.
       if event.type == pygame.KEYUP:                                  #
            if event.key == pygame.K_LEFT or event.key == ord('a'):    # You should have received a copy of the GNU General Public
                player.control(steps, 0)                               # License along with this program.     If not, see
                player.facing_right = False # add this line            # <http://www.gnu.org/licenses/>.
            if event.key == pygame.K_RIGHT or event.key == ord('d'):
                player.control(-steps, 0)                              import pygame
                player.facing_right = True # add this line             import pygame.freetype




A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                    . CC BY-SA 4.0 . OPENSOURCE.COM                                             67
ADD THROWING MECHANICS TO YOUR PYTHON GAME . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


     import sys
     import os                                                                    def update(self,worldy):
                                                                                        '''
     '''                                                                                throw physics
     Variables                                                                          '''
     '''                                                                                if self.rect.y < worldy:
                                                                                              if player.facing_right:
     worldx = 960                                                                             self.rect.x   += 15
     worldy = 720                                                                             else:
     fps = 40                                                                                     self.rect.x -= 15
     ani = 4                                                                                  self.rect.y   += 5
     world = pygame.display.set_mode([worldx, worldy])                                  else:
     forwardx      = 600                                                                      self.kill()
     backwardx = 120                                                                          self.firing = 0


     BLUE = (80, 80, 155)
     BLACK = (23, 23, 23)                                                     # x location, y location, img width, img height, img file
     WHITE = (254, 254, 254)                                                  class Platform(pygame.sprite.Sprite):
     ALPHA = (0, 255, 0)                                                          def __init__(self, xloc, yloc, imgw, imgh, img):
                                                                                        pygame.sprite.Sprite.__init__(self)
     tx = 64                                                                            self.image = pygame.image.load(os.path.join
     ty = 64                                                                                            ('images', img)).convert()
                                                                                        self.image.convert_alpha()
     font_path = os.path.join(os.path.dirname(os.path.realpath                         self.image.set_colorkey(ALPHA)
      (__file__)),"fonts","amazdoom.ttf")                                               self.rect = self.image.get_rect()
     font_size = tx                                                                     self.rect.y = yloc
     pygame.freetype.init()                                                             self.rect.x = xloc
     myfont = pygame.freetype.Font(font_path, font_size)


                                                                              class Player(pygame.sprite.Sprite):
     '''                                                                          """
     Objects                                                                      Spawn a player
     '''                                                                          """


     def stats(score,health):                                                     def __init__(self):
           myfont.render_to(
                            world, (4, 4), "Score:"+str(score), BLUE,                   pygame.sprite.Sprite.__init__(self)
                               None, size=64)                                           self.movex = 0
           myfont.render_to(
                            world, (4, 72), "Health:"+str(health),                      self.movey = 0
                               BLUE, None, size=64)                                     self.frame = 0
                                                                                        self.health = 10
                                                                                        self.damage = 0
     class Throwable(pygame.sprite.Sprite):                                             self.score = 0
           """                                                                          self.facing_right = True
           Spawn a player                                                               self.is_jumping = True
           """                                                                          self.is_falling = True
           def __init__(self, x, y, img, throw):                                        self.images = []
                 pygame.sprite.Sprite.__init__(self)                                    for i in range(1, 5):
                 self.image = pygame.image.load(os.path.join('images',img))                   img = 
                                                                                                    pygame.image.load(os.path.join('images',
                 self.image.convert_alpha()                                                           'walk' + str(i) + '.png')).convert()
                 self.image.set_colorkey(ALPHA)                                               img.convert_alpha()
                 self.rect = self.image.get_rect()                                            img.set_colorkey(ALPHA)
                 self.rect.x = x                                                              self.images.append(img)
                 self.rect.y = y                                                              self.image = self.images[0]
                 self.firing = throw                                                          self.rect = self.image.get_rect()




     68                                  A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                     . CC BY-SA 4.0 . OPENSOURCE.COM
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ADD THROWING MECHANICS TO YOUR PYTHON GAME


                                                                                   self.movey = 0
   def gravity(self):                                                              self.rect.bottom = g.rect.top
       if self.is_jumping:                                                         self.is_jumping = False       # stop jumping
             self.movey += 3.2
                                                                               # fall off the world
   def control(self, x, y):                                                    if self.rect.y > worldy:
       """                                                                         self.health -=1
      control player movement                                                      print(self.health)
      """                                                                          self.rect.x = tx
       self.movex += x                                                             self.rect.y = ty


   def jump(self):                                                             plat_hit_list = 
                                                                                               pygame.sprite.spritecollide(self,
       if self.is_jumping is False:                                                               plat_list, False)
             self.is_falling = False                                           for p in plat_hit_list:
             self.is_jumping = True                                                self.is_jumping = False       # stop jumping
                                                                                   self.movey = 0
   def update(self):                                                               if self.rect.bottom <= p.rect.bottom:
       """                                                                            self.rect.bottom = p.rect.top
      Update sprite position                                                       else:
      """                                                                             self.movey += 3.2


      # moving left                                                            if self.is_jumping and self.is_falling is False:
       if self.movex < 0:                                                          self.is_falling = True
             self.is_jumping = True                                                self.movey -= 33      # how high to jump
             self.frame += 1
             if self.frame > 3 * ani:                                          loot_hit_list = 
                                                                                               pygame.sprite.spritecollide(self, loot_
                 self.frame = 0                                                                   list, False)
             self.image = 
                          pygame.transform.flip(self.images[self.              for loot in loot_hit_list:
                          frame // ani], True, False)                                  loot_list.remove(loot)
                                                                                       self.score += 1
      # moving right                                                                   print(self.score)
      if self.movex > 0:
             self.is_jumping = True                                            plat_hit_list = 
                                                                                               pygame.sprite.spritecollide(self,
             self.frame += 1                                                                      plat_list, False)
             if self.frame > 3 * ani:
                 self.frame = 0                                                self.rect.x += self.movex
             self.image = self.images[self.frame // ani]                       self.rect.y += self.movey


      # collisions                                                    class Enemy(pygame.sprite.Sprite):
      enemy_hit_list = 
                       pygame.sprite.spritecollide(self,                 """
                          enemy_list, False)                             Spawn an enemy
      if self.damage == 0:                                               """
             for enemy in enemy_hit_list:
                 if not self.rect.contains(enemy):                       def __init__(self, x, y, img):
                     self.damage = self.rect.colliderect(enemy)                pygame.sprite.Sprite.__init__(self)
       if self.damage == 1:                                                    self.image = pygame.image.load(os.path.join('images',img))
             idx = self.rect.collidelist(enemy_hit_list)                       self.image.convert_alpha()
             if idx == -1:                                                     self.image.set_colorkey(ALPHA)
                 self.damage = 0      # set damage back to 0                   self.rect = self.image.get_rect()
                 self.health -= 1     # subtract 1 hp                          self.rect.x = x
                                                                               self.rect.y = y
      ground_hit_list = 
                        pygame.sprite.spritecollide(self,                      self.counter = 0
                             ground_list, False)
       for g in ground_hit_list:                                          def move(self):




A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                     . CC BY-SA 4.0 . OPENSOURCE.COM                                           69
ADD THROWING MECHANICS TO YOUR PYTHON GAME . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


              '''                                                                   plat_list = pygame.sprite.Group()
              enemy movement                                                        ploc = []
              '''                                                                   i=0
              distance = 80                                                         if lvl == 1:
              speed = 8                                                                   ploc.append((200, worldy - ty - 128, 3))
                                                                                          ploc.append((300, worldy - ty - 256, 3))
              if self.counter >= 0 and self.counter <= distance:                          ploc.append((500, worldy - ty - 128 , 4))
                    self.rect.x += speed                                                  while i < len(ploc):
              elifself.counter >= distance and self.counter                                  j=0
                    <= distance*2:                                                            while j <= ploc[i][2]:
                    self.rect.x -= speed                                                             plat = 
                                                                                                            Platform((ploc[i][0] + (j*tx)),
              else:                                                                                             ploc[i][1], tx, ty, 'tile.png')
                    self.counter = 0                                                                plat_list.add(plat)
                                                                                                    j = j + 1
              self.counter += 1                                                               print('run' + str(i) + str(ploc[i]))
                                                                                              i = i + 1
          def update(self, firepower, enemy_list):
              """                                                                    if lvl == 2:
              detect firepower collision                                                  print("Level " + str(lvl))
              """
              fire_hit_list = 
                              pygame.sprite.spritecollide(self,                      return plat_list
                                firepower, False)
              for fire in fire_hit_list:                                         def loot(lvl):
                    enemy_list.remove(self)                                          if lvl == 1:
                                                                                          loot_list = pygame.sprite.Group()
                                                                                          loot = Platform(tx*5, ty*5, tx, ty, 'loot_1.png')
     class Level:                                                                         loot_list.add(loot)
          def ground(lvl, gloc, tx, ty):
              ground_list = pygame.sprite.Group()                                   if lvl == 2:
              i = 0                                                                       print(lvl)
              if lvl == 1:
                    while i < len(gloc):                                            return loot_list
                         ground = 
                                  Platform(gloc[i], worldy - ty, tx, ty,
                                   'tile-ground.png')
                         ground_list.add(ground)                           '''
                         i = i + 1                                         Setup
                                                                           '''
             if lvl == 2:
                    print("Level " + str(lvl) )                            backdrop = pygame.image.load(os.path.join('images', 'stage.png'))
                                                                           clock = pygame.time.Clock()
             return ground_list                                            pygame.init()
                                                                           backdropbox = world.get_rect()
          def bad(lvl, eloc):                                              main = True
             if lvl == 1:
                    enemy = Enemy(eloc[0],eloc[1],'enemy.png')             player = Player()        # spawn player
                    enemy_list = pygame.sprite.Group()                     player.rect.x = 0        # go to x
                    enemy_list.add(enemy)                                  player.rect.y = 30        # go to y
             if lvl == 2:                                                  player_list = pygame.sprite.Group()
                    print("Level " + str(lvl) )                            player_list.add(player)
                                                                           steps = 10
             return enemy_list                                             fire = Throwable(player.rect.x, player.rect.y, 'fire.png', 0)
                                                                           firepower = pygame.sprite.Group()
          # x location, y location, img width, img height, img file
          def platform(lvl,tx,ty):                                         eloc = []




     70                                     A GUIDE TO BUILDING A VIDEO GAME IN PYTHON               . CC BY-SA 4.0 . OPENSOURCE.COM
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ADD THROWING MECHANICS TO YOUR PYTHON GAME



eloc = [300, worldy-ty-80]                                                               if not fire.firing:
enemy_list = Level.bad(1, eloc )                                                              fire = 
                                                                                                     Throwable(player.rect.x, player.rect.y,
gloc = []                                                                                           'fire.png', 1)
                                                                                              firepower.add(fire)
i = 0
while i <= (worldx / tx) + tx:                                              # scroll the world forward
      gloc.append(i * tx)                                                   if player.rect.x >= forwardx:
      i = i + 1                                                                      scroll = player.rect.x - forwardx
                                                                                     player.rect.x = forwardx
ground_list = Level.ground(1, gloc, tx, ty)                                          for p in plat_list:
plat_list = Level.platform(1, tx, ty)                                                    p.rect.x -= scroll
enemy_list = Level.bad( 1, eloc )                                                for e in enemy_list:
loot_list = Level.loot(1)                                                            e.rect.x -= scroll
                                                                                 for l in loot_list:
                                                                                     l.rect.x -= scroll
'''
Main Loop                                                                   # scroll the world backward
'''                                                                         if player.rect.x <= backwardx:
                                                                                     scroll = backwardx - player.rect.x
while main:                                                                          player.rect.x = backwardx
      for event in pygame.event.get():                                               for p in plat_list:
          if event.type == pygame.QUIT:                                                       p.rect.x += scroll
              pygame.quit()                                                      for e in enemy_list:
              try:                                                                   e.rect.x += scroll
                     sys.exit()                                                  for l in loot_list:
              finally:                                                               l.rect.x += scroll
                   main = False
                                                                            world.blit(backdrop, backdropbox)
         if event.type == pygame.KEYDOWN:                                   player.update()
              if event.key == ord('q'):                                     player.gravity()
                  pygame.quit()                                             player_list.draw(world)
                  try:                                                      if fire.firing:
                       sys.exit()                                                fire.update(worldy)
                  finally:                                                       firepower.draw(world)
                       main = False                                         enemy_list.draw(world)
              if event.key == pygame.K_LEFT or event.key == ord('a'):            enemy_list.update(firepower,enemy_list)
                  player.control(-steps, 0)                                 loot_list.draw(world)
              if event.key == pygame.K_RIGHT or event.key == ord('d'):      ground_list.draw(world)
                  player.control(steps, 0)                                  plat_list.draw(world)
              if event.key == pygame.K_UP or event.key == ord('w'):         for e in enemy_list:
                  print('jump')                                                  e.move()
                                                                            stats(player.score, player.health)
         if event.type == pygame.KEYUP:                                     pygame.display.flip()
              if event.key == 
                              pygame.K_LEFT or event.key == ord('a'):       clock.tick(fps)
                  player.control(steps, 0)
                  player.facing_right = False
              if event.key == 
                              pygame.K_RIGHT or event.key == ord('d'):   Links
                  player.control(-steps, 0)                              [1]	https://opensource.com/article/17/12/game-python-add-a-
                  player.facing_right = True                                  player
              if event.key == pygame.K_SPACE:                            [2] https://opensource.com/article/18/5/pygame-enemy




A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                      . CC BY-SA 4.0 . OPENSOURCE.COM                                           71
ADD SOUND TO YOUR PYTHON GAME . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .




     Add sound to your Python game
     Hear what happens when your hero fights, jumps, collects loot, and more by adding sounds to your
     game. Learn how in the 13th article in this series on creating a platformer in Pygame.



     PYGAME               PROVIDES an easy way to integrate
                          sounds into your Python video game.
     Pygame’s mixer module [1] can play one or more sounds
                                                                          sounds from famous video games are such a part of popu-
                                                                          lar culture, but that’s how the law works. If you want to ship
                                                                          a sound with your game, you must find an open source or
     on command, and by mixing those sounds together, you                 Creative Commons [2] sound that gives you permission to
     can have, for instance, background music playing at the              give the sound away with your game.
     same time you hear the sounds of your hero collecting loot              There are several sites that specialize in free and legal
     or jumping over enemies.                                             sounds, including:
       It is easy to integrate the mixer module into an existing
     game, so—rather than giving you code samples showing                 • Freesound [3] hosts sound effects of all sorts.
     you exactly where to put them—this article explains the four         • Incompetech [4] hosts background music.
     steps required to get sound in your application.                     • Open Game Art [5] hosts some sound effects and music.

     Start the mixer                                                      Some sound files are free to use only if you give the com-
     First, in your code’s setup section, start the mixer process.        poser or sound designer credit. Read the conditions of use
     Your code already starts Pygame and Pygame fonts, so                 carefully before bundling any with your game! Musicians and
     grouping it together with these is a good idea:                      sound designers work just as hard on their sounds as you
                                                                          work on your code, so it’s nice to give them credit even when
     pygame.init()                                                        they don’t require it.
     pygame.font.init()                                                      To give your sound sources credit, list the sounds that you
     pygame.mixer.init() # add this line                                  use in a text file called CREDIT, and place the text file in your
                                                                          game folder.
     Define the sounds                                                       You might also try making your own music. The excellent
     Next, you must define the sounds you want to use. This re-           LMMS [6] audio workstation is easy to use and ships with
     quires that you have the sounds on your computer, just as            lots of interesting sounds. It’s available on all major platforms
     using fonts requires you to have fonts, and using graphics           and exports to Ogg Vorbis [7] (OGG) audio format.
     requires you to have graphics.
        You also must bundle those sounds with your game so               Add sound to Pygame
     that anyone playing your game has the sound files.                   When you find a sound that you like, download it. If it comes
        To bundle a sound with your game, first create a new              in a ZIP or TAR file, extract it and move the sounds into the
     directory in your game folder, right along with the directory        sound folder in your game directory.
     you created for your images and fonts. Call it sound:                   If the sound file has a complicated name with spaces or
                                                                          special characters, rename it. The filename is completely
     s = 'sound'                                                          arbitrary, and the simpler it is, the easier it is for you to type
                                                                          into your code.
     Even though there are plenty of sounds on the internet,                 Most video games use OGG sound files because the
     it’s not necessarily legal to download them and give them            format provides high quality in small file sizes. When you
     away with your game. It seems strange because so many                download a sound file, it might be an MP3, WAVE, FLAC,



     72                             A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                 . CC BY-SA 4.0 . OPENSOURCE.COM
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ADD SOUND TO YOUR PYTHON GAME



or another audio format. To keep your compatibility high and       music = pygame.mixer.music.load(os.path.join(s, 'music.ogg'))
your download size low, convert these to Ogg Vorbis with a
tool like fre:ac [8] or Miro [9].                                  And start the music:
  For example, assume you have downloaded a sound file
called ouch.ogg.                                                   pygame.mixer.music.play(-1)
  In your code’s setup section, create a variable represent-
ing the sound file you want to use:                                The -1 value tells Pygame to loop the music file infinitely.
                                                                   You can set it to anything from 0 and beyond to define how
ouch = pygame.mixer.Sound(os.path.join(s, 'ouch.ogg'))             many times the music should loop before stopping.

Trigger a sound                                                    Enjoy the soundscapes
To use a sound, all you have to do is call the variable when       Music and sound can add a lot of flavor to your game. Try
you want to trigger it. For instance, to trigger the OUCH          adding some to your Pygame project!
sound effect when your player hits an enemy:

for enemy in enemy_hit_list:                                       Links
   pygame.mixer.Sound.play(ouch)                                   [1]	https://www.pygame.org/docs/ref/mixer.html
   score -= 1                                                      [2]	https://opensource.com/article/20/1/what-creative-
                                                                        commons
You can create sounds for all kinds of actions, such as jump-      [3] https://freesound.org/
ing, collecting loot, throwing, colliding, and whatever else       [4]	https://incompetech.filmmusic.io/
you can imagine.                                                   [5]	https://opengameart.org/
                                                                   [6]	https://opensource.com/life/16/2/linux-multimedia-studio
Add background music                                               [7]	https://en.wikipedia.org/wiki/Vorbis
If you have music or atmospheric sound effects you want            [8]	https://www.freac.org/index.php/en/downloads-
to play in your game’s background, you can use the music                mainmenu-330
function of Pygame’s mixer module. In your setup section,          [9] http://getmiro.com/
load the music file:




A GUIDE TO BUILDING A VIDEO GAME IN PYTHON               . CC BY-SA 4.0 . OPENSOURCE.COM                                           73
HOW TO INSTALL PYTHON ON WINDOWS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .




     How to install Python on Windows
     Install Python, run an IDE, and start coding right from your Microsoft Windows desktop.

                                                                        Install Python
     SO YOU WANT                      TO LEARN to program? One of
                                      the most common languages
     to start with is Python [1], popular for its unique blend of ob-
                                                                        Once the package is downloaded, open it to start the
                                                                        installer.
     ject-oriented [2] structure and simple syntax. Python is also         It is safe to accept the default install location, and it’s
     an interpreted language, meaning you don't need to learn           vital to add Python to PATH. If you don’t add Python to your
     how to compile code into machine language: Python does             PATH, then Python applications won’t know where to find
     that for you, allowing you to test your programs sometimes         Python (which they require in order to run). This is not se-
     instantly and, in a way, while you write your code.                lected by default, so activate it at the bottom of the install
        Just because Python is easy to learn doesn’t mean you           window before continuing!
     should underestimate its potential power. Python is used
     by movie studios [3], financial institutions, IT houses, video
     game studios, makers, hobbyists, artists [4], teachers, and
     many others.
        On the other hand, Python is also a serious programming
     language, and learning it takes dedication and practice. Then
     again, you don’t have to commit to anything just yet. You can
     install and try Python on nearly any computing platform, so if
     you’re on Windows, this article is for you.
        If you want to try Python on a completely open source oper-
     ating system, you can install Linux [5] and then try Python [6].

     Get Python
     Python is available from its website, Python.org [7]. Once
     there, hover your mouse over the Downloads menu, then
     over the Windows option, and then click the button to down-        Before Windows allows you to install an application from a
     load the latest release.                                           publisher other than Microsoft, you must give your approval.
                                                                        Click the Yes button when prompted by the User Account
                                                                        Control system.




     Alternatively, you can click the Downloads menu button and
     select a specific version from the downloads page.



     74                            A GUIDE TO BUILDING A VIDEO GAME IN PYTHON               . CC BY-SA 4.0 . OPENSOURCE.COM
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . HOW TO INSTALL PYTHON ON WINDOWS



Wait patiently for Windows to distribute the files from the Py-        To install it, visit the PyCharm IDE website, download the
thon package into the appropriate locations, and when it’s           installer, and run it. The process is the same as with Python:
finished, you’re done installing Python.                             start the installer, allow Windows to install a non-Microsoft
   Time to play.                                                     application, and wait for the installer to finish.
                                                                       Once PyCharm is installed, double-click the PyCharm icon
Install an IDE                                                       on your desktop or select it from the Start menu.
To write programs in Python, all you really need is a text editor,
but it’s convenient to have an integrated development environ-       Tell Python what to do
ment (IDE). An IDE integrates a text editor with some friendly       Keywords tell Python what you want it to do. In IDLE, go to
and helpful Python features. IDLE 3 and Pycharm (Commu-              the File menu and create a new file. In PyCharm, click the
nity Edition) are two great open source options to consider.         New Project button.
                                                                       In your new, empty file, type this into IDLE or PyCharm:
IDLE 3
Python comes with an IDE called IDLE. You can write code             print("Hello world.")
in any text editor, but using an IDE provides you with key-
word highlighting to help detect typos, a Run button to test         • If you are using IDLE, go to the Run menu and select the
code quickly and easily, and other code-specific features that          Run Module option.
a plain text editor like Notepad++ [8] normally doesn’t have.        • If you are using PyCharm, click the Run button in the top
   To start IDLE, click the Start (or Window) menu and type             right corner of the window.
python for matches. You may find a few matches, since Py-
thon provides more than one interface, so make sure you
launch IDLE.




                                                                     Any time you run code, your IDE prompts you to save the file
                                                                     you’re working on. Do that before continuing.
                                                                       The keyword print tells Python to print out whatever text
                                                                     you give it in parentheses and quotes.
                                                                       That’s not very exciting, though. At its core, Python has ac-
                                                                     cess to only basic keywords like print and help, basic math
                                                                     functions, and so on.
                                                                       Use the import keyword to load more keywords. Start a
                                                                     new file and name it pen.py.
                                                                       Warning: Do not call your file turtle.py, because turtle.
                                                                     py is the name of the file that contains the turtle program you
If you don’t see Python in the Start menu, reinstall Python.         are controlling. Naming your file turtle.py confuses Python
Be sure to select Add Python to PATH in the install wizard.          because it thinks you want to import your own file.
Refer to the Python docs [9] for detailed instructions.                Turtle [11] is a fun module to use. Add this code to your file:

PyCharm IDE                                                          import turtle
If you already have some coding experience and IDLE seems
too simple for you, try PyCharm (Community Edition) [10], an         turtle.begin_fill()
open source IDE for Python. It has keyword highlighting to           turtle.forward(100)
help detect typos, quotation and parenthesis completion to           turtle.left(90)
avoid syntax errors, line numbers (helpful when debugging),          turtle.forward(100)
indentation markers, and a Run button to test code quickly           turtle.left(90)
and easily.                                                          turtle.forward(100)




A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                  . CC BY-SA 4.0 . OPENSOURCE.COM                                        75
HOW TO INSTALL PYTHON ON WINDOWS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


     turtle.left(90)                                                    Once you complete that script, you’re ready to move on to
     turtle.forward(100)                                                more exciting modules. A good place to start is this introduc-
     turtle.end_fill()                                                  tory dice game [12].

     See what shapes you can draw with the turtle module.               Stay Pythonic
       To clear your turtle drawing area, use the turtle.clear()        Python is a fun language with modules for practically any-
     keyword. What do you think the keyword turtle.color("blue")        thing you can think to do with it. As you can see, it’s easy to
     does?                                                              get started with Python, and as long as you’re patient with
       Try more complex code:                                           yourself, you may find yourself understanding and writing
                                                                        Python code with the same fluidity as you write your native
     import turtle as t                                                 language. Work through some Python articles [13] here on
     import time                                                        Opensource.com, try scripting some small tasks for yourself,
                                                                        and see where Python takes you. To really integrate Python
     t.color("blue")                                                    with your daily workflow, you might even try Linux, which is
     t.begin_fill()                                                     natively scriptable in ways no other operating system is. You
                                                                        might find yourself, given enough time, using the applica-
     counter = 0                                                        tions you create!
                                                                           Good luck, and stay Pythonic.
     while counter < 4:
          t.forward(100)
          t.left(90)                                                    Links
          counter = counter+1                                           [1]	 https://www.python.org/
                                                                        [2]	 https://opensource.com/article/19/7/get-modular-python-
     t.end_fill()                                                             classes
     time.sleep(2)                                                      [3]	 https://github.com/edniemeyer/weta_python_db
                                                                        [4] https://opensource.com/article/19/7/rgb-cube-python-
     Notice that turtle, in this example code, has not only been              scribus
     imported, but it’s also been given the shorter nickname t,         [5] https://opensource.com/article/19/7/ways-get-started-linux
     which is quicker and easier to type. This is a convenience         [6] https://opensource.com/article/17/10/python-101
     function in Python.                                                [7] https://www.python.org/downloads/
                                                                        [8] https://notepad-plus-plus.org/
     Challenge                                                          [9]	 http://docs.python.org/3/using/windows.html
     As a challenge, try changing your script to get this result:       [10]	https://www.jetbrains.com/pycharm/
                                                                              download/#section=windows
                                                                        [11] https://opensource.com/life/15/8/python-turtle-graphics
                                                                        [12]	https://opensource.com/article/17/10/python-101#python-
                                                                              101-dice-game
                                                                        [13]	https://opensource.com/sitewide-search?search_api_
                                                                              views_fulltext=Python




     76                            A GUIDE TO BUILDING A VIDEO GAME IN PYTHON               . CC BY-SA 4.0 . OPENSOURCE.COM
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . MANAGING PYTHON PACKAGES THE RIGHT WAY




Managing Python packages
the right way                                                               By László Kiss Kollár

Don’t fall victim to the perils of Python package management.


THE PYTHON                      PACKAGE INDEX (PYPI) indexes an
                                amazing array of libraries and
applications covering every use case imaginable. Howev-
                                                                  $ python3.7 -m pip install pytest
                                                                  Collecting pytest
                                                                  Downloading...
er, when it comes to installing and using these packages,         [...]
newcomers often find themselves running into issues with          In
                                                                    stalling collected packages: atomicwrites, pluggy, py,
missing permissions, incompatible library dependencies,               more-itertools, pytest
and installations that break in surprising ways.                  Could not install packages due to an EnvironmentError: [Error 13]
   The Zen of Python states: “There should be one—and                 Permission denied:
preferably only one—obvious way to do it.” This is certainly      '
                                                                   /usr/lib/python3.7/site-packages/site-packages/atomicwrites-
not always the case when it comes to installing Python pack-       x.y.z.dist-info'
ages. However, there are some tools and methods that can          Consider using '--user' option or check the permissions.
be considered best practices. Knowing these can help you          $
pick the right tool for the right situation.
                                                                  This fails because we are running pip install as a non-root
Installing applications system-wide                               user and we don’t have write permission to the site-packages
pip is the de facto package manager in the Python world.          directory.
It can install packages from many sources, but PyPI [1] is           You can technically get around this by running pip as a
the primary package source where it’s used. When installing       root (using the sudo command) or administrative user. How-
packages, pip will first resolve the dependencies, check if       ever, one problem is that we just installed a bunch of Python
they are already installed on the system, and, if not, install    packages into a location the Linux distribution’s package
them. Once all dependencies have been satisfied, it pro-          manager owns, making its internal database and the instal-
ceeds to install the requested package(s). This all happens       lation inconsistent. This will likely cause issues anytime we
globally, by default, installing everything onto the machine in   try to install, upgrade, or remove any of these dependencies
a single, operating system-dependent location.                    using the package manager.
   Python 3.7 looks for packages on an Arch Linux system in          As an example, let’s try to install pytest again, but now
the following locations:                                          using my system’s package manager, pacman:

$ python3.7 -c "import sys; print('\n'.join(sys.path))"           $ sudo pacman -S community/python-pytest
                                                                  resolving dependencies...
/usr/lib/python37.zip                                             looking for conflicting packages...
/usr/lib/python3.7                                                [...]
/usr/lib/python3.7/lib-dynload                                    py
                                                                    thon-py: /usr/lib/site-packages/py/_pycache_/
/usr/lib/python3.7/site-packages                                      _metainfo.cpython-37.pyc exists in filesystem
                                                                  py
                                                                    thon-py: /usr/lib/site-packages/py/_pycache_/
One problem with global installations is that only a single           _builtin.cpython-37.pyc exists in filesystem
version of a package can be installed at one time for a given     py
                                                                    thon-py: /usr/lib/site-packages/py/_pycache_/
Python interpreter. This can cause issues when a package              _error.cpython-37.pyc exists in filesystem
is a dependency of multiple libraries or applications, but they
require different versions of this dependency. Even if things     Another potential issue is that an operating system can use Py-
seem to be working fine, it is possible that upgrading the de-    thon for system tools, and we can easily break these by modi-
pendency (even accidentally while installing another pack-        fying Python packages outside the system package manager.
age) will break these applications or libraries in the future.    This can result in an inoperable system, where restoring from
   Another potential issue is that most Unix-like distributions   a backup or a complete reinstallation is the only way to fix it.
manage Python packages with the built-in package manager
(dnf, apt, pacman, brew, and so on), and some of these            sudo pip install: A bad idea
tools install into a non-user-writeable location.                 There is another reason why running pip install as root is a



A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                . CC BY-SA 4.0 . OPENSOURCE.COM                                         77
MANAGING PYTHON PACKAGES THE RIGHT WAY . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


     bad idea. To explain this, we first have to look at how Python        If the package we want to use is available and we don’t
     libraries and applications are packaged.                              mind slightly older versions, the package manager offers a
         Most Python libraries and applications today use setuptools       convenient and safe way to install Python packages. And,
     as their build system. setuptools requires a setup.py file in the     since these packages install system-wide, they are avail-
     root of the project, which describes package metadata and can         able to all users on the system. This also means that we
     contain arbitrary Python code to customize the build process.         can use them only if we have the required permissions to
     When a package is installed from the source distribution, this        install packages on the system.
     file is executed to perform the installation and execute tasks like      If we want to use something that is not available in the
     inspecting the system, building the package, etc.                     package manager’s selection or is too old, or we simply don’t
         Executing setup.py with root permissions means we can             have the necessary permissions to install packages, we can
     effectively open up the system to malicious code or bugs.             use pip instead.
     This is a lot more likely than you might think. For example,
     in 2017, several packages were uploaded to PyPI [2] with              User scheme installations
     names resembling popular Python libraries. The uploaded               pip supports the “user scheme” mode introduced in Py-
     code collected system and user information and uploaded               thon 2.6. This allows for packages to be installed into a us-
     it to a remote server. These packages were pulled shortly             er-owned location. On Linux, this is typically ~/.local. Putting
     thereafter. However, these kinds of “typo-squatting” incidents        ~/.local/bin/ on our PATH will make it possible to have Py-
     can happen anytime since anyone can upload packages to                thon tools and scripts available at our fingertips and manage
     PyPI and there is no review process to make sure the code             them without root privileges.
     doesn’t do any harm.
         The Python Software Foundation (PSF) recently an-                 $ python3.7 -m pip install --user black
     nounced that it will sponsor work to improve the security             Collecting black
     of PyPI [3]. This should make it more difficult to carry out           Using cached
     attacks such as “pytosquatting” [4] and hopefully make this           [...]
     less of an issue in the future.                                       Installing collected packages: click, toml, black
         Security issues aside, sudo pip install won’t solve all the        Th
                                                                              e scripts black and blackd are installed in
     dependency problems: you can still install only a single ver-             '/home/tux/.local/bin' which is not on PATH.
     sion of any given library, which means it’s still easy to break        Co
                                                                              nsider adding this directory to PATH or, if you prefer to
     applications this way.                                                    suppress this warning, use --no-warn-script-location.
         Let’s look at some better alternatives.                           Successfully installed black-x.y click-x.y toml-x.y.z
                                                                           $
     OS package managers
     It is very likely that the “native” package manager we use on         However, this solution does not solve the issue if and when
     our OS of choice can also install Python packages. The ques-          we need different versions of the same package.
     tion is: should we use pip, or apt, dnf, pacman, and so on?
         The answer is: it depends.                                        Enter virtual environments
         pip is generally used to install packages directly from PyPI,     Virtual environments offer isolated Python package installa-
     and Python package authors usually upload their packages              tions that can coexist independently on the same system.
     there. However, most package maintainers will not use PyPI,           This offers the same benefits as user scheme installations,
     but instead take the source code from the source distribu-            but it also allows the creation of self-contained Python instal-
     tion (sdist) created by the author or a version control system        lations where an application does not share dependencies
     (e.g., GitHub), apply patches if needed, and test and release         with any other application. Virtualenv creates a directory
     the package for their respective platforms. Compared to the           that holds a self-contained Python installation, including the
     PyPI distribution model, this has pros and cons:                      Python binary and essential tools for package management:
                                                                           setuptools, pip, and wheel.
     • S oftware maintained by native package managers is gen-
        erally more stable and usually works better on the given           Creating virtual environments
        platform (although this might not always be the case).             virtualenv is a third-party package, but Python 3.3 added
     • This also means it takes extra work to package and test            the venv package to the standard library. As a result, we
        upstream Python code:                                              don’t have to install anything to use virtual environments in
       1. The package selection is usually much smaller than              modern versions of Python. We can simply use python3.7
           what PyPI offers.                                               -m venv <env_name> to create a new virtual environment.
       2. Updates are slower and package managers will often                 After creating a new virtual environment, we must activate
           ship much older versions.                                       it by sourcing the activate script in the bin directory of the



     78                              A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                 . CC BY-SA 4.0 . OPENSOURCE.COM
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . MANAGING PYTHON PACKAGES THE RIGHT WAY



newly created environment. The activation script creates               sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
a new subshell and adds the bin directory to the PATH en-           (test-env) $
vironment variable, enabling us to run binaries and scripts
from this location. This means that this subshell will use          We can simply run ~/test-env/bin/black from anywhere on
python, pip, or any other tool installed in this location in-       the system and it will work just fine.
stead of the ones installed globally on the system.                   It can be useful to add certain commonly used virtual environ-
                                                                    ments to the PATH environment variable so we can quickly and
$ python3.7 -m venv test-env                                        easily use the scripts in them without typing out the full path:
$ . ./test-env/bin/activate
(test-env) $                                                        export PATH=$PATH:~/test-env/bin


After this, any command we execute will use the Python installa-    Now when we execute black, it will be picked up from the
tion inside the virtual environment. Let’s install some packages.   virtual environment (unless it appears somewhere else earli-
                                                                    er on the PATH). Add this line to your shell’s initialization file
(test-env)$ python3.7 -m pip install --user black                   (e.g., ~/.bashrc) to have it automatically set in all new shells.
Collecting black                                                       Virtual environments are very commonly used for Python
    Using cached                                                    development because each project gets its own environment
[...]                                                               where all library dependencies can be installed without inter-
Installing collected packages: click, toml, black                   fering with the system installation.
Successfully installed black-x.y click-x.y toml-x.y.z                  I recommend checking out the virtualenvwrapper [5]
(test-env) $                                                        project, which can help simplify common virtualenv-based
                                                                    workflows.
We can use black inside the virtual environment without any
manual changes to the environment variables like PATH or            What about Conda?
PYTHONPATH.                                                         Conda [6] is a package management tool that can install pack-
                                                                    ages provided by Anaconda on the repo.continuum.io [7]
(test-env) $ black --version                                        repository. It has become very popular, especially for data sci-
black, version x.y                                                  ence. It offers an easy way to create and manage environments
(test-env) $ which black                                            and install packages in them. One drawback compared to pip
/home/tux/test-env/bin/black                                        is that the package selection is much smaller.
(test-env) $
                                                                    A recipe for successful package management
When we are done with the virtual environment, we can sim-          • N ever run sudo pip install.
ply deactivate it with the deactivate function.                     • If you want to make a package available to all users of the
                                                                       machine, you have the right permissions, and the package
(test-env) $ deactivate                                                is available, then use your distribution’s package manager
$                                                                      (apt, yum, pacman, brew, etc.).
                                                                    • If you don’t have root permissions or the OS package man-
Virtual environments can also be used without the activation           ager doesn’t have the package you need, use pip install
script. Scripts installed in a venv will have their shebang line       --user and add the user installation directory to the PATH
rewritten to use the Python interpreter inside the virtual envi-       environment variable.
ronment. This way, we can execute the script from anywhere          • If you want multiple versions of the same library to coexist,
on the system using the full path to the script.                       to do Python development, or just to isolate dependencies
                                                                       for any other reason, use virtual environments.
(test-env) $ head /home/tux/test-env/bin/black
#!/home/tux/test-env/bin/python3.7                                  Links
                                                                    [1]	https://pypi.org/
# -*- coding: utf-8 -*-                                             [2]	https://github.com/pypa/warehouse/issues/3948
import re                                                           [3]	http://pyfound.blogspot.com/2018/12/upcoming-pypi-
import sys                                                               improvements-for-2019.html
                                                                    [4]	https://pytosquatting.overtag.dk/
from black import main                                              [5]	https://virtualenvwrapper.readthedocs.io/
                                                                    [6]	https://conda.io/
if __name__ == '__main__':                                          [7] https://repo.continuum.io/



A GUIDE TO BUILDING A VIDEO GAME IN PYTHON                 . CC BY-SA 4.0 . OPENSOURCE.COM                                           79
EASILY SET IMAGE TRANSPARENCY USING GIMP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .



     Easily set image transparency
     using GIMP
     Use chroma key or “green screen” techniques to set transparencies on your video-game graphics.



     WHETHER                   YOU’RE programming a game or an app
                               with Python [1] or Lua [2], you’re prob-
     ably using PNG graphics for your game assets. An advan-
                                                                          be problematic. Instead, you can pick a color and turn it into
                                                                          an alpha value of 0 in your game framework. For that to
                                                                          work, you must know the colors in your graphic.
     tage of the PNG format, which is not available in a JPEG,
     is the ability to store an alpha channel. Alpha is, essentially,     Prepare your graphic
     the “color” of invisibility or transparency. Alpha is the part of    To prepare a graphic with an explicit color reserved exclu-
     an image you don’t see. For example, if you were to draw a           sively for a chroma key, open the graphic in your favorite
     doughnut, the doughnut hole would be filled with alpha, and          graphic editor. I recommend GIMP [5] or Glimpse [6], but
     you could see whatever was behind it.                                mtPaint [7] or Pinta [8] or even Inkscape [9] can work just
         A common problem is how to find the alpha part of an             as well, depending on the nature of your graphics and your
     image. Sometimes, your programming framework, whether                ability to translate these instructions to a different tool.
     it’s Python Arcade [3], Pygame [4], LÖVE, or anything else,             Start by opening this graphic of Tux the penguin:
     detects the alpha channel and treats it (after the appropri-
     ate function calls) as transparency. That means it renders no
     new pixels where there’s alpha, leaving that doughnut hole
     empty. It’s 100% transparent or 0% opaque and functionally
     “invisible.”
         Other times, your framework and your graphic asset don’t
     agree on where the alpha channel is located (or that an
     alpha channel exists at all), and you get pixels where you
     wanted transparency.                                                 (Seth Kenlon, CC BY-SA 4.0)
         This article describes the most sure-fire way I know to
     work around that.                                                    Select the graphic
                                                                          Once the graphic is open, go to the Windows menu and
     Chroma key                                                           select Dockable Dialogs and then Layers. Right-click on
     In computer graphics, there are a few values that contribute         Tux’s layer in the Layer palette. From the pop-up menu, se-
     to how a pixel is rendered. Chrominance, or chroma, de-              lect Alpha to Selection. If your image does not have a built-
     scribes the saturation or intensity of a pixel. The chroma key       in alpha channel, then you must create your own selection
     technique (also known as green screening) was originally             manually.
     developed as a chemical process, in which a specific color
     (blue at first and later green) was deliberately obscured by a
     matte during the copying of a film negative, allowing anoth-
     er image to be substituted where there once was a blue or
     green screen. That’s a simplified explanation, but it demon-
     strates the origins of what is known as the alpha channel in
     computer graphics.
        An alpha channel is information saved in a graphic to iden-
     tify pixels that are meant to be transparent. RGB graphics,
     for example, have red, green, and blue channels. RGBA
     graphics contain red, green, blue, and alpha. The value of
     alpha can range from 0 to 1, with decimal points being valid
     entries.
        Because an alpha channel can be expressed in several
     different ways, relying on an embedded alpha channel can             (Seth Kenlon, CC BY-SA 4.0)



     80                             A GUIDE TO BUILDING A VIDEO GAME IN PYTHON               . CC BY-SA 4.0 . OPENSOURCE.COM
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EASILY SET IMAGE TRANSPARENCY USING GIMP


To create a selection manually, click the Paths tool from the
toolbox.




                                                                (Seth Kenlon, CC BY-SA 4.0)
(Seth Kenlon, CC BY-SA 4.0)
                                                                Now select the Paths panel from the Windows > Dockable
Using the Paths tool, move your mouse around the graph-         Dialogs menu. In the Paths panel, click the Path to Selection
ic, clicking and releasing (do not drag) at each major inter-   button. Your graphic is now selected.
section of its outline. Don’t worry about following curves;
just find the major intersections and corners. This creates     Grow the selection
a node at each point, with a straight line drawn between        If you feel your selection is too tight, you can give yourself
them. You don’t need to close your path, so when you reach      a little slack by growing the selection. I sometimes do this
the final intersection before the one where you started,        when I want to impose or thicken a border around a graphic.
you’re done.                                                       To grow a selection, click the Select menu and choose
                                                                Grow. Enter a pixel value and click OK.

                                                                Invert the selection
                                                                You have your graphic selected, but what you actually want
                                                                to select is everything except your graphic. That’s because
                                                                you’re creating an alpha matte to define what in your graphic
                                                                will be replaced by something else. In other words, you need
                                                                to mark the pixels that will be turned invisible.
                                                                   To invert the selection, click on the Select menu and
                                                                choose Invert. Now everything except your graphic is se-
                                                                lected.

                                                                Fill with alpha
                                                                With everything except your graphic selected, choose the
                                                                color you want to use to designate your alpha matte. The
                                                                most common color is green (as you might guess from the
                                                                term “green screen”). There’s nothing magical about the color
(Seth Kenlon, CC BY-SA 4.0)                                     green or even a particular shade of green. It’s used because
                                                                humans, frequent subjects of graphic manipulation, contain
Once you’ve created your outline path, go to the Win-           no green pigment, so it’s easy to key out green without ac-
dows menu and select Dockable Dialogs and then Tool             cidentally keying out part of the central subject. Of course, if
Options. In the Tool Options panel, select Edit (Ctrl).         your graphic is a green alien or an emerald or something that
With this activated, you can edit the paths you’ve just         does contain green, you should use a different color. You can
drawn by clicking the lines or nodes and adjusting them         use any color you want, as long as it’s solid and consistent.
to fit your graphic better. You can even give the lines         If you’re doing this to many graphics, your choice should be
curves.                                                         consistent across all graphics.



A GUIDE TO BUILDING A VIDEO GAME IN PYTHON             . CC BY-SA 4.0 . OPENSOURCE.COM                                       81
EASILY SET IMAGE TRANSPARENCY USING GIMP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


                                                                       whether or not you added a border. This process removes
                                                                       the alpha channel from your image, filling in any “transpar-
                                                                       ent” pixels with the background color.




     (Seth Kenlon, CC BY-SA 4.0)
                                                                       (Seth Kenlon, CC BY-SA 4.0)
     Set your foreground color with the color value you’ve cho-
     sen. To ensure that your choice is exact, use the HTML [10]       You now have an image ready for your game engine. Export
     or HSV [11] representation of the color. For example, if you’re   the image to whatever format your game engine prefers, and
     using pure green, it can be expressed in GIMP (and most           then import it into your game using whatever function calls
     open source graphic applications) as 00ff00 (that’s 00 red,       it requires. In your code, set the alpha value to 00ff00 (or
     FF green, and 00 blue, with F being the maximum amount).          whatever color you used), and then use the game engine’s
                                                                       graphic transforms to treat that color as an alpha channel.

                                                                       Other methods
                                                                       This isn’t the only way to get transparency in your game
                                                                       graphics. Check your game engine’s documentation to find
                                                                       out how it tries to process alpha channels by default, and
                                                                       when you’re not certain, try letting your game engine au-
                                                                       to-detect transparency in your graphic before you go about
                                                                       editing it. Sometimes, your game engine’s expectations and
                                                                       your graphic’s preset values happen to match, and you get
                                                                       transparency without having to do any extra work.
                                                                          When that fails, though, try a little chroma key. It’s worked for
                                                                       the film industry for nearly 100 years, and it can work for you too.

                                                                       Links
     (Seth Kenlon, CC BY-SA 4.0)                                       [1]	https://opensource.com/article/17/10/python-101
                                                                       [2]	https://opensource.com/article/17/4/how-program-games-
     Whatever color you choose, make sure you take note of the              raspberry-pi
     HTML or HSV values so you use the exact same color for            [3]	https://opensource.com/article/18/4/easy-2d-game-
     every graphic.                                                         creation-python-and-arcade
       To fill in your alpha matte, click the Edit menu and choose     [4]	https://opensource.com/article/17/12/game-framework-
     Fill with FG Color.                                                    python
                                                                       [5] http://gimp.org/
     Flatten and export                                                [6]	https://glimpse-editor.github.io/
     If you’ve left a border around your graphic, set your back-       [7]	https://opensource.com/article/17/2/mtpaint-pixel-art-
     ground color to the color you want to use as the border                animated-gifs
     stroke. This is usually either black or white, but it can be      [8] https://www.pinta-project.com/
     anything that suits your game’s aesthetic.                        [9] http://inkscape.org/
        Once you have set the background color, click on the           [10] https://www.w3schools.com/colors/colors_picker.asp
     Image menu and select Flatten Image. It’s safe to do this         [11]	
                                                                            https://en.wikipedia.org/wiki/HSL_and_HSV



     82                            A GUIDE TO BUILDING A VIDEO GAME IN PYTHON               . CC BY-SA 4.0 . OPENSOURCE.COM