19. Steuerung durch den Benutzer¶
Wie interagieren wir mit dem Benutzer? Wie ermöglichen wir dem Benutzer, ein Objekt auf dem Bildschirm zu bewegen?
Wir können dies mit der Maus, mit der Tastatur oder mit dem Game-Controller tun.
19.1. Bewegen mit der Maus¶
Der Schlüssel zum Verwalten der Mausbewegungen ist die Methode on_mouse_motion
in der Klasse arcade.Window
zu überschreiben. Diese Methode wird jedes Mal aufgerufen, wenn sich die Maus bewegt. Die Methodendefinition sieht folgendermaßen aus:
def on_mouse_motion(self, x, y, dx, dy):
Das x
und y
sind die Koordinaten der Maus. Das dx
und dy
repräsentieren die Änderung von x
und y
seit dem letzten Aufruf der Methode.
Wenn du ein grafisches Element auf dem Bildschirm mit der Maus steuerst, willst du häufig den Mauszeiger verstecken. Wenn du den Mauszeiger nicht sehen möchtest, rufe in der Methode __init__
die folgende Methode der Elternklasse auf:
self.set_mouse_visible(False)
Das folgende Beispiel nimmt unsere Klasse Ball
und bewegt sie mit der Maus über den Bildschirm.
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 | import arcade
SCREEN_WIDTH = 640
SCREEN_HEIGHT = 480
class Ball:
def __init__(self, position_x, position_y, radius, color):
# 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.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)
class MyGame(arcade.Window):
def __init__(self, width, height, title):
# Call the parent class's init function
super().__init__(width, height, title)
# Make the mouse disappear when it is over the window.
# So we just see our object, not the pointer.
self.set_mouse_visible(False)
arcade.set_background_color(arcade.color.ASH_GREY)
# Create our ball
self.ball = Ball(50, 50, 15, arcade.color.AUBURN)
def on_draw(self):
""" Called whenever we need to draw the window. """
arcade.start_render()
self.ball.draw()
def on_mouse_motion(self, x, y, dx, dy):
""" Called to update our objects. Happens approximately 60 times per second."""
self.ball.position_x = x
self.ball.position_y = y
def main():
window = MyGame(640, 480, "Drawing Example")
arcade.run()
main()
|
19.2. Mausklicks¶
Wir können Mausklicks auch verarbeiten, indem wir eine on_mouse_press
-Methode definieren:
def on_mouse_press(self, x, y, button, modifiers):
""" Called when the user presses a mouse button. """
if button == arcade.MOUSE_BUTTON_LEFT:
print("Left mouse button pressed at", x, y)
elif button == arcade.MOUSE_BUTTON_RIGHT:
print("Right mouse button pressed at", x, y)
19.3. Bewegen mit der Tastatur¶
Das Bewegen mit der Tastatur ähnelt unserem Beispiel für einen springenden Ball. Es gibt nur zwei Unterschiede:
Wir steuern die Werte von
change_x
undchange_y
mit der Tastatur.Wenn wir den Bildschirmrand erreichen, halten wir an, statt abzuprallen.
Um festzustellen, wenn eine Taste gedrückt wird, überschreiben wir die Methode on_key_press
. Man könnte vermuten, dass wir einen Tastendruck als ein Ereignis zu bekommen. Aber es sind tatsächlich zwei. Wenn die Taste gedrückt wird, bewegen wir uns. Wenn die Taste losgelassen wird, bleiben wir stehen. Das ergibt zwei Ereignisse. Das Loslassen einer Taste wird durch on_key_release
gesteuert.
Diese Methoden haben eine key
-Variable als Parameter, die mit einer if
-Anweisung mit den Werten in arcade.key verglichen werden kann.
def on_key_press(self, key, modifiers):
if key == arcade.key.LEFT:
print("Left key hit")
elif key == arcade.key.A:
print("The 'a' key was hit")
Wir können dies in einem Programm verwenden, um einen Ball über den Bildschirm zu bewegen. Siehe die markierten Zeilen im Programm unten:
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 | import arcade
SCREEN_WIDTH = 640
SCREEN_HEIGHT = 480
MOVEMENT_SPEED = 3
class Ball:
def __init__(self, position_x, position_y, change_x, change_y, radius, color):
# 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):
# Move the ball
self.position_y += self.change_y
self.position_x += self.change_x
class MyGame(arcade.Window):
def __init__(self, width, height, title):
# Call the parent class's init function
super().__init__(width, height, title)
# Make the mouse disappear when it is over the window.
# So we just see our object, not the pointer.
self.set_mouse_visible(False)
arcade.set_background_color(arcade.color.ASH_GREY)
# Create our ball
self.ball = Ball(50, 50, 0, 0, 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):
self.ball.update()
def on_key_press(self, key, modifiers):
""" Called whenever the user presses a key. """
if key == arcade.key.LEFT:
self.ball.change_x = -MOVEMENT_SPEED
elif key == arcade.key.RIGHT:
self.ball.change_x = MOVEMENT_SPEED
elif key == arcade.key.UP:
self.ball.change_y = MOVEMENT_SPEED
elif key == arcade.key.DOWN:
self.ball.change_y = -MOVEMENT_SPEED
def on_key_release(self, key, modifiers):
""" Called whenever a user releases a key. """
if key == arcade.key.LEFT or key == arcade.key.RIGHT:
self.ball.change_x = 0
elif key == arcade.key.UP or key == arcade.key.DOWN:
self.ball.change_y = 0
def main():
window = MyGame(640, 480, "Drawing Example")
arcade.run()
main()
|
19.3.1. Verhindern, dass der Bildschirm verlassen wird¶
Leider gibt es im vorherigen Programm nichts, was die Spieler davon abhält, sich über den Bildschirmrand hinaus zu bewegen. Wenn wir den Spieler daran hindern möchten, sich den Bildschirmbereich zu verlassen, benötigen wir zusätzlichen Code.
Wir erkennen den Rand, indem wir position_x
mit der linken und rechten Seite des Bildschirms vergleichen. Zum Beispiel:
if self.position_x < 0:
Das ist aber nicht perfekt. Da die Position die Mitte des Balls angibt, haben wir zu dem Zeitpunkt, an dem die x-Koordinate 0 ist, den Bildschirm bereits verlassen. Es ist besser, die Position mit dem Radius des Balls zu vergleichen:
if self.position_x < self.radius:
Was machen wir, wenn er am Rande angekommen ist? Setze den Wert einfach auf den Rand zurück:
# See if the ball hit the edge of the screen. If so, change direction
if self.position_x < self.radius:
self.position_x = self.radius
Hier ist ein vollständiges Beispiel:
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 | import arcade
SCREEN_WIDTH = 640
SCREEN_HEIGHT = 480
MOVEMENT_SPEED = 3
class Ball:
def __init__(self, position_x, position_y, change_x, change_y, radius, color):
# 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):
# 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.position_x = self.radius
if self.position_x > SCREEN_WIDTH - self.radius:
self.position_x = SCREEN_WIDTH - self.radius
if self.position_y < self.radius:
self.position_y = self.radius
if self.position_y > SCREEN_HEIGHT - self.radius:
self.position_y = SCREEN_HEIGHT - self.radius
class MyGame(arcade.Window):
def __init__(self, width, height, title):
# Call the parent class's init function
super().__init__(width, height, title)
# Make the mouse disappear when it is over the window.
# So we just see our object, not the pointer.
self.set_mouse_visible(False)
arcade.set_background_color(arcade.color.ASH_GREY)
# Create our ball
self.ball = Ball(50, 50, 0, 0, 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):
self.ball.update()
def on_key_press(self, key, modifiers):
""" Called whenever the user presses a key. """
if key == arcade.key.LEFT:
self.ball.change_x = -MOVEMENT_SPEED
elif key == arcade.key.RIGHT:
self.ball.change_x = MOVEMENT_SPEED
elif key == arcade.key.UP:
self.ball.change_y = MOVEMENT_SPEED
elif key == arcade.key.DOWN:
self.ball.change_y = -MOVEMENT_SPEED
def on_key_release(self, key, modifiers):
""" Called whenever a user releases a key. """
if key == arcade.key.LEFT or key == arcade.key.RIGHT:
self.ball.change_x = 0
elif key == arcade.key.UP or key == arcade.key.DOWN:
self.ball.change_y = 0
def main():
window = MyGame(640, 480, "Drawing Example")
arcade.run()
main()
|
19.4. Bewegen mit dem Game-Controller¶
Die Arbeit mit Game-Controllern ist etwas komplexer. Am Rechner könnte kein Game-Controller oder auch fünf Controller angeschlossen sein.
Wir können eine Liste aller mit der Funktion get_joysticks
eingesteckten Gamepads erhalten. Dies gibt entweder eine Liste mit den Gamepads oder eine leere Liste zurück, wenn keine Gamepads vorhanden sind.
Nachfolgend finden Sie einen Codeblock, der in eine __init__
-Methode für Ihre Anwendung eingefügt werden kann, mit der eine Objektvariable erstellt wird, die ein Gamepad darstellt, sofern es vorhanden ist.
joysticks = arcade.get_joysticks()
if joysticks:
self.joystick = joysticks[0]
self.joystick.open()
else:
print("There are no joysticks.")
self.joystick = None
19.5. Joystick-Werte¶
Danach können Sie die Position des Game-Controller-Joysticks ermitteln, indem Sie self.joystick.x
und self.joystick.y
aufrufen.
Probiere dies, kombiniert mit dem Initialisierungscode von oben, aus:
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 | import arcade
SCREEN_WIDTH = 640
SCREEN_HEIGHT = 480
MOVEMENT_SPEED = 5
DEAD_ZONE = 0.02
class Ball:
def __init__(self, position_x, position_y, change_x, change_y, radius, color):
# 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):
# 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.position_x = self.radius
if self.position_x > SCREEN_WIDTH - self.radius:
self.position_x = SCREEN_WIDTH - self.radius
if self.position_y < self.radius:
self.position_y = self.radius
if self.position_y > SCREEN_HEIGHT - self.radius:
self.position_y = SCREEN_HEIGHT - self.radius
class MyGame(arcade.Window):
def __init__(self, width, height, title):
# Call the parent class's init function
super().__init__(width, height, title)
# Make the mouse disappear when it is over the window.
# So we just see our object, not the pointer.
self.set_mouse_visible(False)
arcade.set_background_color(arcade.color.ASH_GREY)
# Create our ball
self.ball = Ball(50, 50, 0, 0, 15, arcade.color.AUBURN)
# Get a list of all the game controllers that are plugged in
joysticks = arcade.get_joysticks()
# If we have a game controller plugged in, grab it and
# make an instance variable out of it.
if joysticks:
self.joystick = joysticks[0]
self.joystick.open()
else:
print("There are no joysticks.")
self.joystick = None
def on_draw(self):
""" Called whenever we need to draw the window. """
arcade.start_render()
self.ball.draw()
def update(self, delta_time):
# Update the position according to the game controller
if self.joystick:
print(self.joystick.x, self.joystick.y)
def main():
window = MyGame(640, 480, "Drawing Example")
arcade.run()
main()
|
Führe das Programm aus und sieh dir die Werte an, die es für Ihren Game-Controller ausgibt, wenn du den Joystick bewegen.
Die Werte liegen zwischen -1 und +1, wobei 0 ein zentrierter Joystick ist.
Die Zahlen auf der x-Achse sind negativ, wenn der Joystick nach links geht, positiv nach rechts.
Die Zahlen für y-Achse sind eventuell entgegengesetzt zu dem was du erwartest. Negativ für oben, positiv für unten.

