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: http://kenney.nl
  5Tiled available from: http://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"""
 10
 11import arcade
 12import os
 13import time
 14
 15TILE_SPRITE_SCALING = 0.5
 16PLAYER_SCALING = 0.6
 17
 18SCREEN_WIDTH = 800
 19SCREEN_HEIGHT = 600
 20SCREEN_TITLE = "Sprite Tiled Map with Levels Example"
 21SPRITE_PIXEL_SIZE = 128
 22GRID_PIXEL_SIZE = (SPRITE_PIXEL_SIZE * TILE_SPRITE_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        # Set the working directory (where we expect to find files) to the same
 47        # directory this .py file is in. You can leave this out of your own
 48        # code, but it is needed to easily run the examples using "python -m"
 49        # as mentioned at the top of this program.
 50        file_path = os.path.dirname(os.path.abspath(__file__))
 51        os.chdir(file_path)
 52
 53        # Sprite lists
 54        self.wall_list = None
 55        self.player_list = None
 56        self.coin_list = None
 57
 58        # Set up the player
 59        self.score = 0
 60        self.player_sprite = None
 61
 62        self.physics_engine = None
 63        self.view_left = 0
 64        self.view_bottom = 0
 65        self.end_of_map = 0
 66        self.game_over = False
 67        self.last_time = None
 68        self.frame_count = 0
 69        self.fps_message = None
 70
 71        self.level = 1
 72        self.max_level = 2
 73
 74    def setup(self):
 75        """ Set up the game and initialize the variables. """
 76
 77        # Sprite lists
 78        self.player_list = arcade.SpriteList()
 79        self.coin_list = arcade.SpriteList()
 80
 81        # Set up the player
 82        self.player_sprite = arcade.Sprite(":resources:images/animated_characters/female_person/femalePerson_idle.png",
 83                                           PLAYER_SCALING)
 84
 85        # Starting position of the player
 86        self.player_sprite.center_x = 128
 87        self.player_sprite.center_y = 64
 88        self.player_list.append(self.player_sprite)
 89
 90        self.load_level(self.level)
 91
 92        self.game_over = False
 93
 94    def load_level(self, level):
 95        # Read in the tiled map
 96        my_map = arcade.tilemap.read_tmx(f":resources:tmx_maps/level_{level}.tmx")
 97
 98        # --- Walls ---
 99
100        # Calculate the right edge of the my_map in pixels
101        self.end_of_map = my_map.map_size.width * GRID_PIXEL_SIZE
102
103        # Grab the layer of items we can't move through
104        self.wall_list = arcade.tilemap.process_layer(my_map,
105                                                      'Platforms',
106                                                      TILE_SPRITE_SCALING,
107                                                      use_spatial_hash=True)
108
109        self.physics_engine = arcade.PhysicsEnginePlatformer(self.player_sprite,
110                                                             self.wall_list,
111                                                             gravity_constant=GRAVITY)
112
113        # --- Other stuff
114        # Set the background color
115        if my_map.background_color:
116            arcade.set_background_color(my_map.background_color)
117
118        # Set the view port boundaries
119        # These numbers set where we have 'scrolled' to.
120        self.view_left = 0
121        self.view_bottom = 0
122
123    def on_draw(self):
124        """
125        Render the screen.
126        """
127
128        self.frame_count += 1
129
130        # This command has to happen before we start drawing
131        arcade.start_render()
132
133        # Draw all the sprites.
134        self.player_list.draw()
135        self.wall_list.draw()
136        self.coin_list.draw()
137
138        if self.last_time and self.frame_count % 60 == 0:
139            fps = 1.0 / (time.time() - self.last_time) * 60
140            self.fps_message = f"FPS: {fps:5.0f}"
141
142        if self.fps_message:
143            arcade.draw_text(self.fps_message, self.view_left + 10, self.view_bottom + 40, arcade.color.BLACK, 14)
144
145        if self.frame_count % 60 == 0:
146            self.last_time = time.time()
147
148        # Put the text on the screen.
149        # Adjust the text position based on the view port so that we don't
150        # scroll the text too.
151        distance = self.player_sprite.right
152        output = f"Distance: {distance:.0f}"
153        arcade.draw_text(output, self.view_left + 10, self.view_bottom + 20, arcade.color.BLACK, 14)
154
155        if self.game_over:
156            arcade.draw_text("Game Over", self.view_left + 200, self.view_bottom + 200, arcade.color.BLACK, 30)
157
158    def on_key_press(self, key, modifiers):
159        """
160        Called whenever the mouse moves.
161        """
162        if key == arcade.key.UP:
163            if self.physics_engine.can_jump():
164                self.player_sprite.change_y = JUMP_SPEED
165        elif key == arcade.key.LEFT:
166            self.player_sprite.change_x = -MOVEMENT_SPEED
167        elif key == arcade.key.RIGHT:
168            self.player_sprite.change_x = MOVEMENT_SPEED
169
170    def on_key_release(self, key, modifiers):
171        """
172        Called when the user presses a mouse button.
173        """
174        if key == arcade.key.LEFT or key == arcade.key.RIGHT:
175            self.player_sprite.change_x = 0
176
177    def on_update(self, delta_time):
178        """ Movement and game logic """
179
180        if self.player_sprite.right >= self.end_of_map:
181            if self.level < self.max_level:
182                self.level += 1
183                self.load_level(self.level)
184                self.player_sprite.center_x = 128
185                self.player_sprite.center_y = 64
186                self.player_sprite.change_x = 0
187                self.player_sprite.change_y = 0
188            else:
189                self.game_over = True
190
191        # Call update on all sprites (The sprites don't do much in this
192        # example though.)
193        if not self.game_over:
194            self.physics_engine.update()
195
196        coins_hit = arcade.check_for_collision_with_list(self.player_sprite, self.coin_list)
197        for coin in coins_hit:
198            coin.remove_from_sprite_lists()
199            self.score += 1
200
201        # --- Manage Scrolling ---
202
203        # Track if we need to change the view port
204
205        changed = False
206
207        # Scroll left
208        left_bndry = self.view_left + VIEWPORT_LEFT_MARGIN
209        if self.player_sprite.left < left_bndry:
210            self.view_left -= left_bndry - self.player_sprite.left
211            changed = True
212
213        # Scroll right
214        right_bndry = self.view_left + SCREEN_WIDTH - VIEWPORT_RIGHT_MARGIN
215        if self.player_sprite.right > right_bndry:
216            self.view_left += self.player_sprite.right - right_bndry
217            changed = True
218
219        # Scroll up
220        top_bndry = self.view_bottom + SCREEN_HEIGHT - VIEWPORT_MARGIN_TOP
221        if self.player_sprite.top > top_bndry:
222            self.view_bottom += self.player_sprite.top - top_bndry
223            changed = True
224
225        # Scroll down
226        bottom_bndry = self.view_bottom + VIEWPORT_MARGIN_BOTTOM
227        if self.player_sprite.bottom < bottom_bndry:
228            self.view_bottom -= bottom_bndry - self.player_sprite.bottom
229            changed = True
230
231        # If we need to scroll, go ahead and do it.
232        if changed:
233            self.view_left = int(self.view_left)
234            self.view_bottom = int(self.view_bottom)
235            arcade.set_viewport(self.view_left,
236                                SCREEN_WIDTH + self.view_left,
237                                self.view_bottom,
238                                SCREEN_HEIGHT + self.view_bottom)
239
240
241def main():
242    window = MyGame()
243    window.setup()
244    arcade.run()
245
246
247if __name__ == "__main__":
248    main()