In Spielen, die aus miteinander verbundenen Räumen wie The Legend of Zelda, dem neueren The Binding of Isaac oder einer beliebigen Art von Roguelike oder sogar Metroidvania-ähnlichen Spielern bestehen, spielen Türen eine wesentliche Rolle bei der Navigation und beim Fortschritt des Spielers.
Türen ermöglichen es dem Spieler, von einem Raum oder einer Ebene zum anderen zu reisen, und haben daher einen wichtigen Platz in der Navigation und der Verbindung der verschiedenen Räume untereinander und bei der Definition der Karte als offene Welt oder als Dungeonetage. Sie können auch als temporäre Straßensperren fungieren, die der Spieler durch einen bestimmten Mechaniker entsperren muss (z. B. einen Schlüssel erhalten oder einen Schalter aktivieren)..
In diesem Tutorial werde ich verschiedene Sperrmechaniken demonstrieren und Möglichkeiten vorschlagen, sie in Ihren Spielen zu implementieren. Dies soll keinesfalls die einzige oder beste Implementierung sein. Sie sind praktische Beispiele.
Die interaktiven Demos in diesem Tutorial wurden mit dem HTML5-Game Maker-Tool Construct 2 erstellt und sollten mit seiner kostenlosen Version kompatibel sein. (Die CAPX-Dateien sind im Quellendownload verfügbar.) In diesem Lernprogramm erfahren Sie jedoch, wie Sie die Logik für Türen und Schlösser in jedem beliebigen Motor implementieren. Wenn Sie erst einmal die Idee hinter der Logik verstanden haben, müssen Sie sich nur mit Ihrem eigenen Programmierwerkzeug / Ihrer Sprache auskennen und wie Sie es an das aktuelle Spiel anpassen möchten.
Lass uns eintauchen!
Eine Tür ist im Grunde ein Szenerie-Block, der nicht durchfahren werden kann. Dadurch wird verhindert, dass der Charakter des Spielers durchgeht, bis er entriegelt wird. Die Tür kann verschiedene Zustände annehmen: gesperrt oder entriegelt, geschlossen oder geöffnet.
Es muss eine offensichtliche Darstellung des letzteren geben; Der Spieler muss in der Lage sein zu erkennen, dass es sich bei der Tür tatsächlich um eine Tür handelt und ob sie sich in einem gesperrten oder entriegelten Zustand befindet.
In den folgenden Demos werden die Türen durch zwei Grafiken dargestellt:
Ich habe auch verschiedene Farben verwendet, um die verschiedenen Materialien darzustellen, aus denen die Türen bestehen. Aber um ehrlich zu sein, der grafische Aspekt hängt von Ihnen, Ihrem Spiel und seinem Universum ab. Der wichtigste Teil ist, dass die Tür eindeutig als Tür identifiziert werden kann, und es sollte offensichtlich sein, ob sie den Fortschritt des Spielers blockiert oder sich öffnet und zum Rest des Levels oder der Welt führt.
Wenn sie geschlossen oder verriegelt ist, sollte die Tür ein fester Zustand sein. Nach dem Öffnen sollte der Solid-State deaktiviert sein, damit Zeichen durchlaufen werden können. Stellen Sie sicher, dass Sie unabhängig von Ihrer Kollisions-Engine diesen Status schnell ändern können.
Aus Sicht der Programmierung sollte das Türobjekt ein enthalten oder mit einem verknüpft sein ist gesperrt
Boolesche Variable Abhängig vom Wert dieser Variablen können Sie festlegen, welches Sprite angezeigt werden soll und ob der Block fest sein soll oder nicht.
Um die Tür zu entriegeln, sollte das Zeichen ein enthalten has_key
Boolesche Variable selbst, wenn der Spieler einen Schlüssel aufgenommen hat: wahr
wenn sie es haben, falsch
wenn nicht.
In dieser grundlegenden Mechanik fungiert der Schlüssel als Teil des Inventars des Charakters, und ein Schlüssel öffnet sich alles Türen. Die Benutzung des Schlüssels an einer Tür verbraucht sie nicht. Der Schlüssel bleibt im Inventar der Figur.
Zur Veranschaulichung können wir einfach ein Bild des Schlüssels im HUD anzeigen, um den Spieler mitzuteilen, dass er einen Schlüssel besitzt, der die Türen öffnen könnte, sobald der Charakter sie aufgenommen hat (indem er den Charakter über den Schlüssel im Raum bewegt ).
Betrachten Sie das folgende grundlegende Beispiel:
Klicken Sie auf die Demo, um sie zu aktivieren, steuern Sie den Buchstaben mit den Pfeiltasten Ihrer Tastatur und führen Sie die Aktionen mit der Leertaste aus. (In diesem Beispiel lautet die Aktion "Tür öffnen".)
Wände sind feste Blöcke, durch die der Charakter bei einem Aufprall nicht durchgehen kann. Geschlossene Türen sind auch solide.
Um eine Tür zu öffnen, muss sich das Zeichen innerhalb von 64 Pixeln von der Tür befinden und über einen Schlüssel verfügen has_key
Eine boolesche Variable, die bestimmt, ob der Charakter den Schlüssel in seinem Inventar hat wahr
).
Wenn unter diesen Bedingungen der Spieler die Leertaste drückt, wird der Zustand der entsprechenden Tür geändert. Seine boolesche Variable verschlossen
ist eingestellt auf falsch,
und sein "fester" Zustand ist deaktiviert.
Im Pseudocode würde dies ungefähr so aussehen:
Door.Locked = True Door.AnimationFrame = 0 // Der Animationsrahmen, der die Tür als gesperrt anzeigt. Door.Solid = Aktiviert // Der Solid-State der Tür ist aktiviert. Door.Locked = False Door.AnimationFrame = 1 // Die Animation Rahmen, der die Tür als geöffnet anzeigt Door.Solid = Deaktiviert // Der Solid-State der Tür ist deaktiviert. Tastaturtaste "Leertaste" gedrückt und Entfernung (Zeichen, Tür) <= 64px and Door.Locked = True and Character.Has_Key = True //The player has a key Door.Locked = False Keyboard Key "Space" is pressed and Distance(Character,Door) <= 64px and Door.Locked = True and Character.Has_Key = False //The player does not have a key Text.text = "You don't have a key for that door"
Erinnerung: Dieser Code steht nicht für eine bestimmte Sprache. Sie sollten es in jeder beliebigen Sprache implementieren können.
Sie können auch feststellen, dass wir prüfen, wann der Spieler dies tut nicht haben den erwarteten Schlüssel und zeigen eine Rückmeldung an, in der erklärt wird, warum die Tür nicht entriegelt wurde. Sie können Schecks wie die am besten für Ihr Spiel geeigneten erledigen. Beachten Sie jedoch, dass es immer schön ist, Ihrem Spieler zu sagen, dass seine Aktion registriert wurde, und zu erklären, warum er nicht abgeschlossen wurde.
Dies ist eine sehr grundlegende Tür- und Schlosslogik und deren Implementierung. Im weiteren Verlauf des Tutorials werden andere Schließsysteme betrachtet, bei denen es sich um Varianten dieses Basissystems handelt.
Wir haben das grundlegende System gesehen, bei dem der Schlüssel ein ganzer Teil des Inventars des Charakters ist und ein Schlüssel alle Türen öffnet und wieder verwendet werden kann, um mehrere Türen zu öffnen. Lass uns darauf aufbauen.
Im nächsten Beispiel hat der Charakter eine Stapel von Schlüsseln in seinem Inventar. Obwohl es verschiedene Türfarben gibt, ist der Unterschied hier rein grafisch - das Türobjekt ist logischerweise das gleiche wie im Basisbeispiel, und ein Schlüsseltyp kann jeden von ihnen öffnen. Jedes Mal, wenn Sie einen Schlüssel zum Öffnen einer Tür verwenden, wird dieser Schlüssel jedoch vom Stapel entfernt.
Was die Kodierung betrifft, so ist diese Änderung meistens auf der Ebene der Figur. Anstelle einer has_key
Boolesche Variable, möchten Sie eine haben numerisch Variable, die die Anzahl der Schlüssel enthält, die der Charakter "auf Lager" hat.
Jedes Mal, wenn der Charakter einen Schlüssel abholt, fügen Sie hinzu 1
auf diese Variable, um den Stapel nach oben darzustellen. Jedes Mal, wenn der Charakter eine Tür öffnet, subtrahieren 1
von dieser Variablen, um die Verwendung eines Schlüssels darzustellen. (Im Videospielland werden Schlüssel zerstört, sobald sie einmal verwendet werden.)
Eine weitere Modifikation ist, wenn der Spieler die Leertaste drückt: Anstatt zu prüfen, dass a has_key
Boolesche Variable ist wahr
, Wir möchten eigentlich den Wert von überprüfen KeyStack
ist mehr als Null, so dass wir nach dem Öffnen der Tür einen Schlüssel verbrauchen können.
Im Pseudocode sieht das ungefähr so aus:
Türmechanik = wie im Grundbeispiel oben. Tastaturtaste "Leertaste" wird gedrückt und Character.KeyStack> 0 und Abstand (Zeichen, Tür) <= 64 and Door.Locked = True Character.KeyStack = Character.KeyStack - 1 Door.Locked = False
In diesem neuen Beispiel betrachten wir ein Szenario, in dem unterschiedliche Türarten unterschiedliche Schlüsselarten zum Entriegeln erfordern.
Wie im ersten grundlegenden Beispiel sind die Schlüssel Teil des Inventars der Figur. Wir kehren zurück zur Verwendung von booleschen Variablen, um festzustellen, ob der Charakter die erforderlichen Schlüssel aufgenommen hat. Und da wir unterschiedliche Schlüssel haben werden, werden wir auch verschiedene Arten von Türen haben (schwarze Tür, rote Tür, goldene Tür), die auch einen geeigneten Schlüssel erfordern, um sie öffnen zu können (schwarzer Schlüssel, roter Schlüssel bzw. goldener Schlüssel) ).
Die Türobjekte verwenden verschiedene Sprites, um ihr Material anzuzeigen, und enthalten eine numerische Variable mit dem Namen WhichKey
Dies zeigt die erwartete Art des Schlüssels sowie die Art der Grafik an, die angezeigt werden soll. Die verschiedenen Schlüsselwerte sind zur besseren Lesbarkeit als konstante Variablen enthalten.
Im Pseudocode:
CONSTANT BLACK_KEY = 0 CONSTANT RED_KEY = 1 CONSTANT GOLD_KEY = 2 Die Türmechanik ist die gleiche wie im Grundbeispiel. Tastaturtaste "Leertaste" wird gedrückt. // Die Tür erfordert eine schwarze Taste, aber das Zeichen hat keine. Wenn Door.Locked = True und Door.WhichKey = BLACK_KEY und Character.Has_Black_Key = False und Distance (Door, Character) <= 64 Text.text="You need a black key for this door" //The door requires a red key but the character doesn't have one Else If Door.Locked = True and Door.WhichKey = RED_KEY and Character.Has_Red_Key = False and Distance(Door,Character) <= 64 Text.text="You need a red key for this door" //The door requires a gold key but the character doesn't have one Else If Door.Locked = True and Door.WhichKey = GOLD_KEY and Character.Has_Gold_Key = False and Distance(Door,Character) <= 64 Text.text="You need a red key for this door" //The door requires a black key and the character has one Else If Door.Locked = True and Door.WhichKey = BLACK_KEY and Character.Has_Black_Key = True and Distance(Door,Character) <= 64 Door.Locked = False //The door requires a red key and the character has one Else If Door.Locked = True and Door.WhichKey = RED_KEY and Character.Has_Red_Key = True and Distance(Door,Character) <= 64 Door.Locked = False //The door requires a gold key and the character has one Else If Door.Locked = True and Door.WhichKey = GOLD_KEY and Character.Has_Gold_Key = True and Distance(Door,Character) <= 64 Door.Locked = False
Dies ist eine Variation des grundlegenden Beispiels, die verschiedene Arten von Schlüsseln und Türen zulässt und keine Schlüssel zum Öffnen von Türen verbraucht. Sobald Sie den Schlüssel haben, ist er Teil Ihres Inventar-Teils der "Statistik" des Charakters..
Diesmal muss der Spieler, anstatt direkt auf Türen zu wirken, einen bestimmten Schalter aktivieren, um eine bestimmte Tür zu öffnen oder zu schließen.
Die Türen hier sind im Wesentlichen das gleiche Objekt wie im Grundbeispiel. Sie könnten unterschiedliche Grafiken anzeigen, aber die Logik des Objekts ist immer noch dieselbe. Es gibt jedoch einen Zusatz: Wir fügen zwei numerische Variablen hinzu DoorID
und SwitchID
, Wir wissen, welcher Schalter an welche Tür gebunden ist.
Wechselt sind eine neue Art von Objekten, die ich ausgewählt habe, um fest zu sein (aber das muss nicht sein). Sie enthalten eine boolesche Variable, Aktiviert
, und numerische Variablen DoorID
und SwitchID
Wie Sie sich vorstellen können, bestimmen wir, welcher Schalter an welche Tür gebunden ist.
Also wenn ein Schalter hat Aktiviert: Richtig
, Die "verknüpfte" Tür ist eingestellt Gesperrt: falsch
. Unsere Aktion mit der Leertaste wird dann ausgeführt, wenn neben einem Schalter, anstatt neben einer Tür. Beachten Sie das Fehlen einer Taste in diesem Beispiel, da die Schalter als Tasten fungieren:
Wir könnten einfach einen einfachen Code verwenden, der die Türschalter-Verknüpfungen im selben Raum prüft (da in diesem Beispiel drei Türen und Schalter im selben Raum angezeigt werden). Später werden wir jedoch feststellen, dass möglicherweise Schalter für Türen vorhanden sind, die sich in Türen befinden Ein weiterer Raum, und so wird ihre Aktion nicht genau in dem Moment ausgeführt, in dem der Spieler den Schalter aktiviert; Dies geschieht später, wenn der neue Raum geladen wird.
Aus diesem Grund brauchen wir Beharrlichkeit. Eine Möglichkeit hierfür ist die Verwendung von Arrays, um Daten wie den Status der Switches zu verfolgen (dh, ob jeder Switch aktiviert ist oder nicht)..
Im Pseudocode:
CONSTANT SWITCH_DOORID = 0 CONSTANT SWITCH_ACTIVATION = 1 // Durch diese Konstanten behalten wir eine lesbare Erinnerung an die Array-Koordinaten
// Definiere ein Array // Die X-Koordinate des Arrays entspricht dem SwitchID-Wert // Die Y-0-Koordinate ist die DoorID // Die Y-1-Koordinate ist der Aktivierungszustand aSwitch (Anzahl der Schalter, 2) // 2 ist die Höhe (Y), oft 0-basiert.
Führen Sie einige Assoziationen der SwitchIDs mit DoorIDs aus Die Türmechanik ist immer noch die gleiche wie im Basisbeispiel. // Anzeige der korrekten Schaltgrafik entsprechend ihrem Aktivierungsstatus Switch.Activated = True Anzeige des Animationsframes Switch_ON Switch.Activated = False Anzeige des Animationsframes Switch_OFF Tastaturtaste "Leertaste" gedrückt und Abstand (Zeichen, Schalter) <= 64 Switch.Toggle(Activated) //A function that will set the value to either True or False) aSwitch(Switch.SwitchID,SWITCH_ACTIVATION) = Switch.Activated //It can depend on your coding language, but the idea is to set the value in the array where X is the SwitchID and where Y is the state of activation of the switch. The value itself is supposed to be the equivalent of the Switch.Activated boolean value. Door.DoorID = aSwitch(Switch.SwitchID,SWITCH.DOORID) //Allows us to make sure we're applying/selecting the correct door instance //Now according to the activation value, we lock or unlock the door aSwitch(Switch.SwitchID,SWITCH.ACTIVATION) = True Door.Locked = False aSwitch(Switch.SwitchID,SWITCH.ACTIVATION) = False Door.Locked = True
Für dieses spezielle Beispiel, in dem sich die Schalter in demselben Raum befinden wie die Türen, mit denen sie verbunden sind, ist die Array-Technik übertrieben. Wenn Ihr Spiel so eingerichtet ist, dass jeder auf eine Tür wirkende Schalter im selben Raum positioniert wird, gehen Sie auf alle Fälle für die einfachere Methode vor, entfernen Sie das Array und prüfen Sie, ob Objekte aktiv sind nur Bildschirm.
Plattenschalter ähneln Schaltern in dem Sinne, dass sie entweder aktiviert sind oder nicht, und dass wir sie mit Türen verbinden können, um sie zu verriegeln oder zu entriegeln. Der Unterschied besteht darin, wie ein Plattenschalter durch Druck aktiviert wird.
In diesem Beispiel der Ansicht von oben nach unten wird der Plattenschalter aktiviert, wenn der Charakter ihn überlappt. Sie können die Leertaste drücken, um einen Stein auf den Plattenschalter fallen zu lassen, der aktiviert bleibt, auch wenn der Charakter nicht darauf steht.
Die Implementierung hiervon ähnelt dem vorherigen Beispiel mit zwei kleinen Modifikationen:
// Der Großteil der Implementierung ist derselbe wie im vorherigen Beispiel. Ersetzen Sie das Switch-Objekt durch das PlateSwitch-Objekt. // Plate-Switch-Mechanic-Zeichen ODER Rocks überschneiden NICHT PlateSwitch.Activated = False aSwitch (PlateSwitch.SwitchID, SWITCH_ACTIVATION) = PlateSwitch.Activated Door.DoorID = aSwitch (PlateSwitch.SwitchID, SWITCH.DOORID) // Damit können wir sicherstellen, dass wir die richtige Türinstanz anwenden / auswählen. Door.Locked = Wahres Zeichen ODER Rock überlappt PlateSwitch.Activated = True aSwitch (PlateSwitch .SwitchID, SWITCH_ACTIVATION) = PlateSwitch.Activated Door.DoorID = aSwitch (PlateSwitch.SwitchID, SWITCH.DOORID) // Erlaubt uns sicherzustellen, dass wir die richtige Türinstanz anwenden / auswählen. Door.Locked = False Tastaturtaste "Leertaste" wird gedrückt und der Charakter überlappt PlateSwitch Spawn Rock bei PlateSwitch.position
Ein anderer möglicher Sperrmechaniker besteht darin, dass der Spieler alle Feinde (auch Mobs genannt) in einem Raum oder in einem Bereich loswerden muss, um das Entriegeln der Türen auszulösen.
In diesem Beispiel habe ich einige Bereiche in einem einzigen Raum erstellt. Jeder Bereich hat eine Tür und mehrere Mobs (obwohl sich diese Feinde nicht bewegen und keinen Schaden verursachen).
Jeder Bereich hat eine eigene Farbe.
Die Leertaste lässt den Charakter einige Geschosse abfeuern; Drei Projektile töten einen Mob.
Diese Art von Mechanik wird in The Legend of Zelda und The Binding of Isaac verwendet und dreht sich um eine Funktion, die die Anzahl der lebenden Feinde im Raum oder in der Umgebung prüft. In diesem Beispiel enthält jeder farbige Bereich eine Zählung der lebenden Mobs, die eingeleitet wird, wenn der Raum geladen wird und an die Tür gebunden ist. Der Tod eines jeden Mobs zieht ab 1
von diesem Zähler; sobald es fällt 0
, die Türen Verschlossen
Zustand wird in geändert Falsch
.
// Beim Start des Spiels Für jeden Bereich Für jeden Mob, der Area überlappt Area.AliveMobs = Area.AliveMobs + 1
Der Türmechaniker ist derselbe wie im Grundbeispiel
Tastaturtaste "Leertaste" wird gedrückt Spawn ein Projektil aus der Position des Charakters Projektil kollidiert mit Mob Mob.HP = Mob.HP - 1 Zerstöre Projektil Mob.HP <=0 //Mob is dead and Mob is overlapping Area Destroy Mob Area.AliveMobs = Area.AliveMobs - 1 Area.AliveMobs <= 0 and Door is linked to Area //By means of an ID, a pointer or whatever Door.Locked = False
In diesem Beispiel ein Bereich
ist ein farbiges Sprite mit einer zugehörigen numerischen Variablen, AliveMobs
, was zählt die Anzahl der Mobs, die den Bereich überlappen. Sobald alle Mobs in einem Gebiet besiegt sind, wird die entsprechende Tür entriegelt (mit derselben Mechanik, die wir seit dem Grundbeispiel gesehen haben)..
Wie ich bereits in der Einleitung erwähnt habe, können Türen als blockierende Hindernisse wirken, sie können aber auch dazu dienen, dass der Charakter des Spielers von einem Raum zu einem anderen navigiert.
In diesem Beispiel werden Türen standardmäßig entriegelt, da wir uns mehr für den Navigationsaspekt interessieren.
Der Mechaniker hängt sehr stark von dem Spiel ab, das Sie erstellen, und davon, wie Sie mit der Datenstruktur für Ihre Etagen umgehen. Ich werde nicht näher darauf eingehen, wie meine Implementierung funktioniert, da sie sehr spezifisch für Construct 2 ist, aber Sie können sie in den Quelldateien finden, wenn Sie möchten.
In diesem Artikel haben wir gesehen, wie Türen temporäre Hindernisse sind, für die Schlüssel oder Entriegelungsmechanismen erforderlich sind, z. B. Schalter, Plattenschalter oder sogar der Tod von Mobs. Wir haben auch gesehen, wie sie als "Brücken" fungieren können und die Navigation durch verschiedene Bereiche der Spielwelt ermöglichen.
Zur Erinnerung, hier einige mögliche Sperrmechaniken:
Wenn Sie alle diese Mechaniken in einem Spiel gemischt haben, könnte dies zu folgendem Ergebnis führen:
Hier haben wir eine schöne Auswahl an verschiedenen Tür- und Schlossmechaniken, wobei der Spieler durch mehrere Räume gehen muss, um die verschiedenen Türen zu öffnen. Zu Lernzwecken möchten Sie dies möglicherweise in Ihrer eigenen Programmierumgebung unter Verwendung aller bisherigen Implementierungen reproduzieren.
Ich hoffe, dass Ihnen dieser Artikel gefallen hat und dass er für Sie nützlich war, und ich möchte daran erinnern, dass Sie die Quelle für alle Demos auf Github finden können. Sie können sie in der kostenlosen Version von Construct 2 (Version r164.2 oder höher) öffnen und bearbeiten..