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