Drag and Drop - Überschneidung von Objekten prüfen

Bei drag and drop wird ein Objekt angeklickt, mit gedrückter Maustaste verschoben und beim Loslassen der Maustaste über einem Zielobjekt oder Zielbereich platziert. Häufig "rastet" das Objekt beim Verschieben oder Loslassen der Maustaste an einer definierten Stelle des Zielobjekts ein ("magnetischer" Effekt).

Zum Ausprobieren: Klicken Sie in die dunkelblaue Fläche und erzeugen einen gelben Stern. Anschließend ziehen Sie den Stern bei gedrückter Maustaste über den hellblauen Kreis. Beim Loslassen der Maustaste wird der Stern exakt im Zentrum des blauen Kreis platziert.

 

 

Beim Loslassen der Maustaste wird immer geprüft, ob das verschobene Objekt das Zielobjekt schneidet bzw. ganz drinnen liegt.
Ganz allgemein gesprochen, geht es darum festzustellen, ob sich zwei Objekte berühren = collision detection

Daraus ergibt sich die allgemeine Skriptstruktur des MOUSE_UP-Eventhandlers:
  

function stopZiehen(e:MouseEvent):void {
e.target.stopDrag();
if (PRÜFEN, OB SICH GEZOGENES OBJEKT UND ZIELOBJEKT BERÜHREN) {
...
hier kommt der Code, der beschreibt was zu tun ist,
wenn sich die Objekte berühren.
Z.B. das Objekt "rastet" an einer bestimmten Stelle ein.
...
}
}


Es gibt nun drei einfache Möglichkeiten zu prüfen, ob sich zwei Objekte berühren bzw. ein Objekt vollständig innerhalb eines anderen liegt:

  1. Die DisplayObject-Methode hitTestObject
    prüft, ob sich die Rechteckshüllen von zwei Anzeigeobjekten schneiden.
  2. Die DisplayObject-Mehtode hitTestPoint
    prüft, ob ein Punkt innerhalb der Objektkonturen bzw. der Rechteckshülle eines Anzeigeobjekts liegt.
  3. Die DisplayObject-Methoden getRect bzw.getBounds in Verbindung mit der Rectangle-Methode containsRect.
    ermittelt zuerst die Rechteckshüllen beider Objekte und prüft anschließend, ob ein Rechteck vollständig innerhalb des anderen liegt.

Das Video zeigt die unterschiedlichen Resultate der drei Möglichkeiten, die anschließend genau erklärt werden.
 

Der hellblaue Kreis als Zielobjekt ist eine Movieclipinstanz mit dem Namen ziel.
Wir gehen von folgendem Programmcode aus, den wir vom letzten Kapitel übernehmen und nur durch die markierte if-Anweisung erweitern.
 

// Stern durch Anklicken der Bühne erzeugen
this.stage.addEventListener(MouseEvent.MOUSE_UP,erzeugeStern);
function erzeugeStern(e:MouseEvent):void {
if (e.target == e.currentTarget) {
var stern:MovieClip = new GelberStern();
this.addChild(stern);
stern.x = e.stageX;
stern.y = e.stageY;
}
}
// Stern bei gedrückter Maustaste in den hellblauen Kreis ziehen
this.addEventListener(MouseEvent.MOUSE_DOWN, startZiehen);
function startZiehen(e:MouseEvent):void {
e.target.startDrag(false);
}
this.addEventListener(MouseEvent.MOUSE_UP, stopZiehen);
function stopZiehen(e:MouseEvent):void {
e.target.stopDrag();
if (PRÜFEN, OB SICH GEZOGENES OBJEKT UND ZIELOBJEKT BERÜHREN) {
e.target.x = ziel.x;
e.target.y = ziel.y;
}

}

Wenn sich das gezogene Objekt und das Zielobjekt berühren, wird der Ursprung des gezogenen Objekts auf den Ursprung des Zielobjekts ausgerichtet.
 

e.target.x = ziel.x;
e.target.y = ziel.y;

Es werden  nun drei Möglchkeiten für collision detection gezeigt.
 

1. Möglichkeit mit hitTestObject
 

object1.hitTestObject(object2)

Ausgehend von einem Anzeigeobjekt object1 prüft die Methode hitTestObject, ob sich die kleinsten Rechteckshüllen von object1 und object2 überlappen.

Die Methode kann auch den Wert true liefern, wenn sich die eigentlichen Objektkonturen nicht schneiden.

In unserem Beispiel lautet daher die Abfrage in der if-Anweisung:
 

e.target.hitTestObject(ziel)

 

2. Möglichkeit mit hitTestPoint
 

object.hitTestPoint(x,y,true/false)

Ausgehend von einem Anzeigeobjekt object prüft die Methode hitTestPoint, ob der Punkt (x,y) innerhalb der Kontur bzw. der kleinsten Rechteckshülle des Anzeigeobjekts liegt.

Ist der dritte Parameter true gilt die tatsächliche Objektkontur als Bezug, sonst die kleinste Rechteckshülle.

Im obigen Fall liefert hitTestPoint bezogen auf den weißen Punkt den Wert true, wenn der dritte Parameter auf false gesetzt ist. Denn der weiße Punkt liegt nicht innerhalb der Objektkontur des Sterns, aber innerhalb der Rechteckshülle. 

Wenn die tatsächliche Sternkontur als Bezug gelten soll, lautet daher in unserem Beispiel die Abfrage in der if-Anweisung:
 

e.target.hitTestPoint(ziel.x,ziel.y,true)

 

3. Möglichkeit mit getRect bzw. getBounds und containsRect
 

object1.getRect(object2)
object1.getBounds(object2) 

Ausgehend von einem Anzeigeobjekt object1 liefert die Methode getRect (bzw. getBounds) eine Instanz der Klasse Rectangle, die das kleinste umhüllende Rechteck des Anzeigeobjekts beschreibt. Ein Objekt vom Typ Rectangle ist definiert durch die Koordinaten des linken oberen Eckpunkts und durch die Breite und Höhe des Rechtecks. Um die Koordinaten des linken oberen Eckpunkts angeben zu können, benötigt man ein Bezugssystem. Deshalb erhalten getRect und getBounds als Parameter ein Anzeigeobjekt object2, dessen Koordinatensystem das Bezugssystem für die Positionsangabe der Rechteckshülle von object1 ist.

Der Unterschied zwischen getRect und getBounds besteht darin, dass sich getRect auf die Konstruktionslinien des Objekts und getBounds auf die tatsächlichen Objektkonturen bezieht. Der Unterschied ist bei breiten Konturlinien am besten zu erkennen.

In unserem Beispiel ist das Bezugssystem die Bühne.
Die kleinsten umhüllenden Rechtecke vom hellblauen Kreis und vom Stern erhält man somit durch:
 

ziel.getRect(this.stage)        // hellblauer Kreis
e.target.getRect(this.stage) // Stern

 
In der Klasse Rectangle gibt es eine Methode containsRect mit der man überprüfen kann, ob ein Rechteck rectangle2 vollständig in einem anderen Rechteck rectangle1 enthalten ist. 
 

rectangle1.containsRect(rectangle2)

 

Wenn in unserem Beispiel der Stern vollständig in der Rechteckshülle des hellblauen Kreises liegen soll, lautet die Abfrage in der if-Anweisung:
 

ziel.getRect(this.stage).containsRect(e.target.getRect(this.stage))

 

Quellcode in der beigefügten zip-Datei:
dragdrop.fla
dragdrop1.fla
dragdrop2.fla

Ergänzende und vertiefende Module