Plant ist eine Referenz-Erfahrung, bei der Spieler Samen pflanzen und gießen, um anschließend die resultierenden Pflanzen zu ernten und zu verkaufen.

Das Projekt konzentriert sich auf häufige Anwendungsfälle, die Sie beim Entwickeln einer Erfahrung auf Roblox antreffen könnten. Wo zutreffend, finden Sie Hinweise zu Trade-offs, Kompromissen und den Gründen für verschiedene Implementierungsentscheidungen, damit Sie die besten Entscheidungen für Ihre eigenen Erfahrungen treffen können.
Datei herunterladen
- Navigieren Sie zur Plant-Erfahrungsseite.
- Klicken Sie auf die ⋯-Schaltfläche und Im Studio bearbeiten.
Anwendungsfälle
Plant behandelt die folgenden Anwendungsfälle:
- Sitzungsdaten und Persistenz von Spieldaten
- Verwaltung der Benutzeroberflächenansicht
- Client-Server-Netzwerkverbindungen
- Nutzererfahrung für Erstbenutzer (FTUE)
- Käufe von hartem und weichem Währungsgeld
Zusätzlich löst dieses Projekt engere Problembereiche, die auf viele Erfahrungen anwendbar sind, einschließlich:
- Anpassung eines Bereichs im Spiel, der mit einem Spieler verbunden ist
- Verwaltung der Bewegungsgeschwindigkeit des Spielercharakters
- Erstellen eines Objekts, das Charaktere verfolgt
- Erkennen, in welchem Bereich der Welt ein Charakter sich befindet
Beachten Sie, dass es in dieser Erfahrung mehrere Anwendungsfälle gibt, die zu klein, zu nischenspezifisch sind oder keine Lösung für eine interessante Designherausforderung demonstrieren; diese sind nicht behandelt.
Projektstruktur
Die erste Entscheidung bei der Erstellung einer Erfahrung besteht darin, die Projektstruktur zu entscheiden, die hauptsächlich umfasst, wo spezifische Instanzen im Datenmodell platziert sind und wie die Einstiegspunkte für Client- und Servercode organisiert werden.
Datenmodell
Die folgende Tabelle beschreibt, in welchen Containerdiensten im Datenmodell Instanzen platziert sind.
| Dienst | Arten von Instanzen |
|---|---|
| Workspace | Beinhaltet statische Modelle, die die 3D-Welt repräsentieren, 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 | Beinhaltet die kleinste mögliche Teilmenge von Instanzen, die benötigt wird, um den Ladebildschirm anzuzeigen und die Erfahrung zu initialisieren. Je mehr Instanzen in ReplicatedFirst platziert sind, desto länger dauert das Warten, bis sie repliziert werden, bevor Code in ReplicatedFirst ausgeführt werden kann.
|
| ReplicatedStorage | Serviert als Speichercontainer für alle Instanzen, auf die sowohl vom Client als auch vom Server zugegriffen werden muss.
|
| ServerScriptService | Beinhaltet ein Script, das als Einstiegspunkt für allen serverseitigen Code im Projekt dient. |
| ServerStorage | Serviert 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 in der Erfahrung verwendet werden. Unter SoundService haben diese Sound-Objekte keine Position und werden nicht in 3D-Raum simuliert. |
Einstiegspunkte
Die meisten Projekte organisieren Code innerhalb wiederverwendbarer ModuleScripts, die im gesamten Codebasis importiert werden können. ModuleScripts sind wiederverwendbar, führen aber nicht eigenständig 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 in der Erfahrung zugeordnet sind und mehrere Einstiegspunkte schaffen.
Für das Plant Mikrospiel wird ein anderer Ansatz durch ein einzelnes LocalScript implementiert, das der Einstiegspunkt für gesamten Clientcode ist, und ein einzelnes Script, das der Einstiegspunkt für gesamten Servercode ist. Der richtige Ansatz für Ihr Projekt hängt von Ihren Anforderungen ab, aber ein einziger Einstiegspunkt bietet eine bessere Kontrolle über die Reihenfolge, in der Systeme ausgeführt werden.
Die folgenden Listen zeigen die Trade-offs beider Ansätze:
- Ein einzelnes Script und ein einzelnes LocalScript decken jeweils Server- und Clientcode 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 zwischen Systemen per Referenz übergeben werden.
Hochrangige Systemarchitektur
Die obersten Systeme im Projekt sind im Folgenden 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 mehr über das Singleton-Muster später in diesem Leitfaden 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-Erfahrungen beinhalten ein gewisses Element der Kommunikation zwischen dem Client und dem Server. Dies kann das Anfordern eines bestimmten Handlungsablaufs durch den Client und die Replikation von Aktualisierungen an den Client durch den Server umfassen.
In diesem Projekt wird die Client-Server-kommunikation so generisch wie möglich gehalten, indem die Nutzung von RemoteEvent und RemoteFunction-Objekten eingeschränkt wird, um die Menge an speziellen Regeln, die zu beachten sind, zu verringern. Dieses Projekt verwendet die folgenden Methoden, in der Reihenfolge der Vorliebe:
- Replikation über das Spieldaten-System.
- Replikation über Attribute.
- Replikation über Tags.
- Messaging direkt über das Netzwerk-Modul.
Replikation über das Spieldaten-System
Das Spieldaten-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, wodurch es ideal für die Replikation von Änderungen des Spielerstatus vom Server zum Client ist.
Zum Beispiel, anstatt ein maßgeschneidertes UpdateCoins RemoteEvent zu senden, um dem Client mitzuteilen, wie viele Münzen er hat, können Sie Folgendes aufrufen und den Client dies ü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:
- Der aktuelle FTUE-Status
- Das Inventar des Spielers
- Die Anzahl an Münzen, die der Spieler hat
- Der Zustand 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 automatisch Attributwerte, sodass Sie keine Codepfade erstellen müssen, um den mit einem Objekt verbundenen Status zu replizieren. Ein weiterer Vorteil ist, dass diese Replikation zusammen mit der Instanz selbst erfolgt.
Dies ist besonders nützlich für Instanzen, die zur Laufzeit erstellt werden, da Attribute, die auf einer neuen Instanz gesetzt werden, bevor sie im Datenmodell platziert werden, atomar mit der Instanz selbst repliziert werden. Dadurch entfällt 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 vom Datenmodell aus sowohl vom Client als auch vom Server mit der GetAttribute()-Methode lesen und Änderungen mit der GetAttributeChangedSignal()-Methode abonnieren. Im Plant-Projekt wird dieser Ansatz unter anderem verwendet, um den aktuellen Status von Pflanzen an die Clients zu replizieren.
Replikation über Tags
CollectionService ermöglicht es Ihnen, einen String-Tag zu einer 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 eine Pflanze erhalten kann.
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 Client-Anfragen zu übertragen und eine Serverantwort zu erhalten.
Plant verwendet direkte Netzwerkaufrufe für eine Vielzahl von Clientanfragen, einschließlich:
- Gießen einer Pflanze
- Pflanzen eines Samens
- Kauf eines Gegenstands
Der Nachteil dieses Ansatzes ist, dass jede einzelne Nachricht einige maßgeschneiderte Konfigurationen erfordert, was die Komplexität des Projekts erhöhen kann, obwohl dies, wo möglich, vermieden wurde, insbesondere für die Kommunikation vom Server an den Client.
Klassen und Singletons
Klassen im Plant-Projekt können, wie Instanzen in Roblox, erstellt und zerstört werden. Ihre Klassensyntax ist vom idiomatischen Lua-Ansatz des objektorientierten Programmierens inspiriert und enthält eine 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, was konsistent ist mit der Art und Weise, wie Instanzen in Roblox mit Instance.new() erstellt werden.
Dieses Muster wird allgemein für Objekte verwendet, bei denen die Klasse eine physische Darstellung im Datenmodell hat und die Funktionalität der Klasse erweitert. 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 korrespondiert und von dieser manipuliert wird.
Anstatt diese Instanzen zu erstellen, wenn ein Klassenobjekt instanziiert wird, entscheidet sich der Code in der Regel dafür, die Clone()-Methode einer vorgefertigten Version der Instance, die in ReplicatedStorage oder ServerStorage gespeichert ist, zu verwenden. 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, dies zu verstehen. Darüber hinaus ist das Klonen einer Instanz in der Regel eine schnellere Operation als das Erstellen einer neuen Instanz und das Anpassen ihrer Eigenschaften zur Laufzeit.
Zusammensetzung
Obwohl Vererbung in Luau mittels Metatables möglich ist, entscheidet sich das Projekt stattdessen dafür, Klassen durch Zusammensetzung zu erweitern. Wenn Klassen durch Zusammensetzung kombiniert werden, wird das "Kind"-Objekt in der new()-Methode der Klasse instanziiert und ist als Mitglied unter self enthalten.
Ein Beispiel hierfür sehen Sie in der 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 Zerstörungsmethode für Projektklassen ist destroy(), wobei ein kleines d für die camelCase-Konsistenz über die Methoden der Codebasis hinweg verwendet wird, sowie um zwischen den Projektklassen und Roblox-Instanzen zu unterscheiden.
Die Rolle der destroy()-Methode besteht darin, alle von dem Objekt erstellten Instanzen 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 zur Instanz oder Verbindungen zur Instanz verbleiben.
Singletons
Singletons sind, wie der Name schon sagt, Klassen, von denen nur ein Objekt jemals existieren kann. Sie sind das Äquivalent des Projekts zu den Diensten von Roblox. Anstatt eine Referenz zum Singleton-Objekt zu speichern und sie im Luau-Code zu übergeben, nutzt Plant die Tatsache, dass das Erfordern eines ModuleScript seinen Rückgabewert cached. Dies bedeutet, dass das Erfordern des gleichen Singleton-ModuleScript von verschiedenen Stellen aus konstant dasselbe zurückgegebene Objekt bereitstellt. Die einzige Ausnahme von dieser Regel wäre, wenn verschiedene Umgebungen (Client oder Server) auf das ModuleScript zugreifen.
Singletons unterscheiden sich von instanziierbaren Klassen dadurch, dass sie keine new()-Methode haben. Vielmehr 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 über einen Punkt (.) und nicht über einen Doppelpunkt (:) aufgerufen.
Strikte Typinferenz
Luau unterstützt schrittweises Tippen, was bedeutet, dass Sie optionale Typdefinitionen zu einigen oder allen Ihren Code hinzufügen können. In diesem Projekt wird für jedes Skript strikte Typprüfung verwendet. Dies ist die am wenigsten permissive Option für das Script Analysis Tool von Roblox und somit 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 für starkes Luau-typen. In Luau ist der einfachste Ansatz, um den Typ einer Klasse zu erhalten, die Methode typeof():
type ClassType = typeof(Class.new())
Das funktioniert, ist jedoch nicht sehr nützlich, wenn Ihre Klasse mit Werten, die nur zur Laufzeit existieren, initialisiert wird, zum Beispiel Player-Objekten. Darüber hinaus ist die Annahme, die in der idiomatischen Lua-Klassensyntax getroffen wird, dass die Deklaration einer Methode auf einer Klasse self immer eine Instanz dieser Klasse ist; dies ist keine Annahme, die der Typinferenz-Engine gemacht werden kann.
Um die strikte Typinferenz zu unterstützen, verwendet das Plant-Projekt eine Lösung, die sich in mehreren Punkten von der idiomatischen Lua-Klassensyntax unterscheidet und einige davon möglicherweise nicht intuitiv sind:
- Die Definition von self wird dupliziert, sowohl in der Typdeklaration als auch im Konstruktor. Dies bringt eine Wartungsbelastung mit sich, aber Warnungen werden ausgegeben, wenn die beiden Definitionen nicht synchron sind.
- Klassenmethoden werden mit einem Punkt deklariert, sodass self ausdrücklich als Typ 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 MyClass
Typen nach logischen Wächtern umwandeln
Zum Zeitpunkt des Schreibens wird der Typ eines Wertes nach einer Wächterbedingung nicht eingegrenzt. Zum Beispiel ist 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)
end
Um dies zu mildern, werden nach diesen Wächtern neue Variablen erstellt, deren Typ ausdrücklich umgewandelt wird.
--!strict
local function foo(optionalParameter: number?)
if not optionalParameter then
return
end
local parameter = optionalParameter :: number
print(parameter + 1)
end
Traversieren von DataModel-Hierarchien
In einigen Fällen muss die Codebasis die Datenmodellhierarchie eines Baumes von Objekten durchlaufen, die zur Laufzeit erstellt werden. Dies stellt eine interessante Herausforderung für die Typenprü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 einzige Typinformation, die für eine Datenmodellstruktur verfügbar ist, der Typ der höchstgelegenen Instanz ist.
Ein Ansatz zu dieser Herausforderung besteht darin, zu any zu casten und dann zu verfeinern. Zum Beispiel:
local function enableVendor(vendor: Model)
local zonePart: BasePart = (vendor :: any).ZonePart
end
Das Problem mit diesem Ansatz besteht darin, dass die Lesbarkeit beeinträchtigt wird. Stattdessen verwendet das Projekt ein generisches Modul namens getInstance, um die Hierarchien des Datenmodells zu durchlaufen, das intern zu any castet.
local function enableVendor(vendor: Model)
local zonePart: BasePart = getInstance(vendor, "ZonePart")
end
Da das Verständnis der Typ-Engine für das Datenmodell weiterentwickelt wird, könnten Muster wie dieses möglicherweise nicht mehr erforderlich sein.
Benutzeroberfläche
Plant umfasst eine Vielzahl von komplexen und einfachen 2D-Benutzeroberflächen. Dazu gehören nicht interaktive HUD-Elemente wie der Münzzähler und komplexe interaktive Menüs wie das Geschäft.
UI-Ansatz
Sie können Roblox UI lose mit dem HTML DOM vergleichen, da es eine Hierarchie von Objekten ist, 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 bestimmte 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 UI-Erstellung beschleunigen kann. Da sich imperative UI-Codes nur mit dem befassen, was sich ändern muss, sind einfache UI-Änderungen auch leicht umzusetzen. Ein bemerkenswerter Nachteil ist, dass, da imperative UI-Ansätze erfordern, den Zustand manuell in Form von Transformationen einzuführen, komplexe Darstellungen des Zustands sehr schwer zu finden und zu debuggen sein können. Fehler treten häufig auf, wenn imperative UI-Codes entwickelt werden, insbesondere wenn der Zustand und die UI aufgrund mehrerer Updates, die in unerwarteter Reihenfolge interagieren, desynchronisiert werden. Eine weitere Herausforderung bei imperativen Ansätzen ist, dass es schwieriger ist, die UI in bedeutungsvolle 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 an mehreren Stellen im Datenmodell wiederholt werden. |
| Deklarativ | Im deklarativen Ansatz werden die gewünschten Zustände der UI-Instanzen explizit deklariert, und die effiziente Implementierung dieses Zustands wird von 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 erleichtert das Auffinden und Beheben von Fehlern erheblich. Der wesentliche Nachteil besteht darin, dass die gesamte UI-Hierarchie im Code deklariert werden muss. Bibliotheken wie Roact und Fusion haben Syntax, um dies zu erleichtern, aber es ist immer noch ein zeitaufwändiger Prozess und eine weniger intuitive Bearbeitungserfahrung beim Erstellen der UI. |
Plant verwendet einen imperativen Ansatz in der Annahme, dass das direkte Anzeigen der Transformationen einen effektiveren Überblick darüber bietet, wie UI in Roblox erstellt und manipuliert wird. Dies wäre mit einem deklarativen Ansatz nicht möglich. Einige wiederholte UI-Strukturen und Logiken werden auch in wiederverwendbare Komponenten abstrahiert, um eine häufige Falle im imperativen UI-Design zu vermeiden.
Hochrangige Architektur

