摄像机在平台中的使用#

camera_platform.py#
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: arcade.camera.Camera2D = None
72 self.gui_camera = None
73
74 self.camera_shake = 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 x=10,
85 y=40,
86 color=arcade.color.BLACK,
87 font_size=14,
88 )
89 self.text_score = arcade.Text(
90 f"Score: {self.score}",
91 x=10,
92 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.Camera2D.from_raw_data(viewport=viewport)
136 self.gui_camera = arcade.camera.Camera2D.from_raw_data(viewport=viewport)
137
138 self.camera_shake = arcade.camera.grips.ScreenShake2D(self.camera.view_data,
139 max_amplitude=12.5,
140 acceleration_duration=0.05,
141 falloff_time=0.20,
142 shake_frequency=15.0)
143
144 # Center camera on user
145 self.pan_camera_to_user()
146
147 # Calculate the right edge of the my_map in pixels
148 self.top_of_map = self.tile_map.height * GRID_PIXEL_SIZE
149 self.end_of_map = self.tile_map.width * GRID_PIXEL_SIZE
150
151 # --- Other stuff
152 # Set the background color
153 if self.tile_map.background_color:
154 self.background_color = self.tile_map.background_color
155
156 # Keep player from running through the wall_list layer
157 self.physics_engine = arcade.PhysicsEnginePlatformer(
158 self.player_sprite,
159 self.scene.get_sprite_list(LAYER_NAME_PLATFORMS),
160 gravity_constant=GRAVITY,
161 )
162
163 self.game_over = False
164
165 def on_resize(self, width, height):
166 """Resize window"""
167 self.camera.projection = self.gui_camera.projection = (-width/2, width/2, -height/2, height/2)
168 self.camera.viewport = self.gui_camera.viewport = (0, 0, width, height)
169
170 def on_draw(self):
171 """Render the screen."""
172 self.clear()
173
174 self.camera_shake.update_camera()
175 self.camera.use()
176
177 # Draw our Scene
178 self.scene.draw()
179
180 # Readjust the camera so the screen shake doesn't affect
181 # the camera following algorithm.
182 self.camera_shake.readjust_camera()
183
184 self.gui_camera.use()
185
186 # Update fps text periodically
187 if self.last_time and self.frame_count % 60 == 0:
188 fps = 1.0 / (time.time() - self.last_time) * 60
189 self.text_fps.text = f"FPS: {fps:5.2f}"
190
191 self.text_fps.draw()
192
193 if self.frame_count % 60 == 0:
194 self.last_time = time.time()
195
196 # Draw Score
197 self.text_score.draw()
198
199 # Draw game over
200 if self.game_over:
201 arcade.draw_text("Game Over", self.width/2, self.height/2, arcade.color.BLACK,
202 30)
203
204 self.frame_count += 1
205
206 def on_key_press(self, key, modifiers):
207 """
208 Called whenever a key is pressed
209 """
210 if key == arcade.key.UP:
211 if self.physics_engine.can_jump():
212 self.player_sprite.change_y = JUMP_SPEED
213 elif key == arcade.key.LEFT:
214 self.player_sprite.change_x = -MOVEMENT_SPEED
215 elif key == arcade.key.RIGHT:
216 self.player_sprite.change_x = MOVEMENT_SPEED
217
218 def on_key_release(self, key, modifiers):
219 """
220 Called when the user presses a mouse button.
221 """
222 if key == arcade.key.LEFT or key == arcade.key.RIGHT:
223 self.player_sprite.change_x = 0
224
225 def pan_camera_to_user(self, panning_fraction: float = 1.0):
226 """
227 Manage Scrolling
228
229 :param panning_fraction: Number from 0 to 1. Higher the number, faster we
230 pan the camera to the user.
231 """
232
233 # This spot would center on the user
234 screen_center_x, screen_center_y = self.player_sprite.position
235 if screen_center_x < self.camera.viewport_width/2:
236 screen_center_x = self.camera.viewport_width/2
237 if screen_center_y < self.camera.viewport_height/2:
238 screen_center_y = self.camera.viewport_height/2
239 user_centered = screen_center_x, screen_center_y
240
241 self.camera.position = arcade.math.lerp_2d(self.camera.position, user_centered, panning_fraction)
242
243 def on_update(self, delta_time):
244 """Movement and game logic"""
245
246 if self.player_sprite.right >= self.end_of_map:
247 self.game_over = True
248
249 # Call update on all sprites
250 if not self.game_over:
251 self.physics_engine.update()
252 self.camera_shake.update(delta_time)
253
254 coins_hit = arcade.check_for_collision_with_list(
255 self.player_sprite, self.scene.get_sprite_list("Coins")
256 )
257 for coin in coins_hit:
258 coin.remove_from_sprite_lists()
259 self.score += 1
260
261 # Bomb hits
262 bombs_hit = arcade.check_for_collision_with_list(
263 self.player_sprite, self.scene.get_sprite_list("Bombs")
264 )
265 for bomb in bombs_hit:
266 bomb.remove_from_sprite_lists()
267 self.camera_shake.start()
268
269 # Pan to the user
270 self.pan_camera_to_user(panning_fraction=0.12)
271
272 # Update score text
273 self.text_score.text = f"Score: {self.score}"
274
275
276def main():
277 """Get this game started."""
278 window = MyGame()
279 window.setup()
280 arcade.run()
281
282
283if __name__ == "__main__":
284 main()