Construct2 Drag&Drop

Aus Debacher-Wiki
Zur Navigation springenZur Suche springen

Für viele Spielideen braucht man die Möglichkeit Objekte mit der Maus zu bewegen und auch sie an bestimmten Stellen "einschnappen" zu lassen. Wie das geht beschreibt der folgende Text.

Hinweis: Im ersten Besipiel wird einmal Drag&Drop per Hand realisiert. Das ist für eigene Anwendungen nicht notwendig, ab dem zweiten Beispiel in diesem Text wird stattdessen das DragDrop Behavior genutzt.

Einfaches Drag&Drop

spielbare Version

.capx-Datei

Als Grafikobjekte dienen die beiden folgenden Kreise.

64x64-gkreis.png 64x64-rkreis.png

Wir erstellen zuerst ein neues leeres Projekt, dem wir den Namen Drag&Drop geben. Die restlichen Voreinstellungen belassen wir, vor allem die Größeneinstellungen.

Projekt->Window Size: 640,480
Layout1->Layout Size: 1280,1024
Layout1->Margins: 500,500

Dann laden wir die beiden Kreise in das Layout.

Doppelklick auf einen leeren Bereich im Layout (Insert new object) -> Sprite, dann an beliebiger Stelle ins Layout klicken und im sich öffnenden Image Editor auf Load an image from a file und zuerst den grünen Kreis laden und anschließend die gleichen Schritte für den roten Kreis wiederholen. Der grüne Kreis wird ganz in die linke untere Ecke geschoben (Position 32,448) der rote Kreis an eine beliebige andere Position. Anschließend benennen wir die Sprites entsprechend grünerKreis und roterKreis.

Anmerkung: Umlaute in Bezeichnern sind problemlos möglich, mir ist aber noch nicht klar, ob das auch beim Export in beliebige Formate so bleibt.

Drag&drop1.png

Der grüne Kreis bekommt noch eine Instanzen-Variable wirdGezogen (Add/Edit instance Variables -> auf das + Klicken -> wirdGezogen, Boolean, false). Dann fügen wir noch das Mouse-Objekt ein und das Layout ist fertig.

Das Event-Handling

Weiter geht es im Event-Sheet.

Zuerst brauchen wir noch eine globale Variable aktuellZiehend (im EventSheet -> rechte Maustaste -> Add globale variable -> aktuellZiehend, number, 0) in der wir uns merken, ob gerade ein Objekt gezogen wird.

Nun müssen nacheinander unterschiedliche Ereignisse behandelt werden. Um die Übersichtlichkeit zu erhöhen, vor allem wenn die Zahl der Objekte steigt, werden jeweils Gruppen angelegt. Gruppen dienen nur dazu das Event-Sheet zu gliedern und helfen bei der Übersicht.

Zuerst wollen wir erreichen, dass der rote Kreis seine Position zufällig ändert, wenn er angeklickt wird. Also rechte Maustaste -> Add group -> KlickEreignisse. Nun müssen wir innerhalb der Gruppe Events anlegen, das sind dann von der Logik her SubEvents. Also rechte Maustaste -> Add subevent.

Event: Mouse -> On left button clicked

Eine Reaktion soll nur erfolgen, wenn die Maus dann auch über dem roten Kreis ist, aber nicht über dem grünen Kreis.

Also wieder rechte Maustaste -> Add subevent

Event: Mouse -> Cursor is over object -> roterKreis

Dann add another condition.

Event: Mouse -> Cursor is over object -> grünerKreis -> Invert

Action: roterKreis -> set position -> random(32,608), random(32, 448)

Drag&drop2.png

Nach diesem Schritt sollte der rote Kreis schon auf Mausklicks reagieren und seine Position zufällig neu bestimmen.

Nun geht es an das eigentliche Drag&Drop für den grünen Kreis. Dies besteht aus drei Schritten,

  1. dem Drücken der linken Maustaste (initialisiereZiehen)
  2. dem Ziehen mit der Maus (dasZiehen)
  3. und dem Loslassen (das Loslassen)

Für alle drei Schritte werden eigene Gruppen eingerichtet. Zuerst für die Initialisierung (rechte Maustaste -> add group -> initialisiereZiehen). Für die Gruppe wird dann wieder ein Sub-Event angelegt.

Event: Mouse -> mouse button is down -> left

Das soll aber nur dann einen Effekt haben, wenn nicht schon ein anderes Objekt gezogen wird. Also bruachen wir ein Sub-Event.

Event: compare variable -> aktuellZiehend=0

Nun soll natürlich auch noch die Maus über dem grünen Kreis sein, damit etwas passiert, also ein neues Sub-Event.

Event: Mouse -> cursor is over object -> grünerKreis

Vorsichtshalber wollen wir noch sicherstellen, dass der grüne Kreis noch nicht gerade gezogen wird, also add another condition.

Event: grünerKreis -> is boolean instance variable set -> wirdGezogen

Nun können wir handeln.

Action: grünerKreis -> set boolean -> wirdGezogen, True
Action: System -> set value -> aktuellZiehend, 1

Drag&drop3.png

Damit sind die Vorbereitungen abgeschlossen und wir können an das eigentliche Ziehen gehen, (rechteMaustaste -> add group -> dasZiehen).

Hier müssen wir nur untersuchen, ob der grüne Kreis gerade gezogen wird, also add subevent.

Event: -> grünerKreis -> is boolean instance variable set -> wirdGezogen
 
Action: grünerKreis -> set position to -> Mouse.X, Mouse.Y

Momentan kann der grüne Kreis auch unter dem roten Kreis liegen, daher heben wir ihn noch an.

Action: grünerKreis -> move to top of layer

Drag&drop4.png

Nun fehlt noch das Loslassen. Also wieder eine neue Gruppe erzeugen (rechteMaustaste -> add group -> dasLoslassen).

Hier untersuchen wir zuerst, ob die Maustaste losgelassen wurde (add subevent)

Event: Mouse -> on button released -> left

Dann müssen wir feststellen, ob der grüne Kreis gerade gezogen wird (add subevent)

Event: grünerKreis -> is boolean instance variable set -> wirdGezogen

Wir setzen die Variablen zurück.

Action: grünerKreis -> set boolean -> wirdGezogen, false
Action: System -> set value -> aktuellZiehen, 0

Nun gibt es zwei Fälle zu unterscheiden, der grüne Kreis berührt den roten Kreis oder er berührt ihn nicht (add subevent).

Event: grünerKreis -> is overlapping with another object -> roterKreis

Dann setzen wir ihn auf dessen Position, das ergibt den Einrast-Effekt.

Action: grünerKreis -> set position to another object -> roterKreis

Nun müssen wir noch den Fall betrachten, dass die Kreise nicht überlappen, dann schicken wir den grünen Kreis wieder in seine Ecke (insert new event below)

Event: grünerKreis -> is overlapping with another object -> roterKreis, Invert

Action: grünerKreis -> set position to -> 32, 448

Drag&drop5.png

DragDrop über Behavior

spielbare Version

.capx

Das Ziehen, welches im ersten Beispiel in drei Schritten realisiert wurde ist in Construct2 schon eingebaut. Man muss dem grünen Kreis nur das Behavior DragDrop geben, dann kümmerst sich Construct2 um den Rest. Wir können also die Gruppen für das Ziehen einfach löschen, auch die globalen Variablen.

Es gibt nur eine neue Gruppe DragEreignisse.

Event -> grünerKreis -> On DragDrop drop

dann ein Subevent dazu.

Event -> grünerKreis -> Is overlapping an other object: roterKreis
Action -> grünerKreis -> Set position to another object: roterKreis

Und dann parallel dazu.

Event -> grünerKreis -> Is overlapping an other object: roterKreis, Invertiert
Action -> grünerKreis -> Set position to(32,448)

Drag&drop6.png

Mit Vorrat

spielbare Version

.capx-Datei

In der folgenden Erweiterung geht es darum, vom grünen Kreis eine einstellbare Anzahl von Instanzen von einem Vorratsstapel holen zu können. Als Vorratsstapel dient hier der ein schwarzer Kreis mit grünem Inneren 64x64-sgkreis.png.

Der Vorrat wird über die globale Variable Vorrat mit dem Anfangswert 5 realisiert.

Wir legen beide Kreise wieder in die linke untere Ecke (32,448), wobei der grüne Kreis über dem schwarzen Kreis liegen sollte. Zur Kontrolle fügen wir noch oben im Spielfeld ein Textelement ein, welches zu Anfang unter Properties den Text "Anzahl Grün: 1" bekommt.

Im Event Sheet entfernen wir nun die ganze Gruppe "Klick Ereignisse", der schwarze Kreis soll nicht bewegbar sein, also kein DragDrop Behavior.

Dafür richten wir die folgenden Events ein.

Event: HintergrundKreis -> is overlapping another object: VordererKreis , Invertiert

Wenn wir die Anzahl der möglichen Kreise beschränken wollen, dann folgt dazu ein Subevent.

Events: System -> compare two values: VordererKreis.Count < Vorrat

auf alle Fälle folgen dann die Aktionen.

Action: HintergrundKreis -> spawn another object: VordererKreis on layer 0
Action: Text -> set text to "Anzahl Grün: " & VordererKreis.Count

Vorrat1.png

Nun kann man insgesamt 5 grüner Kreise von dem Vorratsstapel nehmen, bevor der Boden zu sehen ist.

Mit unterschiedlichem Vorrat

Im folgenden Beispiel geht es darum, mit möglichst wenig Aufwand im Event-Sheet drei Vorratsstapel mit unterschiedlichen Elementen Anzulegen.

spielbare Version

.capx

Als Vorrat gelten jeweils vollständig mit Farbe gefüllte Kreise und als Hintergrund (nur zur Unterscheidung) schwarze Kreise, die mit etwas Farbe gefüllt sind.

64x64-gkreis.png 64x64-bkreis.png 64x64-rkreis.png

64x64-sgkreis.png 64x64-sbkreis.png 32x32-srkreis.png

Aus beiden Dreiergruppen erzeugen wir jeweils eine Animation mit drei Frames, die Property Speed muss dabei auf 0 stehen.

Vorrat-frames1.png

Von dem Hintergrundkreis erzeugen wir drei Instanzen, die jeweils unterschiedliche Werte bei InitialFrame bekommen, nämlich 0, 1 bzw. 2. Darüber legen wir dann jeweils eine Instanz von VordererKreis mit der gleichen Zahl für InitialFrame.

Wenn jetzt einer der Hintergrundkreise feststellt, dass er nicht von einem VordergrundKreis überlappt wird, dann erzeugt er einen neuen Vordergrundkreis, sofern die zulässige Maximalzahl an Vordergrundkreisen nicht überschritten ist. Die zulässige Maximalzahl für jeden der Kreise wird in einem Array namens Vorrat festgehalten.

Gegenüber dem vorherigen Beispiel verändert sich nur (mal abgesehen von kleinen Umbenennungen) die Gruppe "Vorrat halten".

Im ersten Schritt muss der Vorrat festgelegt werden.

Event: Syste -> On start of layout

Dann wird das Array initialisiert.

Action: Vorrat -> Set size to (3, 1, 1)
Action: Vorrat -> Set value at 0 to 5
Action: Vorrat -> Set value at 1 to 8
Action: Vorrat -> Set value at 2 to 3

Vorsichtshalber achten wir darauf, dass der zuletzt bewegte Kreis immer oben liegt.

Event: VordererKreis -> On DragDrop start
Action: VordererKreis -> Move to top of layer

Nun muss man für jeden Hintergrundkreis nachschauen, ob er überlappt wird. Hier benutzen wir zur Abkürzung die globale Variable Frame, die natürlich vorher definiert sein muss.

Event: System -> For each Hintergrundkreis
Action: System -> Set Frame to HintergrundKreis.AnimationFrame

Der Rest wird in einem Subevent realisiert

Event: HintergrundKreis -> Is overlapping VordererKreis, Invertieren

Dazu kommt per add another condition dann.

Event: Array -> Value at Frame > 1

Action: HintergrundKreis -> Spawn an other object: VordererKreis on layer 0
Action: VordererKreis -> Set animation frame to Frame
Action: Vorrat -> Set value at Frame to Vorrat.At(Frame)-1
Action: Text -> Set text to "Anzahl Kreise: " & VordererKreis.Count

Vorrat-frames2.png