Ebene und Komponenten
In Plant besteht jede UI-Struktur entweder aus einer Layer oder einem Component.
- Layer wird als übergeordnete Gruppierung-Singleton definiert, das vorgefertigte UI-Strukturen in ReplicatedStorage umschließt. Eine Ebene kann eine Anzahl von Komponenten enthalten oder ihre eigene Logik vollständig kapseln. Beispiele für Ebenen sind das Inventarmenü oder der Münzzähler im HUD.
- Component ist ein wiederverwendbares UI-Element. Wenn ein neues Komponentenobjekt instanziiert wird, klont es eine vorgefertigte Vorlage aus ReplicatedStorage. Komponenten können wiederum andere Komponenten enthalten. Beispiele für Komponenten sind eine allgemeine Schaltflächenklasse oder das Konzept einer Liste von Elementen.
Sichtverwaltung
Ein häufiges Problem beim Umgang mit der UI ist die Sichtverwaltung. 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.
Plant geht dieses Problem mit seinem UIHandler-System an, das verwaltet, wann eine UI-Ebene sichtbar sein sollte oder nicht. Alle UI-Ebenen in der Erfahrung werden als HUD oder Menu kategorisiert und ihre Sichtbarkeit wird durch die folgenden Regeln verwaltet:
- Der aktivierte Zustand von Menu und HUD-Ebenen kann umgeschaltet werden.
- Aktivierte HUD-Ebenen werden nur angezeigt, wenn keine Menu-Ebenen aktiviert sind.
- Aktivierte Menu-Ebenen werden in einem Stapel gespeichert und nur eine Menu-Ebene ist gleichzeitig sichtbar. Wenn eine Menu-Ebene aktiviert wird, wird sie an die Spitze des Stapels eingefügt und angezeigt. Wenn eine Menu-Ebene deaktiviert wird, wird sie aus dem Stapel entfernt und die nächste aktivierte Menu-Ebene in der Warteschlange wird angezeigt.
Dieser Ansatz ist intuitiv, da er es ermöglicht, Menüs mit Historie zu navigieren. Wenn ein Menü aus einem anderen Menü geöffnet wird, wird das Schließen des neuen Menüs das alte Menü wieder anzeigen.
UI-Ebenen-Singletons registrieren sich beim UIHandler und erhalten ein Signal, das ausgelöst wird, wenn sich ihre Sichtbarkeit ändern sollte.
Weiterführende Literatur
Aus diesem umfassenden Überblick über das Plant-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-Netzwerkevents und Rückrufe für die Kommunikation über die Client-Server-Grenze.
- UI — Einzelheiten zu Benutzeroberflächenobjekten und -design in Roblox.