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