Pflanze ist ein Referenzspiel, in dem Spieler Samen pflanzen und gießen, damit sie später die resultierenden Pflanzen ernten und verkaufen können.

Das Projekt konzentriert sich auf häufige Anwendungsfälle, die Sie beim Entwickeln eines Spiels auf Roblox antreffen könnten. Wo anwendbar, finden Sie Hinweise zu Kompromissen, Abwägungen und der Begründung verschiedener Implementierungsentscheidungen, damit Sie die beste Entscheidung für Ihre eigenen Spiele treffen können.
Datei herunterladen
- Navigieren Sie zur Pflanze Spielseite.
- Klicken Sie auf die ⋯-Schaltfläche und Im Studio bearbeiten.
Anwendungsfälle
Pflanze deckt die folgenden Anwendungsfälle ab:
- Sitzungsdaten und Persistenz von Spieldaten
- Verwaltung der UI-Ansicht
- Client-Server-Netzwerk
- Erstmalige Benutzererfahrung (FTUE)
- Käufe mit harter und weicher Währung
Darüber hinaus löst dieses Projekt engere Problemstellungen, die auf viele Spiele anwendbar sind, einschließlich:
- Anpassung eines Bereichs im Ort, der mit einem Spieler verbunden ist
- Verwaltung der Bewegungsgeschwindigkeit des Spielercharakters
- Erstellen eines Objekts, das Charaktere verfolgt
- Erkennen, in welchem Teil der Welt sich ein Charakter befindet
Bitte beachten Sie, dass es in diesem Spiel mehrere Anwendungsfälle gibt, die zu klein, zu nischenspezifisch oder nicht interessant genug sind, um eine Lösung für eine interessante Designherausforderung zu demonstrieren; diese werden nicht behandelt.
Projektstruktur
Die erste Entscheidung beim Erstellen eines Spiels besteht darin, wie man das Projekt strukturiert, was hauptsächlich umfasst, wo spezifische Instanzen im Datenmodell platziert werden und wie man Einstiegspunkte für sowohl Client- als auch Servercode organisiert und strukturiert.
Datenmodell
Die folgende Tabelle beschreibt, in welchen Container-Diensten die Instanzen im Datenmodell platziert sind.
| Dienst | Arten von Instanzen |
|---|---|
| Workspace | Enthält statische Modelle, die die 3D-Welt darstellen, insbesondere Teile der Welt, die keinem Spieler gehören. Sie müssen diese Instanzen zur Laufzeit nicht dynamisch erstellen, ändern oder zerstören, daher ist es akzeptabel, sie hier zu belassen. Es gibt auch einen leeren Folder, in den die Farmmodelle der Spieler zur Laufzeit hinzugefügt werden. |
| Lighting | Atmosphärische und Lichteffekte. |
| ReplicatedFirst | Enthält die kleinste mögliche Teilmenge von Instanzen, die benötigt wird, um den Ladebildschirm anzuzeigen und das Spiel zu initialisieren. Je mehr Instanzen in ReplicatedFirst platziert werden, desto länger dauert es, bis sie repliziert werden, bevor der Code in ReplicatedFirst ausgeführt werden kann.
|
| ReplicatedStorage | Dient als Speichercontainer für alle Instanzen, auf die sowohl vom Client als auch vom Server zugegriffen werden muss.
|
| ServerScriptService | Enthält ein Script, das als Einstiegspunkt für allen serverseitigen Code im Projekt dient. |
| ServerStorage | Dient als Speichercontainer für alle Instanzen, die nicht an den Client repliziert werden müssen.
|
| SoundService | Enthält die Sound-Objekte, die für Soundeffekte im Spiel verwendet werden. Unter SoundService haben diese Sound-Objekte keine Position und werden nicht im 3D-Raum simuliert. |
Einstiegspunkte
Die meisten Projekte organisieren Code in wiederverwendbaren ModuleScripts, die im gesamten Codebasis importiert werden können. ModuleScripts sind wiederverwendbar, führen sich jedoch nicht selbst aus; sie müssen von einem Script oder LocalScript importiert werden. Viele Roblox-Projekte haben eine große Anzahl von Script- und LocalScript-Objekten, die jeweils einem Verhalten oder einem bestimmten System im Spiel zugeordnet sind, wodurch mehrere Einstiegspunkte entstehen.
Für das Pflanze Mikrogame wird ein anderer Ansatz durch ein einzelnes LocalScript implementiert, das der Einstiegspunkt für allen Client-Code ist, und ein einzelnes Script, das der Einstiegspunkt für allen Server-Code ist. Der richtige Ansatz für Ihr Projekt hängt von Ihren Anforderungen ab, aber ein einzelner Einstiegspunkt bietet eine größere Kontrolle über die Reihenfolge, in der Systeme ausgeführt werden.
Die folgenden Listen beschreiben die Abwägungen beider Ansätze:
- Ein einzelnes Script und ein einzelnes LocalScript decken server- und clientseitigen Code ab.
- Größere Kontrolle über die Reihenfolge, in der verschiedene Systeme gestartet werden, da der gesamte Code von einem einzigen Skript initialisiert wird.
- Objekte können durch Referenz zwischen Systemen übergeben werden.
Hochrangige Systemarchitektur
Die obersten Systeme im Projekt sind unten detailliert. Einige dieser Systeme sind erheblich komplexer als andere, und in vielen Fällen wird ihre Funktionalität über eine Hierarchie anderer Klassen abstrahiert.

