Theoretisch kollidieren zwei Figuren dann, wenn sich mindestens zwei Pixel der beiden Figuren überlagern. Da Computerfiguren sich immer in einem rechteckigen Bereich befinden, müsste man alle nicht-transparenten Pixel der beiden Bilder auf Überlagerung prüfen. Selbst für kleine Bilder ist der Aufwand dafür enorm. Enthalten die Bilder beispielsweise 50 x 50 Pixel wären dazu 2500 x 2500 Vergleiche notwendig. Um dieses Problem zu überwinden, legt man einfache geometrische Formen als Kollisionsbereiche fest. In JGameGrid sind dies Rechtecke, Kreise, Linien oder Punkte, die bezüglich der Figur beliebige Position und Orientierung haben können. Standardmässig ist der Kollisionsbereich das umhüllende Rechteck (bounding rectangle).
Beispiel 1: Die beiden rechteckigen Figuren bewegen sich auf einer kreisförmigen Bahn und reflektieren dabei am Fensterrand. Wenn sie zusammenstossen, ertönt ein Ton und die beiden Stäbe ändern ihre Bewegungsrichtung um 180°. Kollisionen werden als Events erfasst. Dabei wird die Methode collide() aufgerufen. Mit stick1.addCollisionActor(stick2) wird dem Stick1 mitgeteilt, dass er auf Kollisionen mit stick2 reagieren soll. In diesem Beispiel kann man beobachten, wie exakt der Kollision-Algorithmus von JGameGrid funktioniert. Selbst wenn sich die beiden Figuren mit Eckpunkten berühren, wird ein Kollisionsevent ausgelöst.
|
|
# Gg9.py from gamegrid import * from soundsystem import * # --------------------- class Stick ---------------------------------- class Stick(Actor): def __init__(self): Actor.__init__(self, True, "sprites/stick.gif", 2) def act(self): step = 1 loc = self.getLocation() dir = (self.getDirection() + step) % 360; if loc.x < 50: dir = 180 - dir self.setLocation(Location(55, loc.y)) if loc.x > 450: dir = 180 - dir self.setLocation(Location(445, loc.y)) if loc.y < 50: dir = 360 - dir self.setLocation(Location(loc.x, 55)) if loc.y > 450: dir = 360 - dir self.setLocation(Location(loc.x, 445)) self.setDirection(dir) self.move() def collide(self, actor1, actor2): actor1.setDirection(actor1.getDirection() + 180) actor2.setDirection(actor2.getDirection() + 180) play() return 10 # --------------------- main ----------------------------------------- makeGameGrid(500, 500, 1, False) setSimulationPeriod(10) stick1 = Stick() addActor(stick1, Location(200, 200), 30) stick2 = Stick() addActor(stick2, Location(400, 400), 30) stick2.show(1) stick1.addCollisionActor(stick2) show() doRun() openSoundPlayer("wav/ping.wav") |
Erklärungen zum Programmcode:
|
Beispiel 2: Fünf farbige Kugeln bewegen sich frei im Spielfeld und reflektieren am Rand. Sobald eine Kugel eine andere berührt, wird eine Kollision ausgelöst. Dabei prallt jede Kugel unter dem gleichen Winkel zurück, wie sie angerollt ist, und es wird ein Ton abgespielt. |
|
# Gg9a.py from gamegrid import * from soundsystem import * import random # --------------------- class Ball ---------------------------------- class Ball(Actor): def __init__(self): Actor.__init__(self, True, "sprites/peg.png", 5) def act(self): step = 1 loc = self.getLocation() dir = (self.getDirection() + step) % 360; if loc.x < 20: dir = 180 - dir self.setLocation(Location(20, loc.y)) if loc.x > 480: dir = 180 - dir self.setLocation(Location(478, loc.y)) if loc.y < 20: dir = 360 - dir self.setLocation(Location(loc.x, 22)) if loc.y > 480: dir = 360 - dir self.setLocation(Location(loc.x, 478)) self.setDirection(dir) self.move() # --------------------- class MyActorCollisionListener --------------- class MyActorCollisionListener(GGActorCollisionListener): def collide(self, actor1, actor2): actor1.setDirection(actor1.getDirection() + 180) actor2.setDirection(actor2.getDirection() + 180) play() return 10 # --------------------- main ----------------------------------------- makeGameGrid(500, 500, 1, False) setSimulationPeriod(10) myActorCollisionListener = MyActorCollisionListener() balls = [] for i in range(5): ball = Ball() ball.show(i) addActor(ball, Location(100 + 50 * i, 100 + 50 * i), random.randint(0, 360)) ball.addActorCollisionListener(myActorCollisionListener) balls.append(ball) for i in range(5): for k in range(i + 1, 5): balls[i].addCollisionActor(balls[k]) openSoundPlayer("wav/ping.wav") show() doRun() |
Erklärungen zum Programmcode:
ball.addActorCollisionListener(): Zu jeder Kugel wird ein GGActorCollisionListener registriert | |
balls[i].addCollisionActor(balls[k]): Bei jeder Kugel werden Kollisionen mit allen übrigen Kugeln aktiviert |
Beispiel 3: Ein mausgesteuerter Pfeil kann mit seiner vorderen Spitze (CollisionSpot) Ballone zerplatzen. 10 Ballone werden an zufällig gewählten Positionen erzeugt. Beim Pfeil wird der Endpunkt der Nadel mit dart.setCollisionSpot(new Point(30, 0)) als Collisionsspot festgelegt. Zur Bestimmung des Collisionsspots wird ein Koordinatensystem mit Ursprung (0, 0) im Mittelpunkt des Spritebildes verwendet. Das Spritebild ist 60 x 17 pixel gross, d.h. (30, 0) sind die Koordinaten des Endpunktes der Nadel. Der Pfeil dreht sich in die Richtung der Mausbewegung. Dies wird in der Klasse Dart mit folgendem Verfahren erreicht: Man folgt dem Mauscursor und erfasst fortlaufend die Cursorposition. Jedesmal, wenn die alte und die neue Position 5 Pixel voneinander entfernt sind, bestimmt man die Verbindungsgerade der beiden Punkte. Ihre Richtung, die man mit actan2() bestimmt, betrachtet man als die Bewegungsrichtung der Maus. |
# Gg9b.py from gamegrid import * from soundsystem import * import random import math # --------------------- class Dart -------------------------- class Dart(Actor, GGMouseListener): def __init__(self): Actor.__init__(self, True, "sprites/dart.gif") self.oldLocation = Location() def mouseEvent(self, mouse): location = toLocationInGrid(mouse.getX(), mouse.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.x = location.x self.oldLocation.y = location.y return True def collide(self, actor1, actor2): play() actor2.removeSelf() return 0 # --------------------- main ----------------------------------------- makeGameGrid(600, 600, 1, None, "sprites/town.jpg", False) setTitle("Move dart with mouse to pick the balloon") setSimulationPeriod(50) dart = Dart() addActor(dart, Location(100, 300)) addMouseListener(dart, GGMouse.lDrag) dart.setCollisionSpot(Point(30, 0)) # End point of dart needle for i in range(15): balloon = Actor("_sprites/balloon.gif") addActor(balloon, Location(int(500 * random.random() + 50 ), int(500 * random.random() + 50))) dart.addCollisionActor(balloon) show() doRun() openSoundPlayer("wav/explode.wav") |
Erklärungen zum Programmcode:
dart.setCollisionSpot(new Point(30, 0)): Setzt den CollisionSpot auf die Koordinaten der Nadelspitze, relativ zum Mittelpunkt des Sprite-Bildes | |
dart.addCollisionActor(balloon)) Jeder neu erzeugte Ballon ist ein Collisonspartner des Pfeils | |
actor2.removeSelf(): Lässt den mit der Pfeilspitze berührten Ballon verschwinden | |
return 0: Man gibt in der Methode collide() die Anzahl der Simulationszyklen zurück, bis die Kollisionsdetektion wieder aktiv wird. In diesem Fall kann sie sofort wieder aufgerufen werden |