24. Verwenden von Sprites zum Schießen

Wie bekommen wir Sprites, die wir schießen können?

../../_images/sprites_bullet.gif

Münzen abschießen

24.1. Einstieg

Kehren wir zunächst zu einem Programm für den Einstieg zurück.

Einstiegsprogramm zum Schießen von Sprites
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
import random
import arcade

SPRITE_SCALING_PLAYER = 0.5
SPRITE_SCALING_COIN = 0.2
SPRITE_SCALING_LASER = 0.8
COIN_COUNT = 50

SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600

BULLET_SPEED = 5


class MyGame(arcade.Window):
    """ Main application class. """

    def __init__(self):
        """ Initializer """
        # Call the parent class initializer
        super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, "Sprites and Bullets Demo")

        # Variables that will hold sprite lists
        self.player_list = None
        self.coin_list = None

        # Set up the player info
        self.player_sprite = None
        self.score = 0

        # Don't show the mouse cursor
        self.set_mouse_visible(False)

        arcade.set_background_color(arcade.color.AMAZON)

    def setup(self):

        """ Set up the game and initialize the variables. """

        # Sprite lists
        self.player_list = arcade.SpriteList()
        self.coin_list = arcade.SpriteList()

        # Set up the player
        self.score = 0

        # Image from kenney.nl
        self.player_sprite = arcade.Sprite("character.png", SPRITE_SCALING_PLAYER)
        self.player_sprite.center_x = 50
        self.player_sprite.center_y = 70
        self.player_list.append(self.player_sprite)

        # Create the coins
        for i in range(COIN_COUNT):

            # Create the coin instance
            # Coin image from kenney.nl
            coin = arcade.Sprite("coin_01.png", SPRITE_SCALING_COIN)

            # Position the coin
            coin.center_x = random.randrange(SCREEN_WIDTH)
            coin.center_y = random.randrange(SCREEN_HEIGHT)

            # Add the coin to the lists
            self.coin_list.append(coin)

        # Set the background color
        arcade.set_background_color(arcade.color.AMAZON)

    def on_draw(self):
        """
        Render the screen.
        """

        # This command has to happen before we start drawing
        arcade.start_render()

        # Draw all the sprites.
        self.coin_list.draw()
        self.player_list.draw()

        # Render the text
        arcade.draw_text(f"Score: {self.score}", 10, 20, arcade.color.WHITE, 14)

    def on_mouse_motion(self, x, y, dx, dy):
        """
        Called whenever the mouse moves.
        """
        self.player_sprite.center_x = x
        self.player_sprite.center_y = y

    def on_mouse_press(self, x, y, button, modifiers):
        """
        Called whenever the mouse button is clicked.
        """
        pass

    def update(self, delta_time):
        """ Movement and game logic """

        # Call update on all sprites
        self.coin_list.update()


def main():
    window = MyGame()
    window.setup()
    arcade.run()


if __name__ == "__main__":
    main()

Wenn du dieses Programm ausführst, sollte sich die Spielfigur auf dem Bildschirm bewegen und es sollte Münzen geben. Aber sonst passiert noch nicht viel.

Als nächstes brauchen wir ein ‚Schuss‘-Bild:

../../_images/laserBlue01.png

laserBlue01.png

Lade dieses Bild (ursprünglich von Kenney.nl) herunter und vergewissere dich, dass es sich im selben Ordner wie dein Code befindet.

24.2. Die Spielfigur unten behalten

Im Moment kann sich die Spielfigur überall auf dem Bildschirm bewegen. Wir wollen das Sprite am unteren Bildschirmrand fixieren.

Entferne dazu einfach die Codezeile, um die Spielfigur auf der y-Achse zu bewegen. Die Spielfigur behält den gleichen y-Wert bei, den wir in der Methode setup gesetzt haben.

def on_mouse_motion(self, x, y, dx, dy):
    """
    Called whenever the mouse moves.
    """
    self.player_sprite.center_x = x
    # REMOVE THIS LINE: self.player_sprite.center_y = y

24.3. Die Münzen nach oben bewegen

Wir wollen, dass sich alle Münzen über der Spielfigur befinden. Wir können also die Startpositionen so einstellen, dass sie einen Startpunkt von 150 anstelle von 0 haben. Dadurch bleiben sie über der Spielfigur.

# Create the coins
for i in range(COIN_COUNT):

    # Create the coin instance
    # Coin image from kenney.nl
    coin = arcade.Sprite("coin_01.png", SPRITE_SCALING_COIN)

    # Position the coin
    coin.center_x = random.randrange(SCREEN_WIDTH)
    coin.center_y = random.randrange(150, SCREEN_HEIGHT)

    # Add the coin to the lists
    self.coin_list.append(coin)

24.4. Liste mit Schüssen einrichten

Wir müssen eine Liste erstellen, um die Schüsse/Kugeln zu verwalten. Es gibt vier Stellen, an denen wir diesen bullet_list Code hinzufügen müssen:

  • Erstellen der Variable bullet_list (Zeile 26)

  • Erstellen eines Exemplar von SpriteList (Zeile 44)

  • Zeichnen der Schussliste (Zeile 83)

  • Aktualisieren der Schussliste (Zeile 105)

Einrichten einer Schussliste
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import random
import arcade

SPRITE_SCALING_PLAYER = 0.5
SPRITE_SCALING_COIN = 0.2
SPRITE_SCALING_LASER = 0.8
COIN_COUNT = 50

SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600

BULLET_SPEED = 5


class MyGame(arcade.Window):
    """ Main application class. """

    def __init__(self):
        """ Initializer """
        # Call the parent class initializer
        super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, "Sprites and Bullets Demo")

        # Variables that will hold sprite lists
        self.player_list = None
        self.coin_list = None
        self.bullet_list = None

        # Set up the player info
        self.player_sprite = None
        self.score = 0

        # Don't show the mouse cursor
        self.set_mouse_visible(False)

        arcade.set_background_color(arcade.color.AMAZON)

    def setup(self):

        """ Set up the game and initialize the variables. """

        # Sprite lists
        self.player_list = arcade.SpriteList()
        self.coin_list = arcade.SpriteList()
        self.bullet_list = arcade.SpriteList()

        # Set up the player
        self.score = 0

        # Image from kenney.nl
        self.player_sprite = arcade.Sprite("character.png", SPRITE_SCALING_PLAYER)
        self.player_sprite.center_x = 50
        self.player_sprite.center_y = 70
        self.player_list.append(self.player_sprite)

        # Create the coins
        for i in range(COIN_COUNT):

            # Create the coin instance
            # Coin image from kenney.nl
            coin = arcade.Sprite("coin_01.png", SPRITE_SCALING_COIN)

            # Position the coin
            coin.center_x = random.randrange(SCREEN_WIDTH)
            coin.center_y = random.randrange(150, SCREEN_HEIGHT)

            # Add the coin to the lists
            self.coin_list.append(coin)

        # Set the background color
        arcade.set_background_color(arcade.color.AMAZON)

    def on_draw(self):
        """
        Render the screen.
        """

        # This command has to happen before we start drawing
        arcade.start_render()

        # Draw all the sprites.
        self.coin_list.draw()
        self.player_list.draw()
        self.bullet_list.draw()

        # Render the text
        arcade.draw_text(f"Score: {self.score}", 10, 20, arcade.color.WHITE, 14)

    def on_mouse_motion(self, x, y, dx, dy):
        """
        Called whenever the mouse moves.
        """
        self.player_sprite.center_x = x

    def on_mouse_press(self, x, y, button, modifiers):
        """
        Called whenever the mouse button is clicked.
        """
        pass

    def update(self, delta_time):
        """ Movement and game logic """

        # Call update on all sprites
        self.coin_list.update()
        self.bullet_list.update()


def main():
    window = MyGame()
    window.setup()
    arcade.run()


if __name__ == "__main__":
    main()

24.5. Schüsse erstellen

Wir müssen Schüsse erstellen, wenn der Benutzer die Maustaste drückt. Wir können eine on_mouse_press-Methode hinzufügen, um etwas zu tun, wenn der Benutzer die Maustaste drückt:

def on_mouse_press(self, x, y, button, modifiers):

    # Create a bullet
    bullet = arcade.Sprite("laserBlue01.png", SPRITE_SCALING_LASER)

    # Add the bullet to the appropriate list
    self.bullet_list.append(bullet)

Dadurch wird ein Schuss erstellt, der Schuss befindet sich jedoch standardmäßig in der unteren linken Ecke. Man kann ihn kaum sehen.

Wir können dem Schuss eine Position zuweisen:

def on_mouse_press(self, x, y, button, modifiers):

    # Create a bullet
    bullet = arcade.Sprite("laserBlue01.png", SPRITE_SCALING_LASER)

    bullet.center_x = x
    bullet.center_y = y

    # Add the bullet to the appropriate list
    self.bullet_list.append(bullet)

Aber das wollen wir auch nicht. Der obige Code platziert den Laser dort, wo wir mit der Maus klicken. Wir wollen, dass der Laser dort ist, wo sich die Spielfigur befindet. Das ist einfach:

def on_mouse_press(self, x, y, button, modifiers):

    # Create a bullet
    bullet = arcade.Sprite("laserBlue01.png", SPRITE_SCALING_LASER)

    bullet.center_x = self.player_sprite.center_x
    bullet.center_y = self.player_sprite.center_y

    # Add the bullet to the appropriate list
    self.bullet_list.append(bullet)

We can even start the bullet a bit ABOVE the player:

def on_mouse_press(self, x, y, button, modifiers):

    # Create a bullet
    bullet = arcade.Sprite("laserBlue01.png", SPRITE_SCALING_LASER)

    bullet.center_x = self.player_sprite.center_x
    bullet.center_y = self.player_sprite.center_y + 30

    # Add the bullet to the appropriate list
    self.bullet_list.append(bullet)

Wir können den Schuss mit der Konstante BULLET_SPEED nach oben bewegen, die wir oben im Programm auf 5 setzen:

def on_mouse_press(self, x, y, button, modifiers):

    # Create a bullet
    bullet = arcade.Sprite("laserBlue01.png", SPRITE_SCALING_LASER)

    bullet.center_x = self.player_sprite.center_x
    bullet.center_y = self.player_sprite.center_y + 30
    bullet.change_y = BULLET_SPEED

    # Add the bullet to the appropriate list
    self.bullet_list.append(bullet)

Wir können den Schuss so drehen, dass er nicht seitwärts gerichtet ist. Verwende dazu das Attribut angle der Klasse Sprite:

def on_mouse_press(self, x, y, button, modifiers):

    # Create a bullet
    bullet = arcade.Sprite("laserBlue01.png", SPRITE_SCALING_LASER)

    bullet.center_x = self.player_sprite.center_x
    bullet.center_y = self.player_sprite.center_y + 30
    bullet.change_y = BULLET_SPEED
    bullet.angle = 90

    # Add the bullet to the appropriate list
    self.bullet_list.append(bullet)

24.6. Schuss-Kollisionen

Wie bringen wir die Schüsse dazu, mit den Münzen zusammen zu stoßen? Wir fügen unserer update-Methode Folgendes hinzu:

# Loop through each bullet
for bullet in self.bullet_list:

    # Check this bullet to see if it hit a coin
    hit_list = arcade.check_for_collision_with_list(bullet, self.coin_list)

    # If it did, get rid of the bullet
    if len(hit_list) > 0:
        bullet.remove_from_sprite_lists()

    # For every coin we hit, add to the score and remove the coin
    for coin in hit_list:
        coin.remove_from_sprite_lists()
        self.score += 1

    # If the bullet flies off-screen, remove it.
    if bullet.bottom > SCREEN_HEIGHT:
        bullet.remove_from_sprite_lists()

Wir iterieren mit einer for-Schleife über jeden Schuss. Dabei prüfen wir, ob der Schuss eine der Münzen trifft. Wenn ja, werden wir die Münze los. Wir werden in diesem Fall auch den Schuss los.

Wir überprüfen auch, ob der Schuss oben am Bildschirm landet. Wenn das der Fall ist, entfernen wir den Schuss. Dies kann man leicht vergessen. Wenn man es aber vergisst, verlangsamt sich der Rechner, da er Tausende von Schüssen verfolgt, die vor langer Zeit den uns interessierenden Bereich verlassen haben.

Hier ist das vollständige Beispiel:

sprites_bullet.py
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
import random
import arcade

SPRITE_SCALING_PLAYER = 0.5
SPRITE_SCALING_COIN = 0.2
SPRITE_SCALING_LASER = 0.8
COIN_COUNT = 50

SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600

BULLET_SPEED = 5


class MyGame(arcade.Window):
    """ Main application class. """

    def __init__(self):
        """ Initializer """
        # Call the parent class initializer
        super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, "Sprites and Bullets Demo")

        # Variables that will hold sprite lists
        self.player_list = None
        self.coin_list = None
        self.bullet_list = None

        # Set up the player info
        self.player_sprite = None
        self.score = 0

        # Don't show the mouse cursor
        self.set_mouse_visible(False)

        arcade.set_background_color(arcade.color.AMAZON)

    def setup(self):

        """ Set up the game and initialize the variables. """

        # Sprite lists
        self.player_list = arcade.SpriteList()
        self.coin_list = arcade.SpriteList()
        self.bullet_list = arcade.SpriteList()

        # Set up the player
        self.score = 0

        # Image from kenney.nl
        self.player_sprite = arcade.Sprite("character.png", SPRITE_SCALING_PLAYER)
        self.player_sprite.center_x = 50
        self.player_sprite.center_y = 70
        self.player_list.append(self.player_sprite)

        # Create the coins
        for i in range(COIN_COUNT):

            # Create the coin instance
            # Coin image from kenney.nl
            coin = arcade.Sprite("coin_01.png", SPRITE_SCALING_COIN)

            # Position the coin
            coin.center_x = random.randrange(SCREEN_WIDTH)
            coin.center_y = random.randrange(120, SCREEN_HEIGHT)

            # Add the coin to the lists
            self.coin_list.append(coin)

        # Set the background color
        arcade.set_background_color(arcade.color.AMAZON)

    def on_draw(self):
        """
        Render the screen.
        """

        # This command has to happen before we start drawing
        arcade.start_render()

        # Draw all the sprites.
        self.coin_list.draw()
        self.bullet_list.draw()
        self.player_list.draw()

        # Render the text
        arcade.draw_text(f"Score: {self.score}", 10, 20, arcade.color.WHITE, 14)

    def on_mouse_motion(self, x, y, dx, dy):
        """
        Called whenever the mouse moves.
        """
        self.player_sprite.center_x = x

    def on_mouse_press(self, x, y, button, modifiers):
        """
        Called whenever the mouse button is clicked.
        """

        # Create a bullet
        bullet = arcade.Sprite("laserBlue01.png", SPRITE_SCALING_LASER)

        # The image points to the right, and we want it to point up. So
        # rotate it.
        bullet.angle = 90

        # Position the bullet
        bullet.center_x = self.player_sprite.center_x
        bullet.bottom = self.player_sprite.top

        # Add the bullet to the appropriate lists
        self.bullet_list.append(bullet)

    def update(self, delta_time):
        """ Movement and game logic """

        # Call update on all sprites
        self.coin_list.update()
        self.bullet_list.update()

        # Loop through each bullet
        for bullet in self.bullet_list:

            # Check this bullet to see if it hit a coin
            hit_list = arcade.check_for_collision_with_list(bullet, self.coin_list)

            # If it did, get rid of the bullet
            if len(hit_list) > 0:
                bullet.remove_from_sprite_lists()

            # For every coin we hit, add to the score and remove the coin
            for coin in hit_list:
                coin.remove_from_sprite_lists()
                self.score += 1

            # If the bullet flies off-screen, remove it.
            if bullet.bottom > SCREEN_HEIGHT:
                bullet.remove_from_sprite_lists()


def main():
    window = MyGame()
    window.setup()
    arcade.run()


if __name__ == "__main__":
    main()