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