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