18. Verwenden der Klasse Window

../../_images/window.svg

Wir können eine Klasse verwenden, um unser Programm repräsentieren. Die Arcade-Bibliothek verfügt über eine integrierte Klasse, die ein Fenster auf dem Bildschirm darstellt. Wir können unsere eigene Kindklasse erstellen und Funktionen überschreiben, um Folgendes zu erreichen:

  • Starten und Initialisierung

  • Zeichnen der Elemente auf unserem Bildschirm

  • Animieren/Aktualisieren der Positionen von Elementen auf unserem Bildschirm

  • Auf Tastatureingaben reagieren

  • Auf die Mausaktionen reagieren

Eine der besten Möglichkeiten, das Programmieren zu lernen, ist das Betrachten von Beispielcode. In diesem Kapitel findest du einige Beispiele, anhand derer du Folgendes lernen kannst:

  • Fenster mit einem objektorientierten Ansatz öffnen

  • Objekte animieren

  • Objekte mit der Maus bewegen

  • Objekte mit der Tastatur verschieben

  • Objekte mit dem Joystick bewegen

18.1. Erstellen eines Fensters mit einer Klasse

Bisher haben wir die Funktion open_window verwendet, um ein Fenster zu öffnen. Hier ist der Code:

open_window_with_function.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import arcade

SCREEN_WIDTH = 640
SCREEN_HEIGHT = 480


def main():
    arcade.open_window(SCREEN_WIDTH, SCREEN_HEIGHT, "Drawing Example")

    arcade.run()


main()

Wir können auch ein Exemplar der Klasse Window erstellen, um ein Fenster zu öffnen. Der Programmcode ist ziemlich einfach:

open_window_with_object.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import arcade

SCREEN_WIDTH = 640
SCREEN_HEIGHT = 480


def main():
    window = arcade.Window(SCREEN_WIDTH, SCREEN_HEIGHT, "Drawing Example")

    arcade.run()


main()

Funktionsaufrufe und Aufrufe zum Erstellen eines Objekts sehen sehr ähnlich aus. Der verräterische Hinweis darauf, dass wir im zweiten Beispiel eine Instanz eines Objekts erstellen, ist die Tatsache, dass Window groß geschrieben wird.

18.2. Erweitern der Klasse Window

Die Window-Klasse von Arcade verfügt über eine Reihe integrierter Methoden, die bei Bedarf automatisch aufgerufen werden: Methoden zum Zeichnen, zum Reagieren auf Tastatur, Maus und mehr. Du kannst alle Methoden in der Window Class Documentation nachschlagen. Standardmäßig tun diese Methoden jedoch nichts. Das müssen wir ändern.

Wie wir im vorherigen Kapitel kennengelernt haben, können wir die Funktionalität einer Klasse erweitern, indem wir eine Kindklasse erstellen. Daher können wir die Klasse Window erweitern, indem wir eine Kindklasse davon erstellen. Ich werde meine Kindklasse MyGame nennen.

extension_window_class.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import arcade


class MyGame(arcade.Window):

    def __init__(self, width, height, title):

        # Call the parent class's init function
        super().__init__(width, height, title)


def main():
    window = MyGame(640, 480, "Drawing Example")

    arcade.run()


main()

18.3. Zeichnen mit der Klasse Window

Um mit der Klasse Window zu zeichnen, müssen wir unsere eigene Methode on_draw erstellen. Diese überschreibt die Standardmethode on_draw, die in die Klasse Window eingebaut ist. Wir werden unseren Code zum Zeichnen dort einfügen.

Die Methode on_draw wird ungefähr 60 mal pro Sekunde aufgerufen. Wir werden diese Tatsache nutzen, wenn wir die Bilder animieren.

Wir müssen auch die Hintergrundfarbe einstellen. Da wir das nur einmal machen müssen, setzen wir sie in der __init__-Methode. Es ergibt keinen Sinn, den Hintergrund 60 Mal pro Sekunde einzustellen, wenn er sich nicht ändert.

drawing.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
import arcade


class MyGame(arcade.Window):

    def __init__(self, width, height, title):

        # Call the parent class's init function
        super().__init__(width, height, title)

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

    def on_draw(self):
        """ Called whenever we need to draw the window. """
        arcade.start_render()

        arcade.draw_circle_filled(50, 50, 15, arcade.color.AUBURN)


def main():
    window = MyGame(640, 480, "Drawing Example")

    arcade.run()


main()

Das Ergebnis dieses Programms sieht so aus:

../../_images/draw_example.png

18.4. Animieren

Durch Überschreiben der update-Methode können wir unsere Ballposition aktualisieren und unsere Szene animieren:

simple_animation.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
import arcade


class MyGame(arcade.Window):

    def __init__(self, width, height, title):

        # Call the parent class's init function
        super().__init__(width, height, title)

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

        # Attributes to store where our ball is
        self.ball_x = 50
        self.ball_y = 50

    def on_draw(self):
        """ Called whenever we need to draw the window. """
        arcade.start_render()

        arcade.draw_circle_filled(self.ball_x, self.ball_y, 15, arcade.color.AUBURN)

    def update(self, delta_time):
        """ Called to update our objects. Happens approximately 60 times per second."""
        self.ball_x += 1
        self.ball_y += 1


def main():
    window = MyGame(640, 480, "Drawing Example")

    arcade.run()


