For many video game players, the lure of writing games is a prime reason to learn computer programming. However, building a 2D platform game such as Lode Runner, Pitfall!, or Super Mario Bros. without proper tools or guidance can leave you frustrated. Fortunately, the Python arcade library makes creating a 2D game in Python accessible for many programmers!
If you haven’t already heard about it, the arcade library is a modern Python framework for crafting games with compelling graphics and sound. Object oriented and built for Python 3.6 and above, arcade provides you with a modern set of tools for crafting great game experiences, including platform games.
By the end of this tutorial, you’ll be able to:
- Install the Python
arcadelibrary - Create a basic 2D game structure
- Find usable game artwork and other assets
- Build platform maps using the Tiled map editor
- Define player actions, game rewards, and obstacles
- Control your player with keyboard and joystick input
- Play sound effects for game actions
- Scroll the game screen with viewports to keep your player in view
- Add title, instruction, and pause screens
- Move nonplayer game elements on the screen
This tutorial assumes you have a basic understanding of writing Python programs. You should also be comfortable using the arcade library and familiar with object-oriented Python, which is used extensively in arcade.
You can download all the code, images, and sounds for this tutorial by clicking the link below:
Get the Source Code: Click here to get the source code you’ll use to build a platform game with Python Arcade in this tutorial.
Installing Python arcade
You can install arcade and its dependencies using pip:
$ python -m pip install arcade
Complete installation instructions are available for Windows, Mac, and Linux. You can even install arcade directly from source if you’d prefer.
This tutorial uses Python 3.9 and arcade 2.5.5 throughout.
Designing the Game
Before you begin writing any code, it’s beneficial to have a plan in place. Since your goal is to write a 2D platform game, it would be a good idea to define exactly what makes a game a platformer.
What Is a Platform Game?
There are a few characteristics that separate platform games from other types of games:
- The player jumps and climbs between various platforms on the game field.
- The platforms often feature uneven terrain and uneven height placements.
- Obstacles are placed in the player’s path and must be overcome to reach a goal.
These are just the minimum requirements for a platform game, and you’re free to add other features as you see fit, including:
- Multiple levels of increasing difficulty
- Rewards available throughout the game
- Multiple player lives
- Ability to destroy game obstacles
The game plan developed in this tutorial includes increasing difficulty and rewards.
Game Story
All good games have some backstory to them, even if it’s a simple one:
- The miner in Lode Runner has to collect all the gold.
- Pitfall Harry has to collect thirty-two different treasures in a set amount of time.
- Mario is tasked with rescuing Princess Toadstool.
Your game benefits from a story that connects the actions taken by the player to some overarching goal.
For this tutorial, the game story concerns a space traveler named Roz, who has crash-landed on an alien world. Before their craft crashed, Roz was thrown clear and now needs to find their space ship, fix it, and return home.
To do this, Roz must travel from their current location to the exit of each level, which brings them closer to the ship. Along the way, Roz can collect coins, which are used to fix the damaged craft. Since Roz was ejected from the ship, they don’t have any weapons and so must avoid any dangerous obstacles in the way.
While this story may seem silly, it serves the important purpose of informing the design of your levels and characters. This helps you make decisions as you implement features:
- Since Roz has no weapons, there is no way to shoot enemies that may appear.
- Roz crashed on an alien world, so enemies can be anywhere and anything.
- Because the planet is alien, gravity can be different, which may affect Roz’s jump and movement abilities.
- Roz needs to fix their damaged spaceship, which requires collecting items to do so. Right now, coins are available, but other items may be available later.
When designing a game, you can make the story as simple or involved as you like.
Game Mechanics
With a rough design in mind, you can also begin planning how you will control gameplay. Moving Roz around the game field requires a way to control several different movements:
- Left and Right to move on a platform
- Up and Down to climb ladders between platforms
- Jump to collect coins, avoid enemies, or move between platforms
Traditionally, players are controlled using the four arrow keys for directional movement, along with Space for jumping. You can also use keys such as IJKL, IJKM, or WASD if you’d like.
You aren’t limited to just keyboard input, either. The arcade library includes support for joysticks and game controllers, which you’ll explore later. Once a joystick is connected to your computer, you can move Roz by checking the position of the X- and Y-axis of the stick and jump by checking for specific button presses.
Game Assets
Now that you have an idea of how the game should work, you need to make some decisions about how the game will look and sound. The images, sprites, sounds, and even text used to display the score are collectively known as assets. They define your game in the eyes of your players. Creating them can be a challenge, taking as much time, if not more, than writing the actual game code.
Rather than create your own assets, you can download free or low-cost assets to use in your game. Many artists and designers provide sprites, backgrounds, fonts, sounds, and other content for game makers to use. Here are some sources for music, sound, and art that you can search for useful content:
| Source | Sprites | Artwork | Music | Sound Effects |
|---|---|---|---|---|
| OpenGameArt.org | X | X | X | X |
| Kenney.nl | X | X | X | X |
| Game Art 2D | X | X | ||
| ccMixter | X | X | ||
| Freesound | X | X |
For the game outlined in this tutorial, you’ll use freely available map tile images and sprites created by Kenney.nl. Sound effects provided in the downloadable source code were created by the author using MuseScore and Audacity.
Note: If you decide to use game assets that are owned or created by someone else, be sure to read, understand, and comply with any licensing requirements the owner has specified. The license may require the payment of fees or adding proper attribution, and it may impose licensing restrictions on your game. If you have any questions, consult a legal professional.
The final step before you can begin writing code is deciding how you will structure and store everything.
Defining the Program Structure
Because video games consist of graphic and sound assets as well as code, it’s important to organize your project. Keeping game assets and code properly organized will allow you to make targeted changes to the design or behavior of your game while minimizing the impact to other game aspects.
The project uses the following structure:
arcade_platformer/
|
├── arcade_platformer/
|
├── assets/
| |
│ ├── images/
| | |
│ │ ├── enemies/
| | |
│ │ ├── ground/
| | |
│ │ ├── HUD/
| | |
│ │ ├── items/
| | |
│ │ ├── player/
| | |
│ │ └── tiles/
| |
│ └── sounds/
|
└── tests/
Under the root folder of the project are the following subfolders:
arcade_platformerholds all the Python code for the game.assetsconsists of all your game images, fonts, sounds, and tile maps.testscontains any tests you may choose to write.
While there are some other gameplay decisions to be made, this is enough to begin writing code. You’ll get started by defining the basic arcade code structure in which you can build your platform game!
Defining the Game Structure in Python arcade
Your game uses the full object-oriented capabilities of arcade. To do so, you define a new class based on arcade.Window, then override methods in that class to update and render your game graphics.
Here’s a basic skeleton of what a finished game might look like. You will build on this skeleton as the game progresses:
1"""
2Arcade Platformer
3
4Demonstrating the capabilities of arcade in a platformer game
5Supporting the Arcade Platformer article
6at https://realpython.com/platformer-python-arcade/
7
8All game artwork from www.kenney.nl
9Game sounds and tile maps by author
10"""
11
12import arcade
13
14class Platformer(arcade.Window):
15 def __init__(self):
16 pass
17
18 def setup(self):
19 """Sets up the game for the current level"""
20 pass
21
22 def on_key_press(self, key: int, modifiers: int):
23 """Processes key presses
24
25 Arguments:
26 key {int} -- Which key was pressed
27 modifiers {int} -- Which modifiers were down at the time
28 """
29
30 def on_key_release(self, key: int, modifiers: int):
31 """Processes key releases
32
33 Arguments:
34 key {int} -- Which key was released
35 modifiers {int} -- Which modifiers were down at the time
36 """
37
38 def on_update(self, delta_time: float):
39 """Updates the position of all game objects
40
41 Arguments:
42 delta_time {float} -- How much time since the last call
43 """
44 pass
45
46 def on_draw(self):
47 pass
48
49if __name__ == "__main__":
50 window = Platformer()
51 window.setup()
52 arcade.run()
This basic structure provides almost everything you need to construct a 2D platformer game:
-
Line 12 imports the
arcadelibrary. -
Line 14 defines the class used to run the entire game. Methods of this class are called to update game state, process user input, and draw items on the screen.
-
Line 15 defines
.__init__(), which initializes the game object. You add code here to handle actions that should only be taken when the game first starts. -
Line 18 defines
.setup(), which sets up the game to begin playing. You add code to this method that may need to be repeated throughout the game. For example, this a great place to initialize new levels on success or reset the current level on failure. -
Lines 22 and 30 define
.on_key_press()and.on_key_release(), which allow you to process keyboard input independently.arcadetreats key presses and key releases separately, which helps avoid problems with keyboard auto-repeat. -
Line 38 defines
.on_update(), where you update the state of your game and all the objects in it. This is where collisions between objects are handled, most sound effects are played, scores are updated, and sprites are animated. This method is where everything in your game actually happens, so there is usually a lot of code here. -
Line 46 defines
.on_draw(), where everything displayed in your game is drawn. In contrast to.on_update(), this method usually contains only a few lines of code. -
Lines 49 to 52 define the main entry point for your game. This is where you:
- Create the game object
windowbased on your class defined on line 13 - Set up the game by calling
window.setup() - Kick off the game loop by calling
arcade.run()
- Create the game object
This basic structure works well for most Python arcade games.
Note: In the downloadable materials, this basic code outline is found under arcade_platformer/01_game_skeleton.py.
As you progress through this tutorial, you’ll flesh out each of these methods and add new ones to implement your game’s functionality.
Adding Initial Game Functionality
The first thing to do when starting the game is to open the game window. By the end of this section, your game will look something like this:

You can see the changes to your game skeleton in arcade_platformer/02_open_game_window.py:
11import arcade
12import pathlib
13
14# Game constants
15# Window dimensions
16SCREEN_WIDTH = 1000
17SCREEN_HEIGHT = 650
18SCREEN_TITLE = "Arcade Platformer"
19
20# Assets path
21ASSETS_PATH = pathlib.Path(__file__).resolve().parent.parent / "assets"
22
23class Platformer(arcade.Window):
24 def __init__(self) -> None:
25 super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
26
27 # These lists will hold different sets of sprites
28 self.coins = None
29 self.background = None
30 self.walls = None
31 self.ladders = None
32 self.goals = None
33 self.enemies = None
34
35 # One sprite for the player, no more is needed
36 self.player = None
37
38 # We need a physics engine as well
39 self.physics_engine = None
40
41 # Someplace to keep score
42 self.score = 0
43
44 # Which level are we on?
45 self.level = 1
46
47 # Load up our sounds here
48 self.coin_sound = arcade.load_sound(
49 str(ASSETS_PATH / "sounds" / "coin.wav")
50 )
51 self.jump_sound = arcade.load_sound(
52 str(ASSETS_PATH / "sounds" / "jump.wav")
53 )
54 self.victory_sound = arcade.load_sound(
55 str(ASSETS_PATH / "sounds" / "victory.wav")
56 )
Here’s a breakdown:
-
Lines 11 and 12 import the
arcadeandpathliblibraries you need. -
Lines 16 to 18 define several game window constants that are used to open the game window later.
-
Line 21 saves the path to your
assetsfolder, using the path of the current file as a base. Since you will be using these assets throughout the game, knowing where they are is vital. Usingpathlibensures your paths will work correctly on Windows, Mac, or Linux. -
Line 25 sets up your game window by calling the parent class’
.__init__()method usingsuper()and the constants defined above on lines 16 to 18. -
Lines 28 to 33 define six different sprite lists to hold the various sprites used in the game. It’s not strictly necessary to declare and define these here, as they will be properly and fully defined later in
.setup(). Declaring object properties is a holdover from languages like C++ or Java. Each level will have a different set of objects, which are populated in.setup():-
coinsare collectible objects Roz can find throughout the game. -
backgroundobjects are presented for visual interest only and don’t interact with anything. -
wallsare objects that Roz can’t move through. These include actual walls and the platforms on which Roz walks and jumps. -
laddersare objects that allow Roz to climb up or down. -
goalsare objects Roz must find to move to the next level. -
enemiesare objects Roz must avoid throughout the game. Contact with an enemy will end the game.
-
-
Line 36 declares the player object, which will be properly defined in
.setup(). -
Line 39 declares a physics engine that is used to manage movement and collisions.
-
Line 42 defines a variable to track the current score.
-
Line 45 defines a variable to track the current game level.
-
Lines 48 to 56 use the
ASSETS_PATHconstant defined earlier to locate and load the sound files used for collecting coins, jumping, and finishing each level.
You can add more here if you wish, but remember that .__init__() is only run when the game first starts.
Note: The sounds referenced above are provided in the downloadable materials. You can use them as provided or substitute your own sounds as you wish.
Roz needs to be able to walk, jump, and climb around the game world. Managing when and how that happens is the job of the physics engine.
What Is a Physics Engine?
In most platformers, the user moves the player using a joystick or the keyboard. They might make the player jump or walk the player off a platform. Once the player is in midair, the user doesn’t need to do anything else to make them fall to a lower platform. Controlling where a player can walk and how they fall after they jump or walk off a platform is handled by the physics engine.
In a game, the physics engine provides an approximation of the physical forces that act on players and other game objects. These forces may impart or impact the movement of game objects, including jumping, climbing, falling, and blocking movement.
There are three physics engines included in Python arcade:
-
arcade.PhysicsEngineSimpleis a very basic engine that handles the movement and interactions of a single player sprite and a sprite list of walls. This is useful for top-down games, where gravity is not a factor. -
arcade.PhysicsEnginePlatformeris a more complex engine tailored for use in platform games. In addition to basic movement, it provides a gravity force that pulls objects to the bottom of the screen. It also provides the player a way to jump and climb ladders. -
arcade.PymunkPhysicsEngineis built on top of Pymunk, a 2D physics library that uses the Chipmunk library. Pymunk makes extremely realistic physics calculations available toarcadeapplications.
For this tutorial you will use the arcade.PhysicsEnginePlatformer.
In order to properly set up the arcade.PhysicsEnginePlatformer, you must provide the player sprite as well as two sprite lists containing the walls and ladders with which the player interacts. Since the walls and ladders vary based on the level, you can’t define the physics engine formally until the level is set up, which happens in .setup().
Speaking of levels, how do you define those anyway? As with most things, there’s more than one way to get the job done.
Building Game Levels
Back when video games were still distributed on floppy disks, it was difficult to store all the game level data needed for a game. Many game makers resorted to writing code to create levels. While this method saves disk space, using imperative code to generate game levels limits your ability to modify or augment them later.
As storage space became less expensive, games took advantage by storing more of their assets in data files, which were read and processed by the code. Game levels could now be created and modified without changing the game code, which allowed artists and game designers to contribute without needing to understand the underlying code. This declarative method of level design allows for more flexibility when designing and developing games.
The disadvantage to declarative game level design is the need to not only define the data but store it as well. Fortunately, there’s a tool available that can do both, and it works extremely well with arcade.
Tiled is an open source 2D game level editor that produces files that can be read and used by Python arcade. Tiled allows you to create a collection of images called a tileset, which is used to create a tile map defining each level of your game. You can use Tiled to create tile maps for top-down, isometric, and side-scrolling games, including the levels for your game:

