Work with levels and a tiled map#

Screenshot of using a larger map created by the Tiled Map Editor
sprite_tiled_map_with_levels.py#
  1"""
  2Load a Tiled map file with Levels
  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_with_levels
  9"""
 10import time
 11
 12import arcade
 13
 14TILE_SPRITE_SCALING = 0.5
 15PLAYER_SCALING = 0.6
 16
 17SCREEN_WIDTH = 800
 18SCREEN_HEIGHT = 600
 19SCREEN_TITLE = "Sprite Tiled Map with Levels Example"
 20SPRITE_PIXEL_SIZE = 128
 21GRID_PIXEL_SIZE = SPRITE_PIXEL_SIZE * TILE_SPRITE_SCALING
 22
 23# How many pixels to keep as a minimum margin between the character
 24# and the edge of the screen.
 25VIEWPORT_MARGIN_TOP = 60
 26VIEWPORT_MARGIN_BOTTOM = 60
 27VIEWPORT_RIGHT_MARGIN = 270
 28VIEWPORT_LEFT_MARGIN = 270
 29
 30# Physics
 31MOVEMENT_SPEED = 5
 32JUMP_SPEED = 23
 33GRAVITY = 1.1
 34
 35
 36class MyGame(arcade.Window):
 37    """Main application class."""
 38
 39    def __init__(self):
 40        """
 41        Initializer
 42        """
 43        super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
 44
 45        # Tilemap Object
 46        self.tile_map = None
 47
 48        # Sprite lists
 49        self.player_list = None
 50
 51        # Set up the player
 52        self.score = 0
 53        self.player_sprite = None
 54
 55        self.physics_engine = None
 56        self.view_left = 0
 57        self.view_bottom = 0
 58        self.end_of_map = 0
 59        self.game_over = False
 60        self.last_time = None
 61        self.frame_count = 0
 62        self.fps_message = None
 63
 64        self.level = 1
 65        self.max_level = 2
 66
 67    def setup(self):
 68        """Set up the game and initialize the variables."""
 69
 70        # Sprite lists
 71        self.player_list = arcade.SpriteList()
 72
 73        # Set up the player
 74        self.player_sprite = arcade.Sprite(
 75            ":resources:images/animated_characters/female_person/femalePerson_idle.png",
 76            PLAYER_SCALING,
 77        )
 78
 79        # Starting position of the player
 80        self.player_sprite.center_x = 128
 81        self.player_sprite.center_y = 64
 82        self.player_list.append(self.player_sprite)
 83
 84        self.load_level(self.level)
 85
 86        self.game_over = False
 87
 88    def load_level(self, level):
 89        # layer_options = {"Platforms": {"use_spatial_hash": True}}
 90
 91        # Read in the tiled map
 92        self.tile_map = arcade.load_tilemap(
 93            f":resources:tiled_maps/level_{level}.json", scaling=TILE_SPRITE_SCALING
 94        )
 95
 96        # --- Walls ---
 97
 98        # Calculate the right edge of the my_map in pixels
 99        self.end_of_map = self.tile_map.width * GRID_PIXEL_SIZE
