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()