Zentriert (0, 0)¶

Unten (0, 1)¶

Runter / links (-1, 1)¶

Unten / Rechts (1, 1)¶

Auf (0, -1)¶

Oben / Links (-1, -1)¶

Oben / Rechts (1, -1)¶
Wir können den Ball bewegen, indem wir den folgenden Code zu update
hinzufügen:
def update(self, delta_time):
# Update the position according to the game controller
if self.joystick:
print(self.joystick.x, self.joystick.y)
self.ball.change_x = self.joystick.x
self.ball.change_y = -self.joystick.y
Beachte das -
, das wir vor das Setzen des y-Werts schreiben. Wenn wir dies nicht tun, bewegt sich der Ball entgegengesetzt zu dem, was wir beim Auf- und Absteigen erwarten. Dies liegt daran, dass der Joystick y-Werte hat, die entgegengesetzt zu dem sind, was wir normalerweise erwarten würden. Das hat eine lange Geschichte, mit der ich Euch jetzt nicht langweilen werde.
Aber mit diesem Code bewegt sich unser Ball so langsam. Wie beschleunigen wir das? Wir können es fünfmal schneller machen, indem wir es mit fünf multiplizieren, wenn wir wollen.
def update(self, delta_time):
# Update the position according to the game controller
if self.joystick:
print(self.joystick.x, self.joystick.y)
self.ball.change_x = self.joystick.x * 5
self.ball.change_y = -self.joystick.y * 5
Oder noch besser, definiere eine konstante Variable am Anfang des Programms und verwende diese. In unserem letzten Beispiel unten machen wir genau das.
19.6. Toter Bereich¶
Was ist, wenn der Ball „driftet“, wenn der Joystick zentriert in der Mitte steht?
Joysticks funktionieren mechanisch. Ein zentrierter Joystick kann einen Wert haben, der nicht bei 0, sondern bei 0,0001 oder einer anderen kleinen Zahl liegt. Dies führt zu einem kleinen „Driften“ der Spielfigur. Wir wirken dem oft mit einer „toten Bereich“ entgegen, indem wir, wenn die Zahl unter einem bestimmten Wert liegt, einfach davon ausgehen, dass sie Null ist, um die Abweichung zu beseitigen.
In den hervorgehobenen Linien sehen Sie, wie wir uns um den toten Bereich kümmern:
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 | import arcade
SCREEN_WIDTH = 640
SCREEN_HEIGHT = 480
MOVEMENT_SPEED = 5
DEAD_ZONE = 0.02
class Ball:
def __init__(self, position_x, position_y, change_x, change_y, radius, color):
# 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):
# 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.position_x = self.radius
if self.position_x > SCREEN_WIDTH - self.radius:
self.position_x = SCREEN_WIDTH - self.radius
if self.position_y < self.radius:
self.position_y = self.radius
if self.position_y > SCREEN_HEIGHT - self.radius:
self.position_y = SCREEN_HEIGHT - self.radius
class MyGame(arcade.Window):
def __init__(self, width, height, title):
# Call the parent class's init function
super().__init__(width, height, title)
# Make the mouse disappear when it is over the window.
# So we just see our object, not the pointer.
self.set_mouse_visible(False)
arcade.set_background_color(arcade.color.ASH_GREY)
# Create our ball
self.ball = Ball(50, 50, 0, 0, 15, arcade.color.AUBURN)
# Get a list of all the game controllers that are plugged in
joysticks = arcade.get_joysticks()
# If we have a game controller plugged in, grab it and
# make an instance variable out of it.
if joysticks:
self.joystick = joysticks[0]
self.joystick.open()
else:
print("There are no joysticks.")
self.joystick = None
def on_draw(self):
""" Called whenever we need to draw the window. """
arcade.start_render()
self.ball.draw()
def update(self, delta_time):
# Update the position according to the game controller
if self.joystick:
# Set a "dead zone" to prevent drive from a centered joystick
if abs(self.joystick.x) < DEAD_ZONE:
self.ball.change_x = 0
else:
self.ball.change_x = self.joystick.x * MOVEMENT_SPEED
# Set a "dead zone" to prevent drive from a centered joystick
if abs(self.joystick.y) < DEAD_ZONE:
self.ball.change_y = 0
else:
self.ball.change_y = -self.joystick.y * MOVEMENT_SPEED
self.ball.update()
def main():
window = MyGame(640, 480, "Drawing Example")
arcade.run()
main()
|