第14步-多个级别#
现在我们要让我们的游戏有多个关卡。目前,我们只有两个级别,但此技术可以很容易地扩展到包括更多级别。
首先,在 __init__
函数来表示标记地图结束的位置,以及我们应该加载的级别。
# Where is the right edge of the map?
self.end_of_map = 0
# Level number to load
self.level = 1
下一位是 setup
函数,我们将根据我们创建的Level变量将地图加载调用更改为使用f字符串来加载 Mapfile 。
# Load our TileMap
self.tile_map = arcade.load_tilemap(f":resources:tiled_maps/map2_level_{self.level}.json", scaling=TILE_SCALING, layer_options=layer_options)
同样,在设置函数中,我们将计算当前加载的地图的边缘在哪里,以像素为单位。要做到这一点,我们得到地图的宽度,用瓷砖的数量表示,并将其乘以瓷砖宽度。我们还需要考虑瓷砖的缩放,因为我们是以像素为单位进行测量的。
# Calculate the right edge of the map in pixels
self.end_of_map = (self.tile_map.width * self.tile_map.tile_width) * self.tile_map.scaling
现在在 on_update
函数,我们将添加一个块来对照地图值的末尾检查球员的位置。我们将在这之前完成这项工作 center_camera_to_player
末尾的函数调用。这将增加我们当前的级别,并利用 setup
功能,以便用新关卡重新加载游戏。
# Check if the player got to the end of the level
if self.player_sprite.center_x >= self.end_of_map:
# Advance to the next level
self.level += 1
# Reload game with new level
self.setup()
如果你在这一点上运行游戏,你将能够到达第一级的终点,并有下一级的负荷,并通过它进行比赛。在这一点上我们有两个问题,你注意到了吗?第一个问题是玩家的分数在不同级别之间重置,也许你希望在游戏中发生这种情况,但我们会在这里修复它,这样当切换级别时,我们不会重置分数。
为此,首先将一个新变量添加到 __init__
该函数将作为触发器,以了解是否应重置分数。我们希望能够在玩家输了的时候重置比分,所以这个触发器将帮助我们只在我们想要的时候重置比分。
# Should we reset the score?
self.reset_score = True
现在在 setup
函数,我们可以用这段代码替换分数重置。我们改变了 reset_score
变量在重置分数后返回为True,因为我们游戏中的默认设置应该是重置它,并且我们只在我们想要关闭它的时候才关闭它。
# Reset the score if we should
if self.reset_score:
self.score = 0
self.reset_score = True
最后,在 on_update
我们提高了级别,我们可以添加这一行来关闭分数重置
# Turn off score reset when advancing level
self.reset_score = False
现在球员的得分将在不同级别之间保持不变,但我们还有一个问题。如果你到达第二关的终点,游戏就会崩溃!这是因为我们实际上只有两个级别可用,但我们仍然试图在达到级别2的末尾时将级别提升到3。
有几种方法可以处理这个问题,一种方法是简单地设置更多的关卡。最终,你必须有一个最终的关卡,所以这可能不是最好的解决方案。作为练习,看看你是否能找到一种优雅地处理最后关卡的方法。你可以显示结束屏幕,或者从头开始重新开始游戏,或者任何你想要的东西。
源代码#
1"""
2Platformer Game
3
4python -m arcade.examples.platform_tutorial.14_multiple_levels
5"""
6import arcade
7
8# Constants
9SCREEN_WIDTH = 800
10SCREEN_HEIGHT = 600
11SCREEN_TITLE = "Platformer"
12
13# Constants used to scale our sprites from their original size
14TILE_SCALING = 0.5
15COIN_SCALING = 0.5
16
17# Movement speed of player, in pixels per frame
18PLAYER_MOVEMENT_SPEED = 5
19GRAVITY = 1
20PLAYER_JUMP_SPEED = 20
21
22
23class MyGame(arcade.Window):
24 """
25 Main application class.
26 """
27
28 def __init__(self):
29
30 # Call the parent class and set up the window
31 super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
32
33 # Variable to hold our texture for our player
34 self.player_texture = None
35
36 # Separate variable that holds the player sprite
37 self.player_sprite = None
38
39 # Variable to hold our Tiled Map
40 self.tile_map = None
41
42 # Replacing all of our SpriteLists with a Scene variable
43 self.scene = None
44
45 # A variable to store our camera object
46 self.camera = None
47
48 # A variable to store our gui camera object
49 self.gui_camera = None
50
51 # This variable will store our score as an integer.
52 self.score = 0
53
54 # This variable will store the text for score that we will draw to the screen.
55 self.score_text = None
56
57 # Where is the right edge of the map?
58 self.end_of_map = 0
59
60 # Level number to load
61 self.level = 1
62
63 # Should we reset the score?
64 self.reset_score = True
65
66 # Load sounds
67 self.collect_coin_sound = arcade.load_sound(":resources:sounds/coin1.wav")
68 self.jump_sound = arcade.load_sound(":resources:sounds/jump1.wav")
69 self.gameover_sound = arcade.load_sound(":resources:sounds/gameover1.wav")
70
71 def setup(self):
72 """Set up the game here. Call this function to restart the game."""
73 layer_options = {
74 "Platforms": {
75 "use_spatial_hash": True
76 }
77 }
78
79 # Load our TileMap
80 self.tile_map = arcade.load_tilemap(f":resources:tiled_maps/map2_level_{self.level}.json", scaling=TILE_SCALING, layer_options=layer_options)
81
82 # Create our Scene Based on the TileMap
83 self.scene = arcade.Scene.from_tilemap(self.tile_map)
84
85 self.player_texture = arcade.load_texture(":resources:images/animated_characters/female_adventurer/femaleAdventurer_idle.png")
86
87 # Add Player Spritelist before "Foreground" layer. This will make the foreground
88 # be drawn after the player, making it appear to be in front of the Player.
89 # Setting before using scene.add_sprite allows us to define where the SpriteList
90 # will be in the draw order. If we just use add_sprite, it will be appended to the
91 # end of the order.
92 self.scene.add_sprite_list_after("Player", "Foreground")
93
94 self.player_sprite = arcade.Sprite(self.player_texture)
95 self.player_sprite.center_x = 128
96 self.player_sprite.center_y = 128
97 self.scene.add_sprite("Player", self.player_sprite)
98
99 # Create a Platformer Physics Engine, this will handle moving our
100 # player as well as collisions between the player sprite and
101 # whatever SpriteList we specify for the walls.
102 # It is important to supply static to the walls parameter. There is a
103 # platforms parameter that is intended for moving platforms.
104 # If a platform is supposed to move, and is added to the walls list,
105 # it will not be moved.
106 self.physics_engine = arcade.PhysicsEnginePlatformer(
107 self.player_sprite, walls=self.scene["Platforms"], gravity_constant=GRAVITY
108 )
109
110 # Initialize our camera, setting a viewport the size of our window.
111 self.camera = arcade.camera.Camera2D()
112
113 # Initialize our gui camera, initial settings are the same as our world camera.
114 self.gui_camera = arcade.camera.Camera2D()
115
116 # Reset the score if we should
117 if self.reset_score:
118 self.score = 0
119 self.reset_score = True
120
121 # Initialize our arcade.Text object for score
122 self.score_text = arcade.Text(f"Score: {self.score}", x=0, y=5)
123
124 self.background_color = arcade.csscolor.CORNFLOWER_BLUE
125
126 # Calculate the right edge of the map in pixels
127 self.end_of_map = (self.tile_map.width * self.tile_map.tile_width) * self.tile_map.scaling
128 print(self.end_of_map)
129
130 def on_draw(self):
131 """Render the screen."""
132
133 # Clear the screen to the background color
134 self.clear()
135
136 # Activate our camera before drawing
137 self.camera.use()
138
139 # Draw our Scene
140 self.scene.draw()
141
142 # Activate our GUI camera
143 self.gui_camera.use()
144
145 # Draw our Score
146 self.score_text.draw()
147
148 def on_update(self, delta_time):
149 """Movement and Game Logic"""
150
151 # Move the player using our physics engine
152 self.physics_engine.update()
153
154 # See if we hit any coins
155 coin_hit_list = arcade.check_for_collision_with_list(
156 self.player_sprite, self.scene["Coins"]
157 )
158
159 # Loop through each coin we hit (if any) and remove it
160 for coin in coin_hit_list:
161 # Remove the coin
162 coin.remove_from_sprite_lists()
163 arcade.play_sound(self.collect_coin_sound)
164 self.score += 75
165 self.score_text.text = f"Score: {self.score}"
166
167 if arcade.check_for_collision_with_list(
168 self.player_sprite, self.scene["Don't Touch"]
169 ):
170 arcade.play_sound(self.gameover_sound)
171 self.setup()
172
173 # Check if the player got to the end of the level
174 if self.player_sprite.center_x >= self.end_of_map:
175 # Advance to the next level
176 self.level += 1
177
178 # Turn off score reset when advancing level
179 self.reset_score = False
180
181 # Reload game with new level
182 self.setup()
183
184 # Center our camera on the player
185 self.camera.position = self.player_sprite.position
186
187 def on_key_press(self, key, modifiers):
188 """Called whenever a key is pressed."""
189
190 if key == arcade.key.ESCAPE:
191 self.setup()
192
193 if key == arcade.key.UP or key == arcade.key.W:
194 if self.physics_engine.can_jump():
195 self.player_sprite.change_y = PLAYER_JUMP_SPEED
196 arcade.play_sound(self.jump_sound)
197
198 if key == arcade.key.LEFT or key == arcade.key.A:
199 self.player_sprite.change_x = -PLAYER_MOVEMENT_SPEED
200 elif key == arcade.key.RIGHT or key == arcade.key.D:
201 self.player_sprite.change_x = PLAYER_MOVEMENT_SPEED
202
203 def on_key_release(self, key, modifiers):
204 """Called whenever a key is released."""
205
206 if key == arcade.key.LEFT or key == arcade.key.A:
207 self.player_sprite.change_x = 0
208 elif key == arcade.key.RIGHT or key == arcade.key.D:
209 self.player_sprite.change_x = 0
210
211
212def main():
213 """Main function"""
214 window = MyGame()
215 window.setup()
216 arcade.run()
217
218
219if __name__ == "__main__":
220 main()