Tiled comes with a great set of docs and a great intro tutorial as well. To get you started and hopefully whet your appetite for more, next you’ll walk through the steps to create your first map level.
Downloading and Starting Tiled
Before you run Tiled, you need to download it. The current version at the time of writing was Tiled version 1.4.3, which was available for Windows, Mac, and Linux in a variety of formats. When downloading, consider supporting its continued maintenance by making a donation as well.
Once you’ve downloaded Tiled, you can start it for the first time. You’ll see the following window:

Click New Map to create the tile map for your first level. The following dialog will appear:

These default tile map properties are great for platform games and represent the best options for an arcade game. Here’s a quick breakdown of other options you can select:
- Orientation specifies how the map is displayed and edited.
- Orthogonal maps are square and are used for top-down and platform games.
arcadeworks best with orthogonal maps. - Isometric maps shift the viewpoint to be a nonsquare angle to the game field, providing a pseudo-3D view of the 2D world. Staggered isometric maps specify that the top edge of the map is the top edge of the view.
- Hexagonal maps use hexagons rather than squares for each map tile (although Tiled displays squares in the editor).
- Orthogonal maps are square and are used for top-down and platform games.
- Tile layer format specifies how the map is stored on disk. Compression using zlib helps conserve disk space.
- Tile render order specifies how tiles are stored in the file and ultimately how they’re rendered by the game engine.
- Map size sets the size of the map to be stored, in tile units. Specifying the map as Infinite tells Tiled to determine the final size based on the edits made.
- Tile size specifies the size of each tile in pixels. If you’re using artwork from an external source, set this to the size of the tiles in that set. The artwork provided for this tutorial uses square sprites that measure 128 × 128 pixels. This means that every tile consists of around 16,000 pixels and that they can be stored on disk and in memory in a way that can increase game performance if necessary.
Click Save As to save the level. Since this is a game asset, save it as arcade_platformer/assets/platform_level_01.tmx.
Tile maps consist of a set of tiles that are placed on specific map layers. To begin defining a tile map for a level, you must first define the tileset to use and the layers on which they appear.
Creating a Tileset
The tiles used to create your level are contained in a tileset. The tileset is associated with the tile map and provides all the sprite images required to define the level.
You define and interact with a tileset using the Tilesets view, located in the lower-right corner of the Tiled window:

Click the New Tileset button to define the tileset for this level. Tiled presents a dialog asking for some information about the new tileset to create:

You have the following options for your new tileset:
- Name is the name of your tileset. Call this one
arcade_platformer. - Type specifies how the tileset will be defined:
- Collection of Images indicates that each tile is contained in a single, separate image on disk. You should select this option, as
arcadeworks best with individual tile images. - Based on Tileset Image indicates that all the tiles are combined into one single large image that Tiled needs to process to locate each individual image. Only select this option if the assets you are using require it.
- Collection of Images indicates that each tile is contained in a single, separate image on disk. You should select this option, as
- Embed in Map tells Tiled to store the tileset in the tile map. Keep this unchecked, as you will save and use the tileset as a separate resource in multiple tile maps.
Click Save As and save it as assets/arcade_platformer.tsx. To reuse this tileset on future tile maps, select Map → Add External Tileset to include it.
Defining the Tileset
Your new tileset is initially empty, so you need to populate it with tiles. You do this by locating your tile images and adding them to the set. Each image should be the same dimensions as the Tile size you defined when you created the tile map.
This example assumes you have downloaded the game assets for this tutorial. You can do so by clicking the link below:
Get the Source Code: Click here to get the source code you’ll use to build a platform game with Python Arcade in this tutorial.
Alternatively, you can download the Platformer Pack Redux (360 Assets) and move the contents of the PNG folder to your arcade-platformer/assets/images folder. Recall that your tile map is located under arcade-platformer/assets, as this will be important later.
On the toolbar, click the blue plus sign (+) or select Tileset → Add Tiles to begin the process. You will be presented with the following dialog:

From here, navigate to the folders listed below to add the specified resources to your tileset:
| Folder | File |
|---|---|
arcade-platformer/assets/images/ground/Grass |
All Files |
arcade-platformer/assets/images/HUD |
hudHeart_empty.png |
hudHeart_full.png |
|
hudHeart_half.png |
|
hudX.png |
|
arcade-platformer/assets/images/items |
coinBronze.png |
coinGold.png |
|
coinSilver.png |
|
flagGreen_down.png |
|
flagGreen1.png |
|
flagGreen2.png |
|
arcade-platformer/assets/images/tiles |
doorOpen_mid.png |
doorOpen_top.png |
|
grass.png |
|
ladderMid.png |
|
ladderTop.png |
|
signExit.png |
|
signLeft.png |
|
signRight.png |
|
torch1.png |
|
torch2.png |
|
water.png |
|
waterTop_high.png |
|
waterTop_low.png |
When you’re done adding files, your tileset should look like this:

If you don’t see all your tiles, click the Dynamically Wrap Tiles button on the toolbar to show them all.
Save your new tileset using Ctrl+S or File → Save from the menu and return to your tile map. You’ll see the new tileset in the lower right of the Tiled interface, ready for use in defining your tile map!
Defining Map Layers
Every item in a level serves a specific purpose:
- Ground and walls define where and how your player can move.
- Coins and other collectible items score points and unlock achievements.
- Ladders allow the player to climb to new platforms but don’t otherwise block movement.
- Background items provide visual interest and may provide information.
- Enemies provide obstacles for the player to avoid.
- Goals provide a reason to move around the level.
Each of these different item types requires different handling in arcade. Therefore, it makes sense to keep them separate when defining them in Tiled. Tiled allows you to do just that by using map layers. By placing different item types on different map layers and processing each layer separately, you can track and handle each type of sprite differently.
To define a layer, first open the Layers view in the upper-right corner of the Tiled screen:

The default layer is already set and selected. Rename this layer as ground by clicking the layer, then changing the Name in the Properties view on the left. Alternatively, you can double-click the name to edit it directly in the Layers panel:

This layer will contain your ground tiles, including walls through which the player can’t walk.
Creating new layers requires you to define not only the layer name but also the layer type. Tiled provides four types of layers:
- Tile layers allow you to place tiles from your tileset onto the map. Placement is restricted to grid locations, and tiles must be placed as defined.
- Object layers allow you to place objects such as collectibles or triggers on the map. Objects may be tiles from the tile map or freely drawn shapes, and they may be visible or not. Each object can be freely positioned, scaled, and rotated.
- Image layers allow you to place images onto the map for use as background or foreground imagery.
- Group layers allow you to gather layers into groups for easier map management.
For this tutorial, you’ll use an object layer to place coins on the map and tile layers for everything else.
To create the new tile layers, click New Layer in the Layers view, then select Tile Layer:

Create three new tile layers named ladders, background, and goal.
Next, create a new object layer called coins to hold your collectibles:

You can arrange the layers in any order you like using the arrow buttons at the bottom of the layer view. Now you can start laying out your level!
Designing a Level
In the book Classic Game Design, author and game developer Franz Lanzinger defines eight rules for classic game design. Here are the first three rules:
- Keep it simple.
- Start gameplay immediately.
- Ramp difficulty from easy to hard.
Similarly, veteran game developer Steve Goodwin talks about balancing games in his book Polished Game Development. He stresses that good game balance starts with level 1, which “should be the first one developed and the last one finished.”
With these ideas in mind, here are some guidelines for designing your platformer levels:
- The first level of the game should introduce the user to basic game features and controls.
- Make the initial obstacles easy to overcome.
- Make the first collectibles impossible to miss and later ones more difficult to obtain.
- Don’t introduce obstacles that require finesse to overcome until the user has learned to navigate the world.
- Don’t introduce enemies until the user has learned to overcome obstacles.
Below is a closer look at a first level designed with these guidelines in mind. In the downloadable materials, this complete level design is found under assets/platform_level_01.tmx:

The player starts on the left and proceeds to the right, indicated by the arrow pointing to the right. As the player moves right, they find a bronze coin, which will increase their score. A second bronze coin is found later hanging higher in the air, which demonstrates to the player that coins may be anywhere. Then the player finds a gold coin, which has a different point value.
The player then climbs a ramp, which demonstrates that there is more of the world above them. At the top of the hill is the final gold coin, which they have to jump to get. On the other side of the hill is the exit, which is also marked.
This simple level helps show the user how to move and jump. It shows that there are collectible items in the world worth points. It also shows items that are informative or decorative and with which the player does not interact, such as the arrow sign, exit sign, and grass tufts. Finally, it shows them what the goal looks like.
With the hard work of designing your first level complete, you can now build it in Tiled.
Building a Level
Before you can place coins and the goal, you need to know how to get there. So the first thing to define is where the ground is located. With your tile map selected in Tiled, select the ground layer to build.
Note: Make sure you have the correct layer selected when placing tiles on your tile map. If not, arcade won’t be able to properly handle your items.
From your tileset, select the grassCenter tile. Then, click in any grid on the bottom row of your tile map to set that tile in place:

With the first tileset, you can drag across the bottom row to set everything to grassCenter. Then, select the grassMid tile to draw the grassy top of the level across the second row:

Continue building the level using the grass tiles to build a two-tile-high hill starting about halfway through the world. Leave a space of four tiles at the right edge to provide room for the player to walk down the hill and for the exit sign and exit portal.
Next, switch to the goal layer and place the exit portal tiles one tile in from the far-right edge:

With the basic platform and goal in place, you can place some background items. Switch to the background layer, place an arrow on the left side to direct the player where to go and an Exit sign