Tower Defense Game: Getting Started
Python package management is not the easiest thing to wrap your head around. Nevertheless, it’s important that we capture the requirements our game has, properly, and commit them to a Python package.
Getting Started
Configuring your Python Environment
You need Python 3.8 or higher to easily follow this course. The demo itself explicitly requires 3.8 or higher. You can use older versions, but certain, minor, Python language features and standard library additions are unavailable if you do.
You must ensure that your version of Python is installed with tkinter
, a UI toolkit used in the demo to handle opening and saving levels, if you want to run the demo.
It’s not a strict requirement for your own game, but you may need to install python3.X-tk
on Ubuntu, or ensure TKinter is selected during installation on Windows and Mac.
You can check that it works by typing import tkinter
.
I also recommend you develop in a virtual environment, a way of separating your packages so they don’t overlap with other projects.
Now, let’s create a package to host our game.
Creating a Python Package
Python’s package management gets a (deserved) bad reputation for complexity and confusion. But knowing how to create packages is one of those things that is difficult until you’ve done it a few times. Luckily, most packages – even ones you may end up deploying on a server somewhere – will usually work with the simple method I show you below.
Our goal is to have a proper package with a given package name – the demo is called tower
, but you should pick a name of your own – that hosts the package requirements, source code and assets.
-
Create an empty directory. I’m naming it
tower
in this example. -
Inside, optionally
git init
if you want it version-controlled. -
Now, we need to create two files. First,
setup.py
:from setuptools import setup setup()
And this placeholder file that’ll help futureproof your code. It’s called
pyproject.toml
:[build-system] requires = ["setuptools"] build-backend = "setuptools.build_meta"
-
And now for the most important file,
setup.cfg
:[metadata] name = tower version = 1.0 description = Your description long_description = A longer description license = Your license here classifiers = Programming Language :: Python :: 3 [options] zip_safe = false packages = find: install_requires = click==8.* pygame==2.* structlog [options.package_data] tower.assets.gfx = *.png tower.assets.audio = *.wav, *.ogg tower.assets.levels = *.json [options.entry_points] # Optional, but if you want to keep it, # make sure you have a function called # main() in tower.main! console_scripts = tower = tower.main:main
This is the example file used in the demo (but with the comments stripped and a few things tweaked for simplicity’s sake.) You can replace the details with your own.
The important bits deserve scrutiny though:
-
install_requires
is a newline-separated list of packages to install when the package is installed. This is important, of course, because we need these dependencies to run our code. -
name
is the name of the package that is installed. -
packages
detects your packages automatically. -
[options.package_data]
is a list of locations where Python should look for things that it would not ordinarily include, like our media assets. -
console_scripts
creates a shortcut you can invoke from your command prompt or terminal. It’s calledtower
, and it runs themain
function intower.main
.
This declarative configuration specifies the structure of your package.
-
-
Now the directory structure. Create a directory called
tower
(or whatever you prefer.)-
Inside the
tower
directory, create a blank file called__init__.py
. This is the root of your project, so that when you typeimport tower
, everything works. -
If you want to use the same directory structure as I specified in the
setup.cfg
example above, you will need a few more directories and files:
setup.py setup.cfg tower/ ├── assets │ ├── __init__.py │ ├── audio │ │ ├── __init__.py │ ├── gfx │ │ ├── __init__.py │ └── levels │ └── __init__.py ├── __init__.py └── main.py
-
Feel free to shift things around; the important thing to remember is that each sub-directory of tower
has an __init__.py
file. If you don’t want main.py
, then don’t forget to update console_scripts
.
Now you’re ready to test that your project installation works. To start, we’ll install it in editable mode, that means we tell setuptools (and thus Python) to find the code in the directory that you created the files instead of an opaque directory structure usually reserved for packages you install but don’t want to edit.
To do this, go to the root of your package with setup.cfg
and run this:
$ python -m pip install --editable .
Pip will now install your package. When it’s done, you should see something like this:
[ ... lots of output ... ] Successfully installed tower-1.0
You can confirm it works by typing this in a python
shell:
>>> import tower
If you encounter errors, check that you’ve replicated the package structure correctly and that, if you renamed things, you did it consistently. Alternatively you can use the structure in the demo to get you started.
If everything worked correctly, you’ll import your package successfully, and you’re ready to go.
- Understanding package management is important
-
Yes, we’re just writing a game, but this knowledge will work just as well with a large webapp as it would almost anything else. Our requirements – and indeed that of all but the most complex packages you find on PyPi – is a simple case of capturing requirements and, if you have non-python-code you want to include, the location of those assets.
- Doing it properly will help you down the line
-
I see a lot of Python developers subvert the packaging process by amending
sys.path
or other such trickery. That approach works just fine until it doesn’t – like when you want to share your code with someone else, or use the ability to dynamically import resources from your package (and we’ll be doing just that with our media assets.)Spend a bit of time to familiarize yourself with this process. You may one day be called upon to create a package or resolve problems with one.
- If you mess up, revert to a known state
-
This is easiest to do with source control systems like Git. You should also ensure that you delete the
<package name>.egg-info
directory as setuptools loves to cache state in that directory. - There are third-party alternatives available also
-
Tools like Poetry promise to make the process easier or more manageable, but we’re a long, long while away from deprecating and replacing the existing package machinery. So even if you do use Poetry, most other packages don’t. You’ll eventually find yourself interacting with, and debugging or extending, the standard method of writing packages.
Installing & Playing the Tower Defense Demo
This course ships with source code that demonstrates all the concepts you’ll learn about. I recommend you install and play with it, just to get a feeling for what the end-goal is, and to serve as working example that you can refer back to in case you run into problems with your own game.
First, download the demo and extract it to a directory.
Next, you must install it:
$ cd <demo path> $ python -m pip install --editable .
If done in a virtual environment, you now have a clean slate to edit, run and experiment with the demo. You can launch it by typing tower launch
or python -m tower.main launch
.
Done right, you should see the main menu appear.
The game ships with a demo level to demonstrate the game play. You can find it by selecting “Play” in the game, and navigating to the tower/assets/levels
directory and opening “demo.json”.
Sound and Graphical Assets
You can find a selection of assets – or you can use your own, itch.io is a fine source – in the demo under tower/assets
. The assets are part of the package hierarchy, so we can use Python’s import mechanism to import them without having to worry about finding out where they’re located on the file system.
Summary
Spending a little bit of time getting a proper package structure set up and working will pay dividends down the road.
- Packages capture third-party requirements
-
Keeping track of the versions of third-party packages your package depends on avoids version drift and compatibility issues. It’s a common problem where a newer version of a package is released and you inadvertently end up utilizing a package version that your software was never tested against.
- You can also include non-Python assets, like data files
-
Python packages can include data file assets – like images, sound effects, etc. – if you tell it to. We’ll use it later to load our assets into pygame in a maintainable way that avoids messing with explicit file paths.