Jedes dieser Systeme ist ein "Singleton", da es sich um eine nicht instanziierbare Klasse handelt, die stattdessen durch das relevante Client- oder Server-start-Skript initialisiert wird. Sie können später in diesem Leitfaden mehr über das Singleton-Muster lesen.
Server
Die folgenden Systeme sind mit dem Server verbunden.
| System | Beschreibung |
|---|---|
| Netzwerk |
|
| PlayerDataServer |
|
| Markt |
|
| CollisionGroupManager |
|
| FarmManagerServer |
|
| PlayerObjectsContainer |
|
| TagPlayers |
|
| FtueManagerServer |
|
| CharacterSpawner |
|
Client
Die folgenden Systeme sind mit dem Client verbunden.
| System | Beschreibung |
|---|---|
| Netzwerk |
|
| PlayerDataClient |
|
| MarketClient |
|
| LocalWalkJumpManager |
|
| FarmManagerClient |
|
| UISetup |
|
| FtueManagerClient |
|
| CharacterSprint |
|
Client-Server-Kommunikation
Die meisten Roblox-Spiele beinhalten ein gewisses Element der Kommunikation zwischen Client und Server. Dies kann das Anfordern des Servers umfassen, eine bestimmte Aktion auszuführen, und der Server repliziert Updates an den Client.
In diesem Projekt wird die Client-Server-Kommunikation so allgemein wie möglich gehalten, indem die Verwendung von RemoteEvent- und RemoteFunction-Objekten eingeschränkt wird, um die Anzahl der speziellen Regeln, die zu beachten sind, zu verringern. Dieses Projekt verwendet die folgenden Methoden, in der Reihenfolge der Präferenz:
- Replikation über das Spielerdaten-System.
- Replikation über Attribute.
- Replikation über Tags.
- Messaging direkt über das Netzwerk-Modul.
Replikation über das Spielerdaten-System
Das Spielerdaten-System ermöglicht es, Daten mit dem Spieler zu verknüpfen, die zwischen Speichersitzungen bestehen bleiben. Dieses System bietet Replikation vom Client zum Server und eine Reihe von APIs, die verwendet werden können, um Daten abzufragen und Änderungen zu abonnieren, was es ideal macht, um Änderungen am Spielerstatus vom Server an den Client zu replizieren.
Zum Beispiel, anstatt ein maßgeschneidertes UpdateCoins RemoteEvent auszulösen, um dem Client mitzuteilen, wie viele Münzen er hat, können Sie Folgendes aufrufen und den Client über das PlayerDataClient.updated-Ereignis abonnieren lassen.
PlayerDataServer.setValue(player, "coins", 5)Natürlich ist dies nur nützlich für die Replikation vom Server zum Client und für Werte, die zwischen Sitzungen bestehen bleiben sollen, aber dies gilt für eine überraschend große Anzahl von Fällen im Projekt, einschließlich:
- Die aktuelle FTUE-Phase
- Das Inventar des Spielers
- Die Anzahl der Münzen, die der Spieler hat
- Der Status der Farm des Spielers
Replikation über Attribute
In Situationen, in denen der Server einen benutzerdefinierten Wert an den Client replizieren muss, der spezifisch für eine bestimmte Instance ist, können Sie Attribute verwenden. Roblox repliziert Attributwerte automatisch, sodass Sie keine Codepfade aufrechterhalten müssen, um den Status, der mit einem Objekt verbunden ist, zu replizieren. Ein weiterer Vorteil ist, dass diese Replikation zusammen mit der Instanz selbst erfolgt.
Dies ist besonders nützlich für zur Laufzeit erstellte Instanzen, da Attribute, die auf einer neuen Instanz gesetzt werden, bevor sie dem Datenmodell zugeordnet wird, atomar mit der Instanz selbst repliziert werden. Dies umgeht die Notwendigkeit, Code zu schreiben, um auf zusätzliche Daten zu warten, die über ein RemoteEvent oder StringValue repliziert werden.
Sie können Attribute auch direkt aus dem Datenmodell, sowohl vom Client als auch vom Server, mit der Methode GetAttribute() lesen und Änderungen mit der Methode GetAttributeChangedSignal() abonnieren. Im Pflanze Projekt wird dieser Ansatz unter anderem verwendet, um den aktuellen Status der Pflanzen an die Clients zu replizieren.
Replikation über Tags
CollectionService ermöglicht es Ihnen, ein String-Tag auf eine Instance anzuwenden. Dies ist nützlich, um Instanzen zu kategorisieren und diese Kategorisierung an den Client zu replizieren.
Zum Beispiel wird das CanPlant-Tag auf dem Server angewendet, um dem Client anzuzeigen, dass ein bestimmter Topf in der Lage ist, eine Pflanze zu empfangen.
Direktes Messaging über das Netzwerkmodul
Für Situationen, in denen keine der vorherigen Optionen zutrifft, können Sie benutzerdefinierte Netzwerkaufrufe über das Netzwerk-Modul verwenden. Dies ist die einzige Option im Projekt, die die Kommunikation vom Client zum Server ermöglicht und daher am nützlichsten ist, um Clientanfragen zu übertragen und eine Serverantwort zu erhalten.
Pflanze verwendet direkte Netzwerkaufrufe für eine Vielzahl von Clientanfragen, einschließlich:
- Gießen einer Pflanze
- Pflanzen eines Samens
- Kauf eines Artikels
Der Nachteil dieses Ansatzes besteht darin, dass jede einzelne Nachricht eine maßgeschneiderte Konfiguration erfordert, die die Komplexität des Projekts erhöhen kann, obwohl dies wo immer möglich vermieden wurde, insbesondere für die Kommunikation vom Server zum Client.
Klassen und Singletons
Klassen im Pflanze Projekt, wie Instanzen auf Roblox, können erstellt und zerstört werden. Ihre Klassensyntax ist inspiriert von dem idiomatischen Lua-Ansatz für objektorientierte Programmierung mit einer Reihe von Änderungen, um die Unterstützung für strikte Typprüfung zu ermöglichen.
Instanziierung
Viele Klassen im Projekt sind mit einer oder mehreren Instances verbunden. Objekte einer bestimmten Klasse werden mit einer new()-Methode erstellt, die mit der Art und Weise übereinstimmt, wie Instanzen in Roblox mit Instance.new() erstellt werden.
Dieses Muster wird im Allgemeinen für Objekte verwendet, bei denen die Klasse eine physische Darstellung im Datenmodell hat, und die Klasse erweitert ihre Funktionalität. Ein gutes Beispiel ist BeamBetween, das ein Beam-Objekt zwischen zwei gegebenen Attachment-Objekten erstellt und diese Anhänge so orientiert, dass der Strahl immer nach oben zeigt. Diese Instanzen könnten von einer vorgefertigten Version in ReplicatedStorage geklont oder als Argument in new() übergeben und im Objekt unter self gespeichert werden.
Entsprechende Instanzen
Wie oben erwähnt, haben viele Klassen in diesem Projekt eine Datenmodellrepräsentation, eine Instanz, die mit der Klasse übereinstimmt und von ihr manipuliert wird.
Anstatt diese Instanzen zu erstellen, wenn ein Klassenobjekt instanziiert wird, entscheidet sich der Code im Allgemeinen dafür, eine vorgefertigte Version der Instance unter ReplicatedStorage oder ServerStorage zu Clone(). Obwohl es möglich wäre, die Eigenschaften dieser Instanzen zu serialisieren und sie in den new()-Funktionen der Klasse von Grund auf zu erstellen, würde dies das Bearbeiten der Objekte sehr umständlich machen und es für einen Leser schwieriger machen, sie zu verstehen. Darüber hinaus ist das Klonen einer Instanz im Allgemeinen eine schnellere Operation als das Erstellen einer neuen Instanz und das Anpassen ihrer Eigenschaften zur Laufzeit.
Komposition
Obwohl Vererbung in Luau mithilfe von Metatabellen möglich ist, entscheidet sich das Projekt stattdessen dafür, Klassen durch Komposition zu erweitern. Bei der Kombination von Klassen durch Komposition wird das "Kind"-Objekt in der new()-Methode der Klasse instanziiert und als Mitglied unter self aufgenommen.
Ein Beispiel dafür in Aktion ist die CloseButton-Klasse, die die Button-Klasse umschließt.
Bereinigung
Ähnlich wie eine Instance mit der Methode Destroy() zerstört werden kann, können auch Klassen, die instanziiert werden können, zerstört werden. Die Destruktormethode für Projektklassen ist destroy() mit einem kleinen d für die Konsistenz von camelCase über die Methoden des Codes, sowie um zwischen den Klassen des Projekts und den Roblox-Instanzen zu unterscheiden.
Die Rolle der destroy()-Methode besteht darin, alle Instanzen, die vom Objekt erstellt wurden, zu zerstören, alle Verbindungen zu trennen und destroy() für alle Kindobjekte aufzurufen. Dies ist besonders wichtig für Verbindungen, da Instanzen mit aktiven Verbindungen nicht vom Luau-Garbage-Collector bereinigt werden, selbst wenn keine Referenzen auf die Instanz oder Verbindungen zur Instanz mehr bestehen.
Singletons
Singletons, wie der Name schon sagt, sind Klassen, für die nur ein Objekt existieren kann. Sie sind das Äquivalent des Projekts zu den Diensten von Roblox. Anstatt eine Referenz auf das Singleton-Objekt zu speichern und es im Luau-Code weiterzugeben, nutzt Pflanze die Tatsache, dass das Erfordern eines ModuleScript seinen zurückgegebenen Wert zwischenspeichert. Das bedeutet, dass das Erfordern des gleichen Singleton ModuleScript aus verschiedenen Orten konsistent dasselbe zurückgegebene Objekt bereitstellt. Die einzige Ausnahme von dieser Regel wäre, wenn unterschiedliche Umgebungen (Client oder Server) auf das ModuleScript zugreifen.
Singletons unterscheiden sich von instanziierbaren Klassen dadurch, dass sie keine new()-Methode haben. Stattdessen wird das Objekt zusammen mit seinen Methoden und seinem Zustand direkt über das ModuleScript zurückgegeben. Da Singletons nicht instanziiert werden, wird die self-Syntax nicht verwendet und Methoden werden stattdessen mit einem Punkt (.) anstelle eines Doppelpunktes (:) aufgerufen.
Strikte Typinferenz
Luau unterstützt schrittweises Typing, was bedeutet, dass Sie optional Typdefinitionen zu einem Teil oder zu allem Ihrem Code hinzufügen können. In diesem Projekt wird für jedes Skript eine strikte Typprüfung verwendet. Dies ist die am wenigsten permissive Option für das Script Analysis-Tool von Roblox und daher am wahrscheinlichsten, Typfehler vor der Laufzeit zu erkennen.
Typisierte Klassensyntax
Der etablierte Ansatz zur Erstellung von Klassen in Lua ist gut dokumentiert, jedoch nicht gut geeignet für starkes Luau-Typing. In Luau ist der einfachste Ansatz, um den Typ einer Klasse zu erhalten, die Methode typeof():
type ClassType = typeof(Class.new())Das funktioniert, ist aber nicht sehr nützlich, wenn Ihre Klasse mit Werten initialisiert wird, die nur zur Laufzeit existieren, zum Beispiel Player-Objekte. Darüber hinaus geht die Annahme, die in der idiomatischen Lua-Klassensyntax getroffen wird, davon aus, dass die Deklaration einer Methode auf einer Klasse self immer eine Instanz dieser Klasse sein wird; dies ist keine Annahme, die die Typinferenz-Engine treffen kann.
Um die strikte Typinferenz zu unterstützen, verwendet das Pflanze Projekt eine Lösung, die sich in mehreren Punkten von der idiomatischen Lua-Klassensyntax unterscheidet, von denen einige möglicherweise nicht intuitiv erscheinen:
- Die Definition von self wird sowohl in der Typdeklaration als auch im Konstruktor dupliziert. Dies führt zu einer Wartungsbelastung, aber Warnungen werden angezeigt, wenn die beiden Definitionen nicht mehr synchron sind.
- Klassenmethoden werden mit einem Punkt deklariert, sodass self explizit als ClassType deklariert werden kann. Methoden können weiterhin wie erwartet mit einem Doppelpunkt aufgerufen werden.
--!strict
local MyClass = {}
MyClass.__index = MyClass
export type ClassType = typeof(setmetatable(
{} :: {
property: number,
},
MyClass
))
function MyClass.new(property: number): ClassType
local self = {
property = property,
}
setmetatable(self, MyClass)
return self
end
function MyClass.addOne(self: ClassType)
self.property += 1
end
return MyClassTypen nach logischen Wächtern umwandeln
Zum Zeitpunkt des Schreibens wird der Typ eines Wertes nach einer Wächterbedingung nicht eingegrenzt. Zum Beispiel wird der Typ von optionalParameter nach dem Wächter unten nicht eingegrenzt.
--!strict
local function foo(optionalParameter: number?)
if not optionalParameter then
return
end
print(optionalParameter + 1)
endUm dies zu mildern, werden nach diesen Wächtern neue Variablen mit ihrem Typ explizit umgewandelt.
--!strict
local function foo(optionalParameter: number?)
if not optionalParameter then
return
end
local parameter = optionalParameter :: number
print(parameter + 1)
endTraversieren von DataModel-Hierarchien
In einigen Fällen muss die Codebasis die Datenmodellhierarchie eines Baums von Objekten durchlaufen, die zur Laufzeit erstellt werden. Dies stellt eine interessante Herausforderung für die Typprüfung dar. Zum Zeitpunkt des Schreibens ist es nicht möglich, eine generische Datenmodellhierarchie als Typ zu definieren. Daher gibt es Fälle, in denen die einzigen Typinformationen, die für eine Datenmodellstruktur verfügbar sind, der Typ der obersten Instanz sind.
Ein Ansatz für diese Herausforderung besteht darin, auf any zu casten und dann zu verfeinern. Zum Beispiel:
local function enableVendor(vendor: Model)
local zonePart: BasePart = (vendor :: any).ZonePart
endDas Problem mit diesem Ansatz ist, dass er die Lesbarkeit beeinträchtigt. Stattdessen verwendet das Projekt ein generisches Modul namens getInstance, um Datenmodellhierarchien zu durchlaufen, das intern auf any castet.
local function enableVendor(vendor: Model)
local zonePart: BasePart = getInstance(vendor, "ZonePart")
endDa das Verständnis der Typ-Engine für das Datenmodell sich weiterentwickelt, ist es möglich, dass Muster wie dieses nicht mehr notwendig sein werden.
Benutzeroberfläche
Pflanze umfasst eine Vielzahl von komplexen und einfachen 2D-Benutzeroberflächen. Dazu gehören nicht-interaktive Heads-up-Display (HUD)-Elemente wie der Münzzähler und komplexe interaktive Menüs wie der Shop.
UI-Ansatz
Sie können Roblox UI grob mit dem HTML DOM vergleichen, da es sich um eine Hierarchie von Objekten handelt, die beschreiben, was der Benutzer sehen sollte. Ansätze zur Erstellung und Aktualisierung einer Roblox-UI werden grob in imperative und declarative Praktiken unterteilt.
| Ansatz | Vorteile und Nachteile |
|---|---|
| Imperativ | Im imperativen Ansatz wird die UI wie jede andere Instanzhierarchie auf Roblox behandelt. Die UI-Struktur wird vor der Laufzeit im Studio erstellt und dem Datenmodell hinzugefügt, typischerweise direkt in StarterGui. Dann manipuliert der Code zur Laufzeit spezifische Teile der UI, um den Zustand widerzuspiegeln, den der Ersteller benötigt. Dieser Ansatz hat einige Vorteile. Sie können die UI von Grund auf im Studio erstellen und im Datenmodell speichern. Dies ist eine einfache und visuelle Bearbeitungserfahrung, die die Erstellung von UI beschleunigen kann. Da imperativer UI-Code sich nur um das kümmert, was geändert werden muss, macht es auch einfache UI-Änderungen leicht umsetzbar. Ein bemerkenswerter Nachteil ist, dass, da imperativen UI-Ansätze erfordern, dass der Zustand manuell in Form von Transformationen implementiert wird, komplexe Darstellungen des Zustands sehr schwer zu finden und zu debuggen sein können. Es ist üblich, dass Fehler beim Entwickeln von imperativem UI-Code auftreten, insbesondere wenn der Zustand und die UI aufgrund mehrerer Updates, die in unerwarteter Reihenfolge interagieren, desynchronisiert werden. Eine weitere Herausforderung bei imperativen Ansätzen besteht darin, dass es schwieriger ist, die UI in sinnvolle Komponenten zu zerlegen, die einmal deklariert und wiederverwendet werden können. Da der gesamte UI-Baum zur Bearbeitungszeit deklariert wird, können häufige Muster in mehreren Teilen des Datenmodells wiederholt werden. |
| Deklarativ | Im deklarativen Ansatz werden die gewünschten Zustände von UI-Instanzen explizit deklariert, und die effiziente Implementierung dieses Zustands wird durch Bibliotheken wie Roact oder Fusion abstrahiert. Der Vorteil dieses Ansatzes ist, dass die Implementierung des Zustands trivial wird und Sie nur beschreiben müssen, wie Ihre UI aussehen soll. Dies macht das Identifizieren und Beheben von Fehlern erheblich einfacher. Der Hauptnachteil besteht darin, dass die gesamte UI-Hierarchie im Code deklariert werden muss. Bibliotheken wie Roact und Fusion haben eine Syntax, um dies zu erleichtern, aber es ist immer noch ein zeitaufwändiger Prozess und eine weniger intuitive Bearbeitungserfahrung beim Komponieren von UI. |
Pflanze verwendet einen imperativen Ansatz unter der Annahme, dass das direkte Anzeigen der Transformationen einen effektiveren Überblick darüber gibt, wie UI auf Roblox erstellt und manipuliert wird. Dies wäre mit einem deklarativen Ansatz nicht möglich. Einige wiederholte UI-Strukturen und -Logik werden auch in wiederverwendbare Komponenten abstrahiert, um eine häufige Falle im Design imperativer UI zu vermeiden.
Hochrangige Architektur

