Work with loading in a Tiled map file

Below is a quick example on using a Tiled map. For more information, see:

Screenshot of using a map created by the Tiled editor
sprite_tiled_map.py
  1"""
  2Load a Tiled map file
  3
  4Artwork from: https://kenney.nl
  5Tiled available from: https://www.mapeditor.org/
  6
  7If Python and Arcade are installed, this example can be run from the command line with:
  8python -m arcade.examples.sprite_tiled_map
  9"""
 10
 11import time
 12
 13import arcade
 14
 15TILE_SCALING = 0.5
 16PLAYER_SCALING = 1
 17
 18WINDOW_WIDTH = 1280
 19WINDOW_HEIGHT = 720
 20WINDOW_TITLE = "Sprite Tiled Map Example"
 21SPRITE_PIXEL_SIZE = 128
 22GRID_PIXEL_SIZE = SPRITE_PIXEL_SIZE * TILE_SCALING
 23CAMERA_PAN_SPEED = 0.30
 24
 25# Physics
 26MOVEMENT_SPEED = 5
 27JUMP_SPEED = 23
 28GRAVITY = 1.1
 29
 30
 31class GameView(arcade.View):
 32    """Main application class."""
 33
 34    def __init__(self):
 35        """
 36        Initializer
 37        """
 38        super().__init__()
 39
 40        # Tilemap Object
 41        self.tile_map = None
 42
 43        # Sprite lists
 44        self.player_list = None
 45        self.wall_list = None
 46        self.coin_list = None
 47
 48        # Set up the player
 49        self.score = 0
 50        self.player_sprite = None
 51
 52        self.physics_engine = None
 53        self.end_of_map = 0
 54        self.game_over = False
 55        self.last_time = None
 56        self.frame_count = 0
 57
 58        # Cameras
 59        self.camera = None
 60        self.camera_bounds = None
 61        self.gui_camera = None
 62
 63        # Text
 64        self.fps_text = arcade.Text(
 65            "",
 66            x=10,
 67            y=40,
 68            color=arcade.color.BLACK,
 69            font_size=14
 70        )
 71        self.distance_text = arcade.Text(
 72            "0.0",
 73            x=10,
 74            y=20,
 75            color=arcade.color.BLACK,
 76            font_size=14,
 77        )
 78
 79    def setup(self):
 80        """Set up the game and initialize the variables."""
 81
 82        # Sprite lists
 83        self.player_list = arcade.SpriteList()
 84
 85        # Set up the player
 86        self.player_sprite = arcade.Sprite(
 87            ":resources:images/animated_characters/female_person/femalePerson_idle.png",
 88            scale=PLAYER_SCALING,
 89        )
 90
 91        # Starting position of the player
 92        self.player_sprite.center_x = 196
 93        self.player_sprite.center_y = 270
 94        self.player_list.append(self.player_sprite)
 95
 96        map_name = ":resources:/tiled_maps/map.json"
 97
 98        layer_options = {
 99            "Platforms": {"use_spatial_hash": True},
100            "Coins": {"use_spatial_hash": True},
101        }
102
103        # Read in the tiled map
104        self.tile_map = arcade.load_tilemap(
105            map_name, layer_options=layer_options, scaling=TILE_SCALING
106        )
107        self.end_of_map = self.tile_map.width * GRID_PIXEL_SIZE
108
109        # Set wall and coin SpriteLists
110        self.wall_list = self.tile_map.sprite_lists["Platforms"]
111        self.coin_list = self.tile_map.sprite_lists["Coins"]
112
113        # --- Other stuff
114        # Set the background color
115        if self.tile_map.background_color:
116            self.window.background_color = self.tile_map.background_color
117
118        # Keep player from running through the wall_list layer
119        walls = [self.wall_list, ]
120        self.physics_engine = arcade.PhysicsEnginePlatformer(
121            self.player_sprite, walls, gravity_constant=GRAVITY
122        )
123
124        self.camera = arcade.camera.Camera2D()
125        self.gui_camera = arcade.camera.Camera2D()
126
127        # Use the tilemap to limit the camera's position
128        # we don't offset the max_y position to give a better experience.
129        max_x = GRID_PIXEL_SIZE * self.tile_map.width
130        max_y = GRID_PIXEL_SIZE * self.tile_map.height
131        self.camera_bounds = arcade.LRBT(
132            self.window.width / 2.0, max_x - self.window.width / 2.0,
133            self.window.height / 2.0, max_y
134        )
135
136        # Center camera on user
137        self.pan_camera_to_user()
138
139        self.game_over = False
140
141    def on_draw(self):
142        """
143        Render the screen.
144        """
145
146        # This command has to happen before we start drawing
147        self.camera.use()
148        self.clear()
149
150        # Start counting frames
151        self.frame_count += 1
152
153        # Draw all the sprites.
154        self.player_list.draw()
155        self.wall_list.draw()
156        self.coin_list.draw()
157
158        # Activate GUI camera for FPS, distance and hit boxes
159        # This will adjust text position based on viewport
160        self.gui_camera.use()
161
162        # Calculate FPS if conditions are met
163        if self.last_time and self.frame_count % 60 == 0:
164            fps = round(1.0 / (time.time() - self.last_time) * 60)
165            self.fps_text.text = f"FPS: {fps:3d}"
166
167        # Draw FPS text
168        self.fps_text.draw()
169
170        # Get time for every 60 frames
171        if self.frame_count % 60 == 0:
172            self.last_time = time.time()
173
174        # Enable to draw hit boxes
175        # self.wall_list.draw_hit_boxes()
176        # self.wall_list_objects.draw_hit_boxes()
177
178        # Get distance and draw text
179        distance = self.player_sprite.right
180        self.distance_text.text = f"Distance: {distance}"
181        self.distance_text.draw()
182
183        # Draw game over text if condition met
184        if self.game_over:
185            arcade.draw_text(
186                "Game Over",
187                200,
188                200,
189                arcade.color.BLACK,
190                30,
191            )
192
193    def on_key_press(self, key, modifiers):
194        """
195        Called whenever a key is pressed.
196        """
197        if key == arcade.key.UP:
198            if self.physics_engine.can_jump():
199                self.player_sprite.change_y = JUMP_SPEED
200        elif key == arcade.key.LEFT:
201            self.player_sprite.change_x = -MOVEMENT_SPEED
202        elif key == arcade.key.RIGHT:
203            self.player_sprite.change_x = MOVEMENT_SPEED
204
205    def on_key_release(self, key, modifiers):
206        """
207        Called when the user presses a mouse button.
208        """
209        if key == arcade.key.LEFT or key == arcade.key.RIGHT:
210            self.player_sprite.change_x = 0
211
212    def on_update(self, delta_time):
213        """Movement and game logic"""
214
215        if self.player_sprite.right >= self.end_of_map:
216            self.game_over = True
217
218        # Call update on all sprites
219        if not self.game_over:
220            self.physics_engine.update()
221
222        coins_hit = arcade.check_for_collision_with_list(
223            self.player_sprite, self.coin_list
224        )
225        for coin in coins_hit:
226            coin.remove_from_sprite_lists()
227            self.score += 1
228
229        # Pan to the user
230        self.pan_camera_to_user(CAMERA_PAN_SPEED)
231
232    def pan_camera_to_user(self, panning_fraction: float = 1.0):
233        """ Manage Scrolling """
234        self.camera.position = arcade.math.smerp_2d(
235            self.camera.position,
236            self.player_sprite.position,
237            self.window.delta_time,
238            panning_fraction,
239        )
240
241        self.camera.position = arcade.camera.grips.constrain_xy(
242            self.camera.view_data,
243            self.camera_bounds
244        )
245
246
247def main():
248    window = arcade.Window(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_TITLE)
249    game = GameView()
250    game.setup()
251
252    window.show_view(game)
253    window.run()
254
255
256if __name__ == "__main__":
257    main()