main()

18.4.1. Kapselung unseres Animationsobjekts

Es braucht nicht viel Vorstellungskraft, um zu begreifen, dass das Hinzufügen weiterer Parameter zum Ball, das Abprallen des Balls oder sogar das Anzeigen mehrerer Bälle auf dem Bildschirm unsere Klasse MyApplication sehr komplex machen würde.

Wenn es nur eine Möglichkeit gäbe, all das Ball-Zeug zusammenzufassen. Warte! Es gibt sie! Verwende Klassen!

Hier ist ein komplexeres Beispiel, aber die gesamte Logik für den Ball wurde in eine neue Klasse Ball verschoben.

ball_class_example.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
import arcade

SCREEN_WIDTH = 640
SCREEN_HEIGHT = 480


class Ball:
    """ This class manages a ball bouncing on the screen. """

    def __init__(self, position_x, position_y, change_x, change_y, radius, color):
        """ Constructor. """

        # Take the parameters of the init function above, and create instance variables out of them.
        self.position_x = position_x
        self.position_y = position_y
        self.change_x = change_x
        self.change_y = change_y
        self.radius = radius
        self.color = color

    def draw(self):
        """ Draw the balls with the instance variables we have. """
        arcade.draw_circle_filled(self.position_x, self.position_y, self.radius, self.color)

    def update(self):
        """ Code to control the ball's movement. """

        # Move the ball
        self.position_y += self.change_y
        self.position_x += self.change_x

        # See if the ball hit the edge of the screen. If so, change direction
        if self.position_x < self.radius:
            self.change_x *= -1

        if self.position_x > SCREEN_WIDTH - self.radius:
            self.change_x *= -1

        if self.position_y < self.radius:
            self.change_y *= -1

        if self.position_y > SCREEN_HEIGHT - self.radius:
            self.change_y *= -1


class MyGame(arcade.Window):
    """ My window class. """

    def __init__(self, width, height, title):
        """ Constructor. """

        # Call the parent class's init function
        super().__init__(width, height, title)
        arcade.set_background_color(arcade.color.ASH_GREY)

        # Create our ball
        self.ball = Ball(50, 50, 3, 3, 15, arcade.color.AUBURN)

    def on_draw(self):
        """ Called whenever we need to draw the window. """
        arcade.start_render()
        self.ball.draw()

    def update(self, delta_time):
        """ Called to update our objects. Happens approximately 60 times per second."""
        self.ball.update()


def main():
    window = MyGame(640, 480, "Drawing Example")

    arcade.run()


main()

Hier ist es in Aktion:

../../_images/ball_class_example.gif

18.4.2. Eine Liste animieren

Wäre es nicht schön, mehrere Objekte zu animieren? Wie verfolgen wir mehrere Objekte? Mit einer Liste! In diesem Beispiel werden drei Bälle gleichzeitig animiert.

ball_list_example.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
import arcade

SCREEN_WIDTH = 640
SCREEN_HEIGHT = 480


class Ball:
    """ This class manages a ball bouncing on the screen. """

    def __init__(self, position_x, position_y, change_x, change_y, radius, color):
        """ Constructor. """

        # Take the parameters of the init function above, and create instance variables out of them.
        self.position_x = position_x
        self.position_y = position_y
        self.change_x = change_x
        self.change_y = change_y
        self.radius = radius
        self.color = color

    def draw(self):
        """ Draw the balls with the instance variables we have. """
        arcade.draw_circle_filled(self.position_x, self.position_y, self.radius, self.color)

    def update(self):
        """ Code to control the ball's movement. """

        # Move the ball
        self.position_y += self.change_y
        self.position_x += self.change_x

        # See if the ball hit the edge of the screen. If so, change direction
        if self.position_x < self.radius:
            self.change_x *= -1

        if self.position_x > SCREEN_WIDTH - self.radius:
            self.change_x *= -1

        if self.position_y < self.radius:
            self.change_y *= -1

        if self.position_y > SCREEN_HEIGHT - self.radius:
            self.change_y *= -1


class MyGame(arcade.Window):

    def __init__(self, width, height, title):

        # Call the parent class's init function
        super().__init__(width, height, title)
        arcade.set_background_color(arcade.color.ASH_GREY)

        # Create a list for the balls
        self.ball_list = []

        # Add three balls to the list
        ball = Ball(50, 50, 3, 3, 15, arcade.color.AUBURN)
        self.ball_list.append(ball)

        ball = Ball(100, 150, 2, 3, 15, arcade.color.PURPLE_MOUNTAIN_MAJESTY)
        self.ball_list.append(ball)

        ball = Ball(150, 250, -3, -1, 15, arcade.color.FOREST_GREEN)
        self.ball_list.append(ball)

    def on_draw(self):
        """ Called whenever we need to draw the window. """
        arcade.start_render()

        # Use a "for" loop to pull each ball from the list, then call the draw
        # method on that ball.
        for ball in self.ball_list:
            ball.draw()

    def update(self, delta_time):
        """ Called to update our objects. Happens approximately 60 times per second."""

        # Use a "for" loop to pull each ball from the list, then call the update
        # method on that ball.
        for ball in self.ball_list:
            ball.update()


def main():
    window = MyGame(640, 480, "Drawing Example")

    arcade.run()


main()
../../_images/ball_list_example.gif