Schicht und Komponenten
In Pflanze sind alle UI-Strukturen entweder eine Layer oder eine Component.
- Layer wird als eine oberste Gruppierungssingleton definiert, die vorgefertigte UI-Strukturen in ReplicatedStorage umschließt. Eine Schicht kann eine Anzahl von Komponenten enthalten oder ihre eigene Logik vollständig kapseln. Beispiele für Schichten sind das Inventarmenü oder der Münzindikator im Heads-up-Display.
- Component ist ein wiederverwendbares UI-Element. Wenn ein neues Komponentenobjekt instanziiert wird, klont es eine vorgefertigte Vorlage aus ReplicatedStorage. Komponenten können selbst andere Komponenten enthalten. Beispiele für Komponenten sind eine generische Schaltflächenklasse oder das Konzept einer Liste von Elementen.
Ansichtshandhabung
Ein häufiges Problem bei der UI-Verwaltung ist die Ansichtshandhabung. Dieses Projekt hat eine Reihe von Menüs und HUD-Elementen, von denen einige auf Benutzereingaben hören, und eine sorgfältige Verwaltung, wann sie sichtbar oder aktiviert sind, ist erforderlich.
Pflanze geht dieses Problem mit seinem UIHandler-System an, das verwaltet, wann eine UI-Schicht sichtbar sein sollte oder nicht. Alle UI-Schichten im Spiel werden als HUD oder Menu kategorisiert, und ihre Sichtbarkeit wird durch die folgenden Regeln verwaltet:
- Der aktivierte Zustand von Menu- und HUD-Schichten kann umgeschaltet werden.
- Aktivierte HUD-Schichten werden nur angezeigt, wenn keine Menu-Schichten aktiviert sind.
- Aktivierte Menu-Schichten werden in einem Stapel gespeichert, und nur eine Menu-Schicht ist zur gleichen Zeit sichtbar. Wenn eine Menu-Schicht aktiviert wird, wird sie an die Spitze des Stapels eingefügt und angezeigt. Wenn eine Menu-Schicht deaktiviert wird, wird sie aus dem Stapel entfernt und die nächste aktivierte Menu-Schicht in der Warteschlange wird angezeigt.
Dieser Ansatz ist intuitiv, da er es ermöglicht, Menüs mit Verlauf zu navigieren. Wenn ein Menü aus einem anderen Menü geöffnet wird, wird beim Schließen des neuen Menüs das alte Menü wieder angezeigt.
UI-Schicht-Singletons registrieren sich beim UIHandler und erhalten ein Signal, das ausgelöst wird, wenn sich ihre Sichtbarkeit ändern sollte.
Weiterführende Literatur
Von diesem umfassenden Überblick über das Pflanze Projekt möchten Sie möglicherweise die folgenden Leitfäden erkunden, die tiefer in verwandte Konzepte und Themen eintauchen.
- Client-Server-Modell — Ein Überblick über das Client-Server-Modell in Roblox.
- Remote-Events und Rückrufe — Alles über Remote-Netzwerkereignisse und Rückrufe zur Kommunikation über die Client-Server-Grenze.
- UI — Details zu Benutzeroberflächenobjekten und -design auf Roblox.