Construct2 Drag&Drop
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
Als Grafikobjekte dienen die beiden folgenden Kreise.
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.
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)
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,
- dem Drücken der linken Maustaste (initialisiereZiehen)
- dem Ziehen mit der Maus (dasZiehen)
- 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
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
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
DragDrop über Behavior
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)
Mit Vorrat
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 .
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
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.
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.
Aus beiden Dreiergruppen erzeugen wir jeweils eine Animation mit drei Frames, die Property Speed muss dabei auf 0 stehen.
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