100
101        self.physics_engine = arcade.PhysicsEnginePlatformer(
102            self.player_sprite,
103            self.tile_map.sprite_lists["Platforms"],
104            gravity_constant=GRAVITY,
105        )
106
107        # --- Other stuff
108        # Set the background color
109        if self.tile_map.background_color:
110            arcade.set_background_color(self.tile_map.background_color)
111
112        # Set the view port boundaries
113        # These numbers set where we have 'scrolled' to.
114        self.view_left = 0
115        self.view_bottom = 0
116
117    def on_draw(self):
118        """
119        Render the screen.
120        """
121
122        self.frame_count += 1
123
124        # This command has to happen before we start drawing
125        self.clear()
126
127        # Draw all the sprites.
128        self.player_list.draw()
129        self.tile_map.sprite_lists["Platforms"].draw()
130
131        if self.last_time and self.frame_count % 60 == 0:
132            fps = 1.0 / (time.time() - self.last_time) * 60
133            self.fps_message = f"FPS: {fps:5.0f}"
134
135        if self.fps_message:
136            arcade.draw_text(
137                self.fps_message,
138                self.view_left + 10,
139                self.view_bottom + 40,
140                arcade.color.BLACK,
141                14,
142            )
143
144        if self.frame_count % 60 == 0:
145            self.last_time = time.time()
146
147        # Put the text on the screen.
148        # Adjust the text position based on the view port so that we don't
149        # scroll the text too.
150        distance = self.player_sprite.right
151        output = f"Distance: {distance:.0f}"
152        arcade.draw_text(
153            output, self.view_left + 10, self.view_bottom + 20, arcade.color.BLACK, 14
154        )
155
156        if self.game_over:
157            arcade.draw_text(
158                "Game Over",
159                self.view_left + 200,
160                self.view_bottom + 200,
161                arcade.color.BLACK,
162                30,
163            )
164
165    def on_key_press(self, key, modifiers):
166        """
167        Called whenever the mouse moves.
168        """
169        if key == arcade.key.UP:
170            if self.physics_engine.can_jump():
171                self.player_sprite.change_y = JUMP_SPEED
172        elif key == arcade.key.LEFT:
173            self.player_sprite.change_x = -MOVEMENT_SPEED
174        elif key == arcade.key.RIGHT:
175            self.player_sprite.change_x = MOVEMENT_SPEED
176
177    def on_key_release(self, key, modifiers):
178        """
179        Called when the user presses a mouse button.
180        """
181        if key == arcade.key.LEFT or key == arcade.key.RIGHT:
182            self.player_sprite.change_x = 0
183
184    def on_update(self, delta_time):
185        """Movement and game logic"""
186
187        if self.player_sprite.right >= self.end_of_map:
188            if self.level < self.max_level:
189                self.level += 1
190                self.load_level(self.level)
191                self.player_sprite.center_x = 128
192                self.player_sprite.center_y = 64
193                self.player_sprite.change_x = 0
194                self.player_sprite.change_y = 0
195            else:
196                self.game_over = True
197
198        # Call update on all sprites (The sprites don't do much in this
199        # example though.)
200        if not self.game_over:
201            self.physics_engine.update()
202
203        # --- Manage Scrolling ---
204
205        # Track if we need to change the view port
206
207        changed = False
208
209        # Scroll left
210        left_bndry = self.view_left + VIEWPORT_LEFT_MARGIN
211        if self.player_sprite.left < left_bndry:
212            self.view_left -= left_bndry - self.player_sprite.left
213            changed = True
214
215        # Scroll right
216        right_bndry = self.view_left + SCREEN_WIDTH - VIEWPORT_RIGHT_MARGIN
217        if self.player_sprite.right > right_bndry:
218            self.view_left += self.player_sprite.right - right_bndry
219            changed = True
220
221        # Scroll up
222        top_bndry = self.view_bottom + SCREEN_HEIGHT - VIEWPORT_MARGIN_TOP
223        if self.player_sprite.top > top_bndry:
224            self.view_bottom += self.player_sprite.top - top_bndry
225            changed = True
226
227        # Scroll down
228        bottom_bndry = self.view_bottom + VIEWPORT_MARGIN_BOTTOM
229        if self.player_sprite.bottom < bottom_bndry:
230            self.view_bottom -= bottom_bndry - self.player_sprite.bottom
231            changed = True
232
233        # If we need to scroll, go ahead and do it.
234        if changed:
235            self.view_left = int(self.view_left)
236            self.view_bottom = int(self.view_bottom)
237            arcade.set_viewport(
238                self.view_left,
239                SCREEN_WIDTH + self.view_left,
240                self.view_bottom,
241                SCREEN_HEIGHT + self.view_bottom,
242            )
243
244
245def main():
246    window = MyGame()
247    window.setup()
248    arcade.run()
249
250
251if __name__ == "__main__":
252    main()