TigerJython xx
für Gymnasien

Maus-Events I


Mit Maus-Events können mausgesteuerte Aktionen ausgelöst werden, die in interaktiven Spielen von grosser Bedeutung sind. In sogenannten Callbackfunktionen (oder kurz Callbacks) definiert man, was geschehen soll, wenn eine Maustaste gedrückt, losgelassen oder die Maus verschoben wird. Die Callbacks werden nicht durch das eigene Programm aufgerufen, sondern automatisch vom System, wenn die Maustaste betätigt wird. In folgenden Beispielen beschreiben wir zwei Möglichkeiten, wie man Calbacks verwendet.

Die Callbacks werden bei der Erzeugung des Spielfensters mit benannten Parametern registriert
(Beispiele 1, 2 und 3)
Man verwendet einen GGMouseListener und registriert ihn mit den notwendigen Callbacks (Beispiel 4)

Im Abschnitt Maus-Events II wird zusätzlich der GGMouseTouchListener verwendet, der nicht nur die Koordinaten des Mausklicks, sondern auch das Spritebild des Actor, der sich an der Position des Mausklicks befindet, erfassen kann.

Beispiel 1: Beim Drücken der Maustaste wird ein neuer Fisch erzeugt. Dies ist ein schönes Beispiel für die objektorientierte Programmierung, denn jeder neue Fisch ist eine Instanz der Klasse Fish und "weiss" darum aus der Klassendefinition, wie er sich bewegen soll.

Die Callbackfunktion pressCallback() wird als globale Funktion definiert. Beim Drücken der Maustaste werden mit e.getX() und e.getY() die Koordinaten des Mausklicks erfasst und an dieser Position ein Objekt der Klasse Fish erzeugt.

Die Registrierung des Callbacks erfolgt als Parameter bei der Erzeugung des Spielfensters (wie bei der Turtlegrafik).

 

# Gg8.py

from gamegrid import *


# ---------------- class Fish ----------------
class Fish(Actor):
    def __init__(self):
        Actor.__init__(self, "sprites/nemo.gif");
     
    def act(self):
        self.move()
        if not self.isMoveValid():
            self.turn(180)
            self.setHorzMirror(not self.isHorzMirror())
            
# ---------------- main ----------------------
def pressCallback(e):
    location = toLocationInGrid(e.getX(), e.getY())
    addActor(Fish(), location)

makeGameGrid(10, 10, 60, Color.red, "sprites/reef.gif", False, 
     mousePressed = pressCallback)
show()
doRun()
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

 

Erklärungen zum Programmcode:

def pressCallback(e): definiert die Callbackfunktion, die aufgerufen wird, wenn die Maustaste gedrückt wird. e steht für Event
toLocationInGrid(e.getX(), e.getY()) erfasst und wandelt sie in Zellenkoordinaten (Locations) um
makeGameGrid( ..., mousePressed = pressCallback) registriert den Callback

 

Beispiel 2: Eine Spielfigur kann mit der gedrückten Maustaste an eine beliebige Position im Gitter verschoben werden.

Wir verwenden zwei Callbacks pressCallback() und dragCallback().
Mit der Funktion pressCallback() wird die Position des Mausklicks erfasst und der Actor, der sich in dieser Zelle befindet, in der Variablen actor gespeichert.
Mit dragCallback() wird der Actor bei gedrückter Maustaste bewegt.

Da die Variable actor in beiden Funktionen verwendet wird, muss sie als global deklariert werden.

Die beiden Funktionen werden mit benannten Parametern beim Erzeugen des Spielfensters registriert.

 

 
# Gg8a.py

from gamegrid import *

# ---------------- class Ghost ----------------
class Ghost(Actor):
    def __init__(self):
        Actor.__init__(self, "sprites/ghost.png")
            
# ---------------- main ----------------------
def pressCallback(e):
    location = toLocationInGrid(e.getX(), e.getY())
    global actor 
    actor = getOneActorAt(location)
    
def dragCallback(e):
    if actor == None:
        return
    location = toLocationInGrid(e.getX(), e.getY())
    actor.setLocation(location)
  

makeGameGrid(10, 10, 60, Color.red, False, 
     mousePressed = pressCallback,
     mouseDragged = dragCallback)
     
ghost1 = Ghost()
ghost2 = Ghost()

addActor(ghost1, Location(5, 5))
addActor(ghost2, Location(2, 3))

show()
doRun()
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

 

Erklärungen zum Programmcode:

mousePressed = pressCallback, mouseDragged = dragCallback : Bei der Erzeugung des Spielfensters können mehrere Callbackfunktionen registriert werden
global actor: Damit die Variable actor in der Methode pressCallback() zugewiesen werden kann, muss sie als global deklariert werden
if actor == None: Wird in eine leere Zelle geklickt, so liefert getOneActorAt() den Wert None. Beim nachfolgenden Ziehen wird der dragCallback mit return verlassen

 

 

Beispiel 3: Im Beispiel 2 bewegt sich die Spielfigur beim Ziehen mit gedrückter Maustaste sprungartig von Zelle zur Zelle. Manchmal ist es eleganter, die Objekte kontinuierlich zu ziehen und beim Loslassen der Maustaste exakt in einer Gitterzelle zu positionieren. Vier Kugeln werden zu Beginn zufällig im Spielfenster. Mit der Maus sollen sie in die mittleren 4 Felder verschoben werden.

Wir verwenden drei Callbacks pressCallback() , dragCallback() und releaseCallback(). Im presCallback() wird die Position des Mausklicks und die Kugel, die sich auf dieser Position befindet, erfasst. Die Methode setLocationOffset() im dragCallback() bewirkt die kontinuierliche Bewegung während des Ziehens mit gedrückter Maustaste. Im releaseCallback() wird die Kugel beim Loslassen der Maustaste wieder genau im Gitter positioniert.

 

 
# Gg8b.py

from gamegrid import *

def pressCallback(e):
    global actor, startLoc
    startLoc = toLocationInGrid(e.getX(), e.getY())
    actor = getOneActorAt(startLoc)
 
def dragCallback(e):
    if actor == None:
        return
    startPoint = toPoint(startLoc)
    actor.setLocationOffset(e.getX() - startPoint.x, e.getY() - startPoint.y) 

def releaseCallback(e):
    if actor == None:
        return
    destLoc = toLocationInGrid(e.getX(), e.getY())
    actor.setLocationOffset(0, 0)
    actor.setLocation(destLoc)    

actor = None
startLoc = None

makeGameGrid(8, 8, 70, Color.red, False,  mousePressed = pressCallback, 
             mouseDragged = dragCallback, mouseReleased = releaseCallback)
setTitle("Sort Balls")
for i in range(4):
    ball = Actor("sprites/marble.png")
    addActor(ball, getRandomEmptyLocation())
show()
setSimulationPeriod(20)
doRun()
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

 

Erklärungen zum Programmcode:

global actor, startLoc: Damit die Variablen in mehreren Callbacks verwendbar sind, müssen sie als global deklariert sein
setLocationOffset(e.getX() - startPoint.x, e.getY() - startPoint.y): bewirkt Bewegung ausserhalb der Gittezellen
setLocationOffset(0, 0): beim Loslassen der Maustaste wird die Kugel in der Mitte der Zelle positioniert

 

Beispiel 4: Ein Auto kann mit der gedrückten Maustaste bewegt werden. Dabei dreht sich das Auto immer in die Bewegungsrichtung. Da die Zellengrösse 1 Pixel ist, bewegt sich das auto kontinuierlich.

Wir verwenden den MouseListener aus der Klasse GGMouseListener. Da das Auto bewegt wird, ist es naheliegend, den Mauslistener der Klasse Car zuzuordnen. Da Python mehrfache Vererbung erlaubt, kann Car von der Klasse Actor und auch von der Klasse GGMouseListener abgeleitet werden. Die Callbackmethode mouseEvent() wird in der Klasse Car definiert.

Damit sich das Auto immer in die Bewegungsrichtung dreht, wird laufend die Bewegungsrichtung aus der Differenz der alten und neuen Koordinaten berechnet. Dabei wartet man immer, bis der Abstand zwischen dem alten und neuen Punkt grösser als 5 Pixel ist.

 

# Gg8c.py

from gamegrid import *
import math

# --------------------- class car --------------------------
class Car(Actor, GGMouseListener):
    def __init__(self):
        Actor.__init__(self, True, "sprites/redcar.gif") 
        self.oldLocation = Location()
    
    def mouseEvent(self, e):
        location = toLocationInGrid(e.getX(), e.getY())
        self.setLocation(location)
        dx = location.x - self.oldLocation.x;
        dy = location.y - self.oldLocation.y;
        if dx * dx + dy * dy < 25:
            return True
        phi = math.atan2(dy, dx)
        self.setDirection(math.degrees(phi))
        self.oldLocation = location
 
# --------------------- main ---------------------------------
makeGameGrid(600, 600, 1, False)
setTitle("Move car using mouse drag")
setSimulationPeriod(50)
setBgColor(Color.gray)
car = Car()
addActor(car, Location(50, 50))
addMouseListener(car, GGMouse.lDrag)
show()
doRun()
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

 

Erklärungen zum Programmcode:

class Car(Actor, GGMouseListener): Die Klasse Car wird von Actor und GGMouseListener abgeleitet
addMouseListener(car, GGMouse.lDrag): Der GGMouse Listener wird mit der Maske lDrag registriert, damit nur der Drag-Event aktiv wird
Die alte Position wird in der Instanzvariablen oldLocation gespeichert, die im Konstruktor erzeugt wird

 


Aufgaben:

1)

Im Spielfenster mit einem blauen Hintergrund werden 20 Ballons an zufällig gewählten Positionen erzeugt (balloon.gif). Mit einem Mausklick können sie vernichtet werden.
 


2)

Wie in der ersten Aufgabe werden 20 Ballons an zufällig gewählten Positionen erzeugt. Definiere eine Callbackfunktion pressCallback(e), mit der du einen Ballon mit der Maus wählen kannst und eine Callbackfunktion dragCallback(e), mit der du den gewählten Ballon verschieben kannst. Verschiebe dann alle Ballons so, dass sie in den obersten zwei Zeilen angeordnet sind.
 


3)


Erzeuge ein Spielfenster mit 20 horizontalen und 20 vertikalen Zelle der Grösse 30, mit dem Hintergrundbild "sprites/town.jpg". Per Mausklick werden an der Mausposition Ballons erzeugt. Diese sollen sich nach unten bewegen.

(Du definierst in der Klasse Balloon eine Funktion act(self), in der die Bewegungsrichtung mit self.setDirection(90) festgelegt wird.