1"""
2Camera Example
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.camera_platform
9"""
10
11import time
12
13import arcade
14
15TILE_SCALING = 0.5
16PLAYER_SCALING = 0.5
17
18SCREEN_WIDTH = 800
19SCREEN_HEIGHT = 600
20
21SCREEN_TITLE = "Camera Example"
22SPRITE_PIXEL_SIZE = 128
23GRID_PIXEL_SIZE = SPRITE_PIXEL_SIZE * TILE_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# Map Layers
38LAYER_NAME_PLATFORMS = "Platforms"
39LAYER_NAME_COINS = "Coins"
40LAYER_NAME_BOMBS = "Bombs"
41
42
43class MyGame(arcade.Window):
44 """Main application class."""
45
46 def __init__(self):
47 """
48 Initializer
49 """
50 super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE, resizable=True)
51
52 # Our TileMap Object
53 self.tile_map = None
54
55 # Our Scene Object
56 self.scene = None
57
58 # Set up the player
59 self.score = 0
60 self.player_sprite = None
61
62 self.physics_engine = None
63 self.top_of_map = 0
64 self.end_of_map = 0
65 self.game_over = False
66 self.last_time = None
67 self.frame_count = 0
68 self.fps_message = None
69
70 # Cameras
71 self.camera = None
72 self.gui_camera = None
73
74 self.shake_offset_1 = 0
75 self.shake_offset_2 = 0
76 self.shake_vel_1 = 0
77 self.shake_vel_2 = 0
78
79 # Text
80 self.text_fps = arcade.Text(
81 "",
82 start_x=10,
83 start_y=40,
84 color=arcade.color.BLACK,
85 font_size=14,
86 )
87 self.text_score = arcade.Text(
88 f"Score: {self.score}",
89 start_x=10,
90 start_y=20,
91 color=arcade.color.BLACK,
92 font_size=14,
93 )
94
95 def setup(self):
96 """Set up the game and initialize the variables."""
97
98 # Map name
99 map_name = ":resources:tiled_maps/level_1.json"
100
101 # Layer Specific Options for the Tilemap
102 layer_options = {
103 LAYER_NAME_PLATFORMS: {
104 "use_spatial_hash": True,
105 },
106 LAYER_NAME_COINS: {
107 "use_spatial_hash": True,
108 },
109 LAYER_NAME_BOMBS: {
110 "use_spatial_hash": True,
111 },
112 }
113
114 # Load in TileMap
115 self.tile_map = arcade.load_tilemap(map_name, TILE_SCALING, layer_options)
116
117 # Initiate New Scene with our TileMap, this will automatically add all layers
118 # from the map as SpriteLists in the scene in the proper order.
119 self.scene = arcade.Scene.from_tilemap(self.tile_map)
120
121 # Set up the player
122 self.player_sprite = arcade.Sprite(
123 ":resources:images/animated_characters/female_person/femalePerson_idle.png",
124 scale=PLAYER_SCALING,
125 )
126
127 # Starting position of the player
128 self.player_sprite.center_x = 196
129 self.player_sprite.center_y = 128
130 self.scene.add_sprite("Player", self.player_sprite)
131
132 viewport = (0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)
133 self.camera = arcade.Camera(viewport=viewport)
134 self.gui_camera = arcade.Camera(viewport=viewport)
135
136 # Center camera on user
137 self.pan_camera_to_user()
138
139 # Calculate the right edge of the my_map in pixels
140 self.top_of_map = self.tile_map.height * GRID_PIXEL_SIZE
141 self.end_of_map = self.tile_map.width * GRID_PIXEL_SIZE
142
143 # --- Other stuff
144 # Set the background color
145 if self.tile_map.background_color:
146 self.background_color = self.tile_map.background_color
147
148 # Keep player from running through the wall_list layer
149 self.physics_engine = arcade.PhysicsEnginePlatformer(
150 self.player_sprite,
151 self.scene.get_sprite_list(LAYER_NAME_PLATFORMS),
152 gravity_constant=GRAVITY,
153 )
154
155 self.game_over = False
156
157 def on_resize(self, width, height):
158 """Resize window"""
159 self.camera.resize(width, height)
160 self.gui_camera.resize(width, height)
161
162 def on_draw(self):
163 """Render the screen."""
164 self.clear()
165
166 self.camera.use()
167
168 # Draw our Scene
169 self.scene.draw()
170
171 self.gui_camera.use()
172
173 # Update fps text periodically
174 if self.last_time and self.frame_count % 60 == 0:
175 fps = 1.0 / (time.time() - self.last_time) * 60
176 self.text_fps.text = f"FPS: {fps:5.2f}"
177
178 self.text_fps.draw()
179
180 if self.frame_count % 60 == 0:
181 self.last_time = time.time()
182
183 # Draw Score
184 self.text_score.draw()
185
186 # Draw game over
187 if self.game_over:
188 x = 200 + self.camera.position[0]
189 y = 200 + self.camera.position[1]
190 arcade.draw_text("Game Over", x, y, arcade.color.BLACK, 30)
191
192 self.frame_count += 1
193
194 def on_key_press(self, key, modifiers):
195 """
196 Called whenever a key is pressed
197 """
198 if key == arcade.key.UP:
199 if self.physics_engine.can_jump():
200 self.player_sprite.change_y = JUMP_SPEED
201 elif key == arcade.key.LEFT:
202 self.player_sprite.change_x = -MOVEMENT_SPEED
203 elif key == arcade.key.RIGHT:
204 self.player_sprite.change_x = MOVEMENT_SPEED
205
206 def on_key_release(self, key, modifiers):
207 """
208 Called when the user presses a mouse button.
209 """
210 if key == arcade.key.LEFT or key == arcade.key.RIGHT:
211 self.player_sprite.change_x = 0
212
213 def pan_camera_to_user(self, panning_fraction: float = 1.0):
214 """
215 Manage Scrolling
216
217 :param panning_fraction: Number from 0 to 1. Higher the number, faster we
218 pan the camera to the user.
219 """
220
221 # This spot would center on the user
222 screen_center_x = self.player_sprite.center_x - (self.camera.viewport_width / 2)
223 screen_center_y = self.player_sprite.center_y - (
224 self.camera.viewport_height / 2
225 )
226 if screen_center_x < 0:
227 screen_center_x = 0
228 if screen_center_y < 0:
229 screen_center_y = 0
230 user_centered = screen_center_x, screen_center_y
231
232 self.camera.move_to(user_centered, panning_fraction)
233
234 def on_update(self, delta_time):
235 """Movement and game logic"""
236
237 if self.player_sprite.right >= self.end_of_map:
238 self.game_over = True
239
240 # Call update on all sprites
241 if not self.game_over:
242 self.physics_engine.update()
243
244 coins_hit = arcade.check_for_collision_with_list(
245 self.player_sprite, self.scene.get_sprite_list("Coins")
246 )
247 for coin in coins_hit:
248 coin.remove_from_sprite_lists()
249 self.score += 1
250
251 # Bomb hits
252 bombs_hit = arcade.check_for_collision_with_list(
253 self.player_sprite, self.scene.get_sprite_list("Bombs")
254 )
255 for bomb in bombs_hit:
256 bomb.remove_from_sprite_lists()
257 print("Pow")
258 self.camera.shake((4, 7))
259
260 # Pan to the user
261 self.pan_camera_to_user(panning_fraction=0.12)
262
263 # Update score text
264 self.text_score.text = f"Score: {self.score}"
265
266
267def main():
268 """Get this game started."""
269 window = MyGame()
270 window.setup()
271 arcade.run()
272
273
274if __name__ == "__main__":
275 main()