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