Machen eines Match-3-Spiels in Construct 2: Punkte, Matching und Schwerkraft
German (Deutsch) translation by Władysław Łucyszyn (you can also view the original English article)
Im vorherigen Tutorial haben wir ein grundlegendes Match-Erkennungssystem in unser Match-3-Spiel integriert. Während wir auf dem besten Weg sind, ein spielbares Spiel zu haben, gibt es noch einige wichtige Spielelemente, die wir brauchen, bevor wir das, was wir haben, wirklich als "Spiel" bezeichnen können. Dieser Artikel wird sich darauf konzentrieren, einige dieser fehlenden Details auszufüllen und uns unserem Endprodukt viel näher zu bringen.
Finale Spiel-Demo
Hier ist eine Demo des Spiels, auf das wir in dieser Serie hinarbeiten:
1. Punkte vergeben
Wir werden Punkte behandeln, bevor wir mit der Verbesserung des Matching-Systems beginnen, da es viel einfacher ist, das erste Problem in unserem aktuellen Matching-System zu erkennen, wenn wir ein Punktesystem implementiert haben.
Das Punktesystem in unserem Spiel wird sehr einfach sein. Für jeden Block, der zur Bildung einer bestimmten Gruppe verwendet wird, erhält der Spieler 10 Punkte. In einem späteren Tutorial werden wir auch ein System hinzufügen, mit dem der Spieler mehr Punkte sammeln kann, indem er mehrere Gruppen miteinander verkettet. Im Moment konzentrieren wir uns jedoch darauf, nur ein einfaches Punktesystem für den Spieler einzuführen.
Bevor wir mit der Bearbeitung der Ereignisse beginnen, müssen wir eine Punkteanzeige hinzufügen. Gehen Sie also zuerst zu Layout 1 und gehen Sie wie folgt vor:
- Fügen Sie ein neues Sprite-Objekt ein.
- Öffnen Sie das Bild Game Field Images/ScoreArea.png aus dem Grafikpaket.
- Schließen Sie den Animationseditor.
- Stellen Sie die Position auf
491, 383
ein.
- Fügen Sie ein neues Textobjekt ein.
- Stellen Sie die Schriftart über das Dropdown-Menü auf
Calibri, Fett, 22
ein. - Setzen Sie den Namen auf
ScoreText
- Stellen Sie die Farbe auf
Weiß
oder255, 255, 255
ein - Stellen Sie die Position auf
419, 398
ein. - Stellen Sie die Größe auf
200, 50
ein. - Setzen Sie den Text auf
0
.
- Stellen Sie die Schriftart über das Dropdown-Menü auf
Layout 1 sollte nun folgendermaßen aussehen:



Nachdem wir dem Spieler etwas zu sagen haben, was der Punktetext bedeutet, und ein Textobjekt, mit dem die Punktzahl des Spielers angezeigt werden kann, können wir fortfahren, dem Spieler tatsächlich Punkte zu geben. Gehen Sie zu Eventblatt 1 und erstellen Sie eine neue globale Variable.
Global Variable Name: "Score" Type = Number Value = 0
Die Variable sollte folgendermaßen aussehen:



Dies ist die Variable, die wir ändern, wenn wir den Spielern Punkte geben. Als nächstes erstellen wir eine neue Funktion, die beim Aufruf erkennt, wie viele Blöcke der Spieler in Gruppen abgeglichen hat, und ihnen die entsprechende Anzahl von Punkten gibt.
Event: Condition: Function> On Function Name = "GivePoints" Condition: System>For Each Object = Block Condition: Block>Is boolean instance variable set Instance Variable = IsMatched Action: System> Add to Variable = Score Value = 10 Action: ScoreText>Set text Text = Score
Ihr Code sollte folgendermaßen aussehen:



Um es noch einmal zu wiederholen, dieses Event betrachtet jeden einzelnen Block. Jedes Mal, wenn ein Block gefunden wird, für den IsMatched
auf true
gesetzt ist - was bedeutet, dass bestätigt wurde, dass er Teil einer Gruppe ist -, erhält der Spieler 10 Punkte für diesen Block und aktualisiert den Punktetext.
Wenn Sie Ihr Spiel zu diesem Zeitpunkt testen, scheint die Funktion nicht zu funktionieren. Der Grund dafür ist, dass wir die Funktion nirgendwo im Code aufgerufen haben, sodass die Punkte niemals inkrementiert werden und der Text niemals aktualisiert wird. Gehen Sie zu Ihrer FindMatches
-Funktion und fügen Sie am Anfang des letzten Untererevents für diese Funktion eine neue Aktion hinzu.
Action: Function>Call function Name = "GivePoints"
Ihre FindMatches
-Funktion sollte nun folgendermaßen aussehen:



Hinweis: Stellen Sie sicher, dass Sie diese neue Aktion zu Beginn des Unterevents hinzugefügt haben. Wenn Sie diese Aktion am Ende hinzufügen, funktioniert sie nicht, da alle übereinstimmenden Blöcke zerstört wurden, bevor die GivePoints
-Funktion aufgerufen wird. Dies bedeutet, dass bei der Suche nach übereinstimmenden Blöcken keine gefunden werden und der Spieler keine Punkte erhält.
Zu diesem Zeitpunkt können Sie Ihr Spiel erneut testen. Der Punktetext sollte aktualisiert werden und der Spieler erhält für jedes Spiel die richtige Anzahl von Punkten.
2. Verbessern der Match-Erkennung
Nachdem wir das Punktesystem installiert haben, möchten wir, dass Sie das Spiel ausführen und das unten gezeigte Szenario erstellen.

Tauschen Sie nun die Blöcke aus, die ich hier hervorgehoben habe, und beobachten Sie Ihre Punktzahl, um zu sehen, wie viele Punkte Sie erhalten.

Als Sie dieses Match gebildet haben, sollten Sie gesehen haben, dass Sie 50 Punkte gewonnen haben. Dies liegt daran, dass das Punktesystem dem Spieler derzeit 10 Punkte für jeden Block gibt, der als IsMatched
markiert ist, anstatt dem Spieler 10 Punkte für jeden Block zu geben, der in jedem Spiel verwendet wird, wie das oben beschriebene System.
Wenn das Punktesystem richtig funktioniert, gibt es dem Spieler 60 Punkte: 30 für die vertikale Gruppe von drei Blöcken und 30 für die horizontale Gruppe von drei Blöcken. Dieses Problem ergibt sich aus der Tatsache, dass das Match-System keine Möglichkeit zum Markieren hat, wenn ein Block sowohl horizontal als auch vertikal abgeglichen wird. es weiß nur, ob der Block überhaupt übereinstimmt.
Um dieses Problem zu lösen, fügen wir unserem Block-Objekt zunächst zwei neue Instanzvariablen hinzu, MatchedX
und MatchedY
.
Instance Variable: Name = MatchedX Type = Boolean Initial Value = false Instance Variable: Name = MatchedY Type = Boolean Initial Value = false
Ihre Variablen sollten folgendermaßen aussehen:



Diese Variablen werden in Verbindung mit IsMatched
verwendet, um dem System mitzuteilen, wann der Block Teil horizontaler oder X-Gruppen ist und wenn der Block Teil vertikaler oder Y-Gruppen ist. Nachdem wir die Variablen haben, werden wir die CheckMatches
-Funktion so ändern, dass beim Beschriften eines Blocks IsMatched
, weil er eine ausreichend große Gruppe gefunden hat, dieser Block auch als Teil einer X- oder Y-Gruppe gekennzeichnet wird, je nachdem, ob Parameter 3 oder Parameter 4 ist 1.
Gehen Sie zur CheckMatches
-Funktion und ersetzen Sie die ursprüngliche NumMatchesFound
-Prüfung durch diese beiden neuen Untererevents:
Sub-Event: Condition: System>Compare two values First value = Function.Param(3) Comparison = Equal to Second value = 1 Condition: System>Compare variable Variable = NumMatchesFound Comparison = Greater or equal Value = 3 Action: Block>Set Boolean Instance variable = IsMatched Value = True Action: Block>Set Boolean Instance variable = MatchedX Value = True Sub-Event: Condition: System>Compare two values First value = Function.Param(4) Comparison = Equal to Second value = 1 Condition: System>Compare variable Variable = NumMatchesFound Comparison = Greater or equal Value = 3 Action: Block>Set Boolean Instance variable = IsMatched Value = True Action: Block>Set Boolean Instance variable = MatchedY Value = True
Ihre CheckMatches
-Funktion sollte nun folgendermaßen aussehen:



Die neue Version von CheckMatches
funktioniert also genauso wie die vorherige, außer dass jetzt auch geprüft wird, ob der Block in einer vertikalen oder einer horizontalen Gruppe übereinstimmt, und der Block entsprechend mit den neuen Variablen beschriftet wird MatchedX
und MatchedY
.
Vergabe von zusätzlichen Punkten an Blöcke, die zweimal übereinstimmen
Nachdem wir nun feststellen können, wann ein Block vertikal, horizontal und in beide Richtungen abgeglichen wird, anstatt nur zu wissen, dass er in einer Gruppe abgeglichen wurde, müssen wir der GivePoints
-Funktion ein Unterevent hinzufügen verteilt zusätzliche 10 Punkte für einen Block, für den sowohl MatchedX
als auch MatchedY
auf true gesetzt sind.
Gehen Sie zur GivePoints
-Funktion und fügen Sie dieses Unterevent hinzu:
Sub-Event: Condition: Block>Is Boolean instance variable set Instance variable = MatchedX Condition: Block>Is Boolean instance variable set Instance variable = MatchedY Action: System>Add to Variable = Score Value = 10 Action: Text>Set text Value = Score
Ihre GivePoints
-Funktion sollte nun folgendermaßen aussehen:



Wenn Sie Ihr Spiel ausführen und das oben dargestellte Szenario erneut erstellen, sollte sich Ihre Punktzahl jetzt korrekt um 60 Punkte erhöhen.
3. Hinzufügen der Schwerkraft
Nachdem wir ein Punktesystem implementiert und das Matching-System aktualisiert haben, werden wir damit beginnen, einen weiteren wichtigen Aspekt des Gameplays zu verbessern. Wenn Sie bis zu diesem Zeitpunkt Zeit mit dem Spiel verbracht haben, wissen Sie, dass eines der größten Probleme darin besteht, dass bei der Zerstörung von Blöcken nichts mit den darüber liegenden Blöcken passiert. Insbesondere fallen die Blöcke über leeren Leerzeichen nicht aus, um diese Leerzeichen auszufüllen.
Dies ist in den Tests in Ordnung, aber in der endgültigen Version wäre es für das Gameplay nachteilig, die Dinge so zu lassen, wie sie sind. Das nächste, was wir hinzufügen werden, ist die "Schwerkraft", die dazu führt, dass die Blöcke fallen und sich füllen Leerzeichen, wenn andere Blöcke zerstört werden.
Die Art und Weise, wie wir dieses System implementieren, ist eigentlich recht einfach. Wir führen eine Überprüfung mit dem Event Block > Is overlapping at offset
durch, um festzustellen, ob sich unter dem betrachteten Block ein Block befindet. Wenn wir feststellen, dass es keinen Block gibt, verschieben wir den Block, den wir betrachten, nach unten, um den leeren Raum auszufüllen. Andernfalls werden wir nichts tun.
Damit dies funktioniert, erstellen wir ein neues Event:
Event: Condition: INVERT>Block>Is overlapping at offset Object = Block Offset X = 0 Offset Y = 8 Action: Block>Move at angle Angle = 90 Distance = (Block.Width + 2)/2
Ihr Code sollte folgendermaßen aussehen:



Wenn Sie das Spiel zu diesem Zeitpunkt ausführen, werden Sie feststellen, dass in dem Moment, in dem das Spiel beginnt, alle Blöcke vom Bildschirm fallen! Der Grund dafür ist, dass wir nichts in den Code eingefügt haben, um zu sagen, wo sich der "Boden" des Spielfelds befinden würde.
Im Wesentlichen erkennen die Blöcke in der unteren Reihe, dass sich keine Blöcke darunter befinden, und fallen entsprechend ab. Sobald die unterste Reihe von Blöcken gefallen ist, sieht die nächstniedrigere Reihe, dass sich jetzt keine Blöcke unter ihnen befinden, und auch sie fallen. Dieser Vorgang wird fortgesetzt, bis alle Blöcke gefallen sind und der Bildschirm vollständig leer bleibt.
Sie können eine etwas verlangsamte Version davon im GIF unten in Aktion sehen:



Um dies zu beheben, fügen wir dem Event eine zweite Bedingung hinzu.
Event: Condition: Block>Compare Y Comparison = Less or equal Y = SPAWNY - 1
Ihr Code sollte jetzt so aussehen:



Durch Hinzufügen dieser neuen Bedingung stellen wir sicher, dass nur Blöcke, die über der Y-Position der untersten Reihe liegen, von unserer "Schwerkraft" betroffen sind. Trotz dieses Fixes haben wir immer noch einige Probleme.
Umgang mit dem Ziehen
Das Hauptproblem besteht darin, dass das Ereignis, bei dem geprüft wird, ob sich unter einem Block ein leerer Bereich befindet, nichts darüber aussagt, dass er inaktiv ist, wenn der Spieler einen Block zieht. Dies bedeutet, dass, wenn Sie einen Block zu weit ziehen, ohne ihn loszulassen, die Blöcke an der Position über der Stelle, von der Sie ihn gezogen haben, in den Bereich fallen, den der von Ihnen gezogene Block hinterlassen hat. Darüber hinaus tritt bei dem Block, den Sie ziehen, ein Problem auf, wenn Sie ihn aus dem Spielfeld entfernen, und er fällt vom Mauszeiger ab, da sich keine Blöcke darunter befinden.
Um dieses Problem zu beheben, müssen wir eine neue globale Variable hinzufügen, um dem System mitzuteilen, wann wir einen Block verschieben, eine neue Aktion zum Ziehen und Ablegen von Ereignissen im Block, um diese globale Variable festzulegen, und eine dritte Bedingung für das Schwerkraftereignis, damit dies erforderlich ist Diese Variable wird vor der Aktivierung berücksichtigt.
Lassen Sie uns zunächst die globale Variable erstellen:
Global Variable: Name = BlockBeingMoved Type = Number Initial Value = 0
Ihre Variable sollte folgendermaßen aussehen:



Wechseln Sie jetzt zum On DragDrop drag start
-Event und fügen Sie eine neue Aktion hinzu:
Action: System>Set Value Variable = BlockBeingMoved Value = 1
Wechseln Sie auch zum On DragDrop drop
-Event und fügen Sie dem primären Event eine neue Aktion hinzu:
Action: System>Set Value Variable = BlockBeingMoved Value = 0
Mit den hinzugefügten Zeilen sollten Ihre DragDrop-Events nun folgendermaßen aussehen:



Gehen Sie schließlich zum Schwerkraft-Event und fügen Sie eine neue Bedingung hinzu:
Condition: System>Compare Variable Variable = BlockBeingMoved Comparison = Equal to Value = 0
Ihr Schwerkraftcode sollte jetzt folgendermaßen aussehen:



Die neue Variable BlockBeingMoved
, die wir erstellt haben, teilt dem System mit, wann ein Block vom Player verschoben wird. Wenn die Variable gleich 0
ist, bedeutet dies, dass kein Block verschoben wird und die Schwerkraftskripte wie gewohnt ausgeführt werden können. Wenn die Variable gleich 1
ist, bedeutet dies, dass ein Block verschoben wird und die Schwerkraftskripte nicht ausgeführt werden sollten.
Wenn Sie das Spiel zu diesem Zeitpunkt ausführen, werden Sie feststellen, dass unabhängig davon, wo Sie den Block bewegen, während Sie ihn ziehen, keine Probleme auftreten.
Nach neuen Matches suchen
Jetzt haben wir nur noch ein letztes Problem in Bezug auf das Schwerkraftsystem. Führen Sie das Spiel aus und erstellen Sie ein ähnliches Szenario:

Nehmen Sie nun den Tausch vor, den ich in diesem nächsten Bild hervorgehoben habe.

Sie sollten beachten, dass, wenn die Gruppe der Grün- / Sternblöcke zerstört wird, ein Orangen- / Sechseckblock fällt und eine Gruppe von drei Blöcken bildet, aber nicht zerstört wird.
Der Grund, warum diese Blöcke nicht zerstört werden, ist, dass wir die FindMatches
-Funktion nie ein zweites Mal aufgerufen haben, um festzustellen, ob neue Matches gebildet wurden, als die Blöcke fielen, um die leeren Felder auszufüllen. Um dies zu beheben, gehen Sie zu dem Event, das unter Leerzeichen nach Leerzeichen sucht, und fügen Sie dieses andere Event hinzu:
Event: Condition: System>Else Action: Function>Call function Name= "FindMatches"
Ihr Code sollte folgendermaßen aussehen:



Diese else-Anweisung bedeutet, dass immer dann, wenn festgestellt wird, dass keine Leerzeichen vorhanden sind, eine Überprüfung durchgeführt wird, um festzustellen, ob Gruppen zu zerstören sind. Dieses Ereignis wird automatisch ausgeführt, wenn Blöcke in neue Positionen fallen, da es durch eine Else-Anweisung aktiviert wird, die mit dieser Prüfung verknüpft ist, und wird erst ausgelöst, wenn sichergestellt ist, dass alle Blöcke an ihren Platz gefallen sind.
Wenn Sie das Spiel zu diesem Zeitpunkt ausführen, werden Sie feststellen, dass Sie jetzt Ketten von Blöcken erstellen können, indem Sie Blöcke so zerstören, dass Gruppen gebildet werden, wenn die verbleibenden Blöcke fallen. Darüber hinaus werden Sie feststellen, dass beim ersten Start des Spiels alle Gruppen, die ursprünglich erzeugt wurden, ebenfalls zerstört werden. Wie ich im vorherigen Tutorial gesagt habe, werden wir eventuell vorgefertigte Matches eliminieren, sodass dieses Problem am Ende keine Rolle spielt.
Entfernen von Blöcken aus dem ursprünglichen Layout
Schließlich müssen wir noch etwas tun, bevor wir unser Schwerkraftsystem als vollständig betrachten können. Je nachdem, wo Sie das erste Block-Sprite platziert haben, als Sie das erste Tutorial abgeschlossen haben, werden Sie möglicherweise feststellen, dass es beim Starten des Spiels herunterfällt und sichtbar wird.
Wenn Sie nicht wissen, was ich meine, gehen Sie zu Layout 1, setzen Sie die Position Ihres Block-Sprites auf 521, -32
und führen Sie das Spiel aus. Wenn Sie das Spiel spielen, sollten Sie das ursprüngliche Blockland an der Position sehen, die ich im Bild unten hervorgehoben habe:



Wie Sie im obigen Bild sehen können, fällt der ursprüngliche Block von seiner Position außerhalb des Bildschirms und wird sichtbar. Wir wollen das nicht, weil es uns erst später Probleme bereiten wird. Um dieses kleine Problem zu lösen, fügen wir dem Event On start of layout
eine Aktion hinzu, die alle Blöcke zerstört, die sich beim ersten Laden im Layout befinden.
Action: Block>Destroy
Ihr Event sollte jetzt so aussehen:



Wenn Sie das Spiel ausführen, sollte der Block nicht mehr angezeigt werden. Sie fragen sich vielleicht, warum wir den Block nicht einfach aus dem Layout gelöscht haben, damit wir uns überhaupt nicht um dieses Problem kümmern müssen. Der Grund, warum wir dies nicht getan haben, ist, dass Construct 2 mit Events keine Kopien eines Objekttyps mit Ereignissen erstellen kann, es sei denn, es gibt bereits eine Instanz dieses Objekttyps im Spiel, wenn es zum ersten Mal geladen wird. Indem wir es innerhalb eines Ereignisses löschen, entfernen wir es, damit es später nicht zu einem Problem wird, und ermöglichen es, durch Code so viele Blöcke zu erzeugen, wie wir benötigen.
Abschluss
Wir haben in diesem Tutorial viele Themen behandelt, und obwohl wir noch mehr tun können, halte ich es für wichtig, eine Pause einzulegen und diese Informationen einfließen zu lassen. In der nächsten Folge werden wir einige kleine Probleme beheben Der ausgefallene Gleitkomma-Text, den Sie vielleicht bemerkt haben, befindet sich in der endgültigen Demo und richtet das Verkettungssystem ein.
Ich hoffe, Sie haben viel aus diesem Teil der Serie herausgeholt und wir sehen uns nächste Woche wieder hier.