Platformer Template#

Quickly get started creating your own platformer!

Screenshot of platformer template

About This Template#

This is a template to get you started coding a side-scrolling platformer as quickly as possible. I recommend the following steps:

  1. Create a folder to hold the code. Copy this example to that folder, and call it main.py.

  2. Make sure the example code runs fine.

  3. Copy the tile images you want to use to a subdirectory of the folder that holds your code.

  4. Create a very simple Tiled Map using the Tiled Map Editor. I suggest just creating a floor and nothing else. For more detail on how to create a tiled map, see Step 9 - Use Tiled Map Editor.

  5. Save the file to the same directory as your source code. If you create a separate tileset, also save it to the same directory as your code.

  6. Update the code to load your file instead of the map.

  7. Test and make sure it works.

  8. Now that you are sure things work, make your own platformer!

Warning

Watch the directories!

One of the most frequent mistakes is to save maps and tile sets to a directory that isn’t the same directory as your code. Or to not have the tile images in that folder. If everything isn’t in the same folder (or a subfolder of that) it is hard to package it up later.

For more detailed instructions, see the tutorial Simple Platformer.

Source Code#

template_platformer.py#
  1"""
  2Platformer Template
  3
  4If Python and Arcade are installed, this example can be run from the command line with:
  5python -m arcade.examples.template_platformer
  6"""
  7from __future__ import annotations
  8
  9import arcade
 10
 11# --- Constants
 12SCREEN_TITLE = "Platformer"
 13
 14SCREEN_WIDTH = 1000
 15SCREEN_HEIGHT = 650
 16
 17# Constants used to scale our sprites from their original size
 18CHARACTER_SCALING = 1
 19TILE_SCALING = 0.5
 20COIN_SCALING = 0.5
 21SPRITE_PIXEL_SIZE = 128
 22GRID_PIXEL_SIZE = SPRITE_PIXEL_SIZE * TILE_SCALING
 23
 24# Movement speed of player, in pixels per frame
 25PLAYER_MOVEMENT_SPEED = 10
 26GRAVITY = 1
 27PLAYER_JUMP_SPEED = 20
 28
 29
 30class MyGame(arcade.Window):
 31    """
 32    Main application class.
 33    """
 34
 35    def __init__(self):
 36
 37        # Call the parent class and set up the window
 38        super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT,
 39                         SCREEN_TITLE, resizable=True)
 40
 41        # Our TileMap Object
 42        self.tile_map = None
 43
 44        # Our Scene Object
 45        self.scene = None
 46
 47        # Separate variable that holds the player sprite
 48        self.player_sprite = None
 49
 50        # Our physics engine
 51        self.physics_engine = None
 52
 53        # A Camera that can be used for scrolling the screen
 54        self.camera_sprites = None
 55
 56        # A non-scrolling camera that can be used to draw GUI elements
 57        self.camera_gui = None
 58
 59        # Keep track of the score
 60        self.score = 0
 61
 62        # What key is pressed down?
 63        self.left_key_down = False
 64        self.right_key_down = False
 65
 66    def setup(self):
 67        """Set up the game here. Call this function to restart the game."""
 68
 69        # Setup the Cameras
 70        self.camera_sprites = arcade.SimpleCamera()
 71        self.camera_gui = arcade.SimpleCamera()
 72
 73        # Name of map file to load
 74        map_name = ":resources:tiled_maps/map.json"
 75
 76        # Layer specific options are defined based on Layer names in a dictionary
 77        # Doing this will make the SpriteList for the platforms layer
 78        # use spatial hashing for detection.
 79        layer_options = {
 80            "Platforms": {
 81                "use_spatial_hash": True,
 82            },
 83        }
 84
 85        # Read in the tiled map
 86        self.tile_map = arcade.load_tilemap(map_name, TILE_SCALING, layer_options)
 87
 88        # Initialize Scene with our TileMap, this will automatically add all layers
 89        # from the map as SpriteLists in the scene in the proper order.
 90        self.scene = arcade.Scene.from_tilemap(self.tile_map)
 91
 92        # Set the background color
 93        if self.tile_map.background_color:
 94            self.background_color = self.tile_map.background_color
 95
 96        # Keep track of the score
 97        self.score = 0
 98
 99        # Set up the player, specifically placing it at these coordinates.
100        src = ":resources:images/animated_characters/female_adventurer/femaleAdventurer_idle.png"
101        self.player_sprite = arcade.Sprite(src, scale=CHARACTER_SCALING)
102        self.player_sprite.center_x = 128
103        self.player_sprite.center_y = 128
104        self.scene.add_sprite("Player", self.player_sprite)
105
106        # --- Other stuff
107        # Create the 'physics engine'
108        self.physics_engine = arcade.PhysicsEnginePlatformer(
109            self.player_sprite, gravity_constant=GRAVITY, walls=self.scene["Platforms"]
110        )
111
112    def on_draw(self):
113        """Render the screen."""
114
115        # Clear the screen to the background color
116        self.clear()
117
118        # Activate the game camera
119        self.camera_sprites.use()
120
121        # Draw our Scene
122        # Note, if you a want pixelated look, add pixelated=True to the parameters
123        self.scene.draw()
124
125        # Activate the GUI camera before drawing GUI elements
126        self.camera_gui.use()
127
128        # Draw our score on the screen, scrolling it with the viewport
129        score_text = f"Score: {self.score}"
130        arcade.draw_text(score_text,
131                         start_x=10,
132                         start_y=10,
133                         color=arcade.csscolor.WHITE,
134                         font_size=18)
135
136    def update_player_speed(self):
137
138        # Calculate speed based on the keys pressed
139        self.player_sprite.change_x = 0
140
141        if self.left_key_down and not self.right_key_down:
142            self.player_sprite.change_x = -PLAYER_MOVEMENT_SPEED
143        elif self.right_key_down and not self.left_key_down:
144            self.player_sprite.change_x = PLAYER_MOVEMENT_SPEED
145
146    def on_key_press(self, key, modifiers):
147        """Called whenever a key is pressed."""
148
149        # Jump
150        if key == arcade.key.UP or key == arcade.key.W:
151            if self.physics_engine.can_jump():
152                self.player_sprite.change_y = PLAYER_JUMP_SPEED
153
154        # Left
155        elif key == arcade.key.LEFT or key == arcade.key.A:
156            self.left_key_down = True
157            self.update_player_speed()
158
159        # Right
160        elif key == arcade.key.RIGHT or key == arcade.key.D:
161            self.right_key_down = True
162            self.update_player_speed()
163
164    def on_key_release(self, key, modifiers):
165        """Called when the user releases a key."""
166        if key == arcade.key.LEFT or key == arcade.key.A:
167            self.left_key_down = False
168            self.update_player_speed()
169        elif key == arcade.key.RIGHT or key == arcade.key.D:
170            self.right_key_down = False
171            self.update_player_speed()
172
173    def center_camera_to_player(self):
174        # Find where player is, then calculate lower left corner from that
175        screen_center_x = self.player_sprite.center_x - (self.camera_sprites.viewport_width / 2)
176        screen_center_y = self.player_sprite.center_y - (self.camera_sprites.viewport_height / 2)
177
178        # Set some limits on how far we scroll
179        if screen_center_x < 0:
180            screen_center_x = 0
181        if screen_center_y < 0:
182            screen_center_y = 0
183
184        # Here's our center, move to it
185        player_centered = screen_center_x, screen_center_y
186        self.camera_sprites.move_to(player_centered)
187
188    def on_update(self, delta_time):
189        """Movement and game logic"""
190
191        # Move the player with the physics engine
192        self.physics_engine.update()
193
194        # See if we hit any coins
195        coin_hit_list = arcade.check_for_collision_with_list(
196            self.player_sprite, self.scene["Coins"]
197        )
198
199        # Loop through each coin we hit (if any) and remove it
200        for coin in coin_hit_list:
201            # Remove the coin
202            coin.remove_from_sprite_lists()
203            # Add one to the score
204            self.score += 1
205
206        # Position the camera
207        self.center_camera_to_player()
208
209    def on_resize(self, width: int, height: int):
210        """ Resize window """
211        self.camera_sprites.resize(width, height)
212        self.camera_gui.resize(width, height)
213
214
215def main():
216    """Main function"""
217    window = MyGame()
218    window.setup()
219    arcade.run()
220
221
222if __name__ == "__main__":
223    main()