Progetto di riferimento per le piante

*Questo contenuto è tradotto usando AI (Beta) e potrebbe contenere errori. Per visualizzare questa pagina in inglese, clicca qui.

Plant è un gioco di riferimento in cui i giocatori piantano e annaffiano semi, in modo da poter successivamente raccogliere e vendere le piante risultanti.

Banner del progetto Plant

Il progetto si concentra su casi d'uso comuni che potresti incontrare durante lo sviluppo di un gioco su Roblox. Dove applicabile, troverai note sui compromessi, le scelte e la razionalità di varie scelte di implementazione, in modo da poter prendere la migliore decisione per i tuoi giochi.

Ottieni il file

  1. Naviga alla pagina del gioco Plant.
  2. Clicca sul pulsante e Modifica in Studio.

Casi d'uso

Plant copre i seguenti casi d'uso:

  • Persistenza dei dati di sessione e dei dati del giocatore
  • Gestione della vista dell'interfaccia utente
  • Networking client-server
  • Esperienza per il primo utilizzo (FTUE)
  • Acquisti di valuta dura e morbida

Inoltre, questo progetto risolve insiemi più ristretti di problemi che sono applicabili a molti giochi, tra cui:

  • Personalizzazione di un'area nel luogo associata a un giocatore
  • Gestione della velocità di movimento del personaggio del giocatore
  • Creazione di un oggetto che segue i personaggi
  • Rilevamento di quale parte del mondo si trova un personaggio

Nota che ci sono diversi casi d'uso in questo gioco che sono troppo piccoli, troppo di nicchia o non dimostrano una soluzione a una sfida di design interessante; questi non sono coperti.

Struttura del progetto

La prima decisione quando si crea un gioco è decidere come strutturare il progetto, che include principalmente dove posizionare istanze specifiche nel modello dei dati e come organizzare e strutturare i punti di ingresso per il codice client e server.

Modello dei dati

La seguente tabella descrive in quali servizi contenitore nel modello dei dati sono posizionate le istanze.

ServizioTipi di istanze
Workspace

Contiene modelli statici che rappresentano il mondo 3D, specificamente parti del mondo che non appartengono a nessun giocatore. Non è necessario creare, modificare o distruggere dinamicamente queste istanze durante il runtime, quindi è accettabile lasciarle qui.

C'è anche una Folder vuota, a cui verranno aggiunti i modelli delle fattorie dei giocatori durante il runtime.

Lighting

Effetti atmosferici e di illuminazione.

ReplicatedFirst

Contiene il più piccolo possibile sottoinsieme di istanze necessarie per visualizzare la schermata di caricamento e inizializzare il gioco. Più istanze vengono posizionate in ReplicatedFirst, più lungo sarà l'attesa affinché vengano replicate prima che il codice in ReplicatedFirst possa essere eseguito.

  • Nella cartella Instances esiste la GUI della schermata di caricamento.
  • Nella cartella Source esiste il codice della schermata di caricamento e il codice necessario per attendere che il resto del gioco venga caricato. Il start LocalScript è il punto di ingresso per tutto il codice lato client nel progetto.
ReplicatedStorage

Funziona come contenitore di archiviazione per tutte le istanze per le quali è necessario l'accesso sia dal client che dal server.

  • Nella cartella Dependencies esistono alcune librerie di terze parti utilizzate dal progetto.
  • Nella cartella Instances esiste un'ampia gamma di istanze prefabbricate.
  • Nella cartella Source esiste tutto il codice non necessario per il processo di caricamento che deve essere accessibile sia dal client che dal server.
ServerScriptService

Contiene uno Script che funge da punto di ingresso per tutto il codice lato server nel progetto.

ServerStorage

Funziona come contenitore di archiviazione per tutte le istanze che non devono essere replicate al client.

  • Nella cartella Instances esiste un modello Farm di esempio. Una copia di questo viene posizionata in Workspace quando il giocatore entra nel gioco, dove verrà replicata a tutti i giocatori.
  • Nella cartella Source esiste tutto il codice esclusivo per il server.
SoundService

Contiene gli oggetti Sound utilizzati per gli effetti sonori nel gioco. Sotto SoundService, questi oggetti Sound non hanno posizione e non sono simulati nello spazio 3D.

Punti di ingresso

La maggior parte dei progetti organizza il codice all'interno di ModuleScripts riutilizzabili che possono essere importati in tutto il codice. ModuleScripts sono riutilizzabili ma non vengono eseguiti da soli; devono essere importati da uno Script o LocalScript. Molti progetti Roblox avranno un gran numero di oggetti Script e LocalScript, ciascuno relativo a un comportamento o a un sistema particolare nel gioco, creando molteplici punti di ingresso.

Per il microgioco Plant, è stato implementato un approccio diverso attraverso un singolo LocalScript che è il punto di ingresso per tutto il codice client e un singolo Script che è il punto di ingresso per tutto il codice server. L'approccio corretto per il tuo progetto dipende dai tuoi requisiti, ma un singolo punto di ingresso fornisce un maggiore controllo sull'ordine in cui i sistemi vengono eseguiti.

Le seguenti liste descrivono i compromessi di entrambi gli approcci:

  • Un singolo Script e un singolo LocalScript coprono rispettivamente il codice server e client.
  • Maggiore controllo sull'ordine in cui diversi sistemi vengono avviati poiché tutto il codice è inizializzato da uno script singolo.
  • Può passare oggetti per riferimento tra i sistemi.

Architettura dei sistemi ad alto livello

I sistemi di alto livello nel progetto sono dettagliati di seguito. Alcuni di questi sistemi sono sostanzialmente più complessi di altri e, in molti casi, la loro funzionalità è astratta attraverso una gerarchia di altre classi.

Diagramma dell'architettura dei sistemi del progetto Plant

Ognuno di questi sistemi è un "singleton", in quanto è una classe non istanziabile che viene invece inizializzata dallo script di avvio client o server pertinente. Puoi leggere di più sul pattern singleton più avanti in questa guida.

Server

I seguenti sistemi sono associati al server.

SistemaDescrizione
Network
  • Crea tutte le istanze RemoteEvent e RemoteFunction.
  • Espone metodi per inviare e ascoltare messaggi dal client.
  • Validazione del tipo per gli argomenti ricevuti dal client durante il runtime.
PlayerDataServer
  • Salva e carica i dati persistenti del giocatore utilizzando DataStoreService.
  • Memorizza i dati del giocatore in memoria e replica le mutazioni al client.
  • Espone segnali e metodi per iscriversi, interrogare e aggiornare i dati del giocatore.
Market
  • Gestisce le transazioni di valuta morbida dal client.
  • Espone un metodo per vendere piante raccolte.
CollisionGroupManager
  • Assegna modelli di personaggi dei giocatori a gruppi di collisione.
  • Configura i gruppi di collisione in modo che i personaggi dei giocatori non possano collidere con i carri delle piante.
FarmManagerServer
  • Ricrea il modello della fattoria di un giocatore dai dati del giocatore quando entra nel gioco.
  • Rimuove il modello della fattoria quando un giocatore esce.
  • Aggiorna i dati del giocatore quando la fattoria di un giocatore viene modificata.
  • Espone un metodo per accedere alla classe Farm associata a un dato giocatore.
PlayerObjectsContainer
  • Crea vari oggetti associati alla vita di un giocatore e fornisce un metodo per recuperarli.
TagPlayers
FtueManagerServer
  • Durante il FTUE, esegue ogni fase e attende che venga completata.
CharacterSpawner
  • Respawna i personaggi quando muoiono. Nota che Players.CharacterAutoLoads è stato disabilitato in modo che il respawn sia sospeso fino a quando i dati del giocatore non sono stati caricati.

Client

I seguenti sistemi sono associati al client.

SistemaDescrizione
Network
  • Attende che il server crei tutte le istanze RemoteEvent e RemoteFunction.
  • Espone metodi per inviare e ascoltare messaggi al e dal server.
  • Applica la validazione del tipo dei parametri durante il runtime.
  • Esegue pcall() su funzioni remote.
PlayerDataClient
  • Memorizza i dati del giocatore locale in memoria.
  • Espone metodi e segnali per interrogare e iscriversi ai cambiamenti nei dati del giocatore.
MarketClient
  • Espone un metodo per richiedere al server di acquistare un oggetto per valuta morbida.
LocalWalkJumpManager
  • Espone metodi per modificare la WalkSpeed o JumpHeight di un personaggio tramite moltiplicatori per evitare conflitti quando si modificano questi valori da più luoghi.
FarmManagerClient
  • Ascolta l'applicazione di tag specifici di CollectionService alle istanze e crea "componenti" che aggiungono comportamento a queste istanze. Un "componente" si riferisce a una classe che viene creata quando un tag di CollectionService viene aggiunto a un'istanza e distrutta quando viene rimosso; questi vengono utilizzati per i prompt CTA nella fattoria e varie classi che comunicano lo stato della fattoria al giocatore.
UISetup
  • Inizializza tutti i livelli dell'interfaccia utente.
  • Configura alcuni livelli per essere visibili solo in sezioni fisiche del mondo.
  • Collega un effetto speciale della telecamera per quando i menu sono abilitati.
FtueManagerClient
  • Configura le fasi del FTUE sul client.
CharacterSprint
  • Usa LocalWalkJumpManager per aumentare la WalkSpeed quando un personaggio giocatore è al di fuori della propria fattoria.

Comunicazione client-server

La maggior parte dei giochi Roblox coinvolge qualche elemento di comunicazione tra il client e il server. Questo può includere la richiesta del client al server di eseguire una certa azione e il server che replica aggiornamenti al client.

In questo progetto, la comunicazione client-server è mantenuta il più generica possibile limitando l'uso di oggetti RemoteEvent e RemoteFunction per ridurre la quantità di regole speciali da tenere traccia. Questo progetto utilizza i seguenti metodi, in ordine di preferenza:

Replica tramite il sistema di dati del giocatore

Il sistema di dati del giocatore consente di associare dati al giocatore che persistono tra le sessioni di salvataggio. Questo sistema fornisce replica dal client al server e un insieme di API che possono essere utilizzate per interrogare i dati e iscriversi ai cambiamenti, rendendolo ideale per replicare i cambiamenti allo stato del giocatore dal server al client.

Ad esempio, piuttosto che attivare un UpdateCoins RemoteEvent su misura per dire al client quanti soldi ha, puoi chiamare il seguente e lasciare che il client si iscriva tramite l'evento PlayerDataClient.updated.

PlayerDataServer.setValue(player, "coins", 5)

Naturalmente, questo è utile solo per la replica dal server al client e per i valori che desideri persistere tra le sessioni, ma questo si applica a un numero sorprendente di casi nel progetto, inclusi:

  • L'attuale fase del FTUE
  • L'inventario del giocatore
  • La quantità di monete che ha il giocatore
  • Lo stato della fattoria del giocatore

Replica tramite attributi

In situazioni in cui il server deve replicare un valore personalizzato al client specifico per una data Instance, puoi utilizzare attributi. Roblox replica automaticamente i valori degli attributi, quindi non è necessario mantenere alcun percorso di codice per replicare lo stato associato a un oggetto. Un altro vantaggio è che questa replica avviene insieme all'istanza stessa.

Questo è particolarmente utile per le istanze create durante il runtime, poiché gli attributi impostati su una nuova istanza prima che venga parentata al modello dei dati si replicheranno atomicamente con l'istanza stessa. Questo evita la necessità di scrivere codice per "attendere" che dati extra vengano replicati tramite un RemoteEvent o StringValue.

Puoi anche leggere direttamente gli attributi dal modello dei dati, sia dal client che dal server, con il metodo GetAttribute() e iscriverti ai cambiamenti con il GetAttributeChangedSignal() metodo. Nel progetto Plant, questo approccio viene utilizzato per, tra le altre cose, replicare lo stato attuale delle piante ai client.

Replica tramite tag

CollectionService ti consente di applicare un tag di stringa a un Instance. Questo è utile per categorizzare le istanze e replicare quella categorizzazione al client.

Ad esempio, il tag CanPlant viene applicato sul server per segnalare al client che un determinato vaso è in grado di ricevere una pianta.

Messaggio direttamente tramite il modulo di rete

Per situazioni in cui nessuna delle opzioni precedenti si applica, puoi utilizzare chiamate di rete personalizzate tramite il modulo Network. Questa è l'unica opzione nel progetto che consente la comunicazione dal client al server e quindi è più utile per trasmettere richieste del client e ricevere una risposta dal server.

Plant utilizza chiamate di rete dirette per una varietà di richieste del client, tra cui:

  • Annaffiare una pianta
  • Piantare un seme
  • Acquistare un oggetto

Lo svantaggio di questo approccio è che ogni singolo messaggio richiede una configurazione su misura che può aumentare la complessità del progetto, anche se questo è stato evitato ovunque possibile, in particolare per la comunicazione dal server al client.

Classi e singleton

Le classi nel progetto Plant, come le istanze su Roblox, possono essere create e distrutte. La sua sintassi di classe è ispirata all'approccio idiomatico di Lua per la programmazione orientata agli oggetti con un certo numero di modifiche per abilitare il supporto per verifica dei tipi rigorosa.

Istanziamento

Molte classi nel progetto sono associate a una o più Instances. Gli oggetti di una data classe vengono creati utilizzando un metodo new(), coerente con il modo in cui le istanze vengono create in Roblox utilizzando Instance.new().

Questo pattern è generalmente utilizzato per oggetti in cui la classe ha una rappresentazione fisica nel modello dei dati e la classe estende la sua funzionalità. Un buon esempio è BeamBetween che crea un oggetto Beam tra due dati oggetti Attachment e mantiene quegli attacchi orientati in modo che il raggio sia sempre rivolto verso l'alto. Queste istanze potrebbero essere clonate da una versione prefabbricata in ReplicatedStorage o passate a new() come argomento e memorizzate all'interno dell'oggetto sotto self.

Istanze corrispondenti

Come notato sopra, molte classi in questo progetto hanno una rappresentazione nel modello dei dati, un'istanza che corrisponde alla classe e viene manipolata da essa.

Piuttosto che creare queste istanze quando un oggetto di classe viene istanziato, il codice generalmente opta per Clone() una versione prefabbricata dell'Instance memorizzata sotto ReplicatedStorage o ServerStorage. Anche se sarebbe possibile serializzare le proprietà di queste istanze e crearle da zero nelle funzioni new() della classe, farlo renderebbe molto scomodo modificare gli oggetti e più difficile per un lettore interpretarli. Inoltre, clonare un'istanza è generalmente un'operazione più veloce rispetto a creare una nuova istanza e personalizzarne le proprietà durante il runtime.

Composizione

Sebbene l'ereditarietà sia possibile in Luau utilizzando metatables, il progetto opta invece di consentire alle classi di estendersi a vicenda attraverso la composizione. Quando si combinano classi tramite composizione, l'oggetto "figlio" viene istanziato nel metodo new() della classe ed è incluso come membro sotto self.

Per un esempio di questo in azione, vedere la classe CloseButton che avvolge la classe Button.

Pulizia

Simile a come un Instance può essere distrutto con il Destroy() metodo, le classi che possono essere istanziate possono anche essere distrutte. Il metodo distruttore per le classi del progetto è destroy() con una d minuscola per la coerenza camelCase tra i metodi del codice, così come per distinguere tra le classi del progetto e le istanze di Roblox.

Il ruolo del metodo destroy() è distruggere eventuali istanze create dall'oggetto, disconnettere eventuali connessioni e chiamare destroy() su eventuali oggetti figli. Questo è particolarmente importante per le connessioni perché le istanze con connessioni attive non vengono pulite dal garbage collector di Luau, anche se non rimangono riferimenti all'istanza o connessioni all'istanza.

Singleton

I singleton, come suggerisce il nome, sono classi per le quali può esistere solo un oggetto. Sono l'equivalente del progetto dei Servizi di Roblox. Piuttosto che memorizzare un riferimento all'oggetto singleton e passarne in giro nel codice Luau, Plant sfrutta il fatto che richiedere un ModuleScript memorizza nella cache il suo valore restituito. Questo significa che richiedere lo stesso ModuleScript singleton da luoghi diversi fornisce costantemente lo stesso oggetto restituito. L'unica eccezione a questa regola sarebbe se ambienti diversi (client o server) accedessero al ModuleScript.

I singleton si distinguono dalle classi istanziabili per il fatto che non hanno un metodo new(). Piuttosto, l'oggetto insieme ai suoi metodi e stato viene restituito direttamente tramite il ModuleScript. Poiché i singleton non vengono istanziati, la sintassi self non viene utilizzata e i metodi vengono invece chiamati con un punto (.) piuttosto che con un due punti (:).

Inferenza dei tipi rigorosa

Luau supporta la tipizzazione graduale, il che significa che sei libero di aggiungere definizioni di tipo opzionali a parte o tutto il tuo codice. In questo progetto, la verifica dei tipi strict è utilizzata per ogni script. Questa è l'opzione meno permissiva per lo strumento di Analisi degli Script di Roblox e quindi la più probabile per catturare errori di tipo prima del runtime.

Sintassi di classe tipizzata

L'approccio stabilito per creare classi in Lua è ben documentato, tuttavia non è ben adatto alla tipizzazione forte di Luau. In Luau, l'approccio più semplice per ottenere il tipo di una classe è il metodo typeof():

type ClassType = typeof(Class.new())

Questo funziona ma non è molto utile quando la tua classe è inizializzata con valori che esistono solo durante il runtime, ad esempio oggetti Player. Inoltre, l'assunzione fatta nella sintassi di classe idiomatica di Lua è che dichiarare un metodo su una classe self sarà sempre un'istanza di quella classe; questa non è un'assunzione che il motore di inferenza dei tipi può fare.

Per supportare l'inferenza dei tipi rigorosa, il progetto Plant utilizza una soluzione che differisce dalla sintassi di classe idiomatica di Lua in diversi modi, alcuni dei quali potrebbero sembrare non intuitivi:

  • La definizione di self è duplicata, sia nella dichiarazione del tipo che nel costruttore. Questo introduce un onere di manutenzione, ma verranno segnalati avvisi se le due definizioni non sono sincronizzate tra loro.
  • I metodi della classe sono dichiarati con un punto, in modo che self possa essere esplicitamente dichiarato come tipo ClassType. I metodi possono comunque essere chiamati con un due punti come previsto.
--!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

Cast dei tipi dopo le guardie logiche

Al momento della scrittura, il tipo di un valore non viene ristretto dopo un'istruzione condizionale di guardia. Ad esempio, dopo la guardia qui sotto, il tipo di optionalParameter non viene ristretto a number.

--!strict
local function foo(optionalParameter: number?)
if not optionalParameter then
return
end
print(optionalParameter + 1)
end

Per mitigare questo, vengono create nuove variabili dopo queste guardie con il loro tipo esplicitamente castato.

--!strict
local function foo(optionalParameter: number?)
if not optionalParameter then
return
end
local parameter = optionalParameter :: number
print(parameter + 1)
end

Traversare le gerarchie del DataModel

In alcuni casi, il codice deve attraversare la gerarchia del modello dei dati di un albero di oggetti creati durante il runtime. Questo presenta una sfida interessante per la verifica dei tipi. Al momento della scrittura, non è possibile definire una gerarchia generica del modello dei dati come tipo. Di conseguenza, ci sono casi in cui l'unica informazione di tipo disponibile per una struttura del modello dei dati è il tipo dell'istanza di livello superiore.

Un approccio a questa sfida è fare il cast a any e poi affinare. Ad esempio:

local function enableVendor(vendor: Model)
local zonePart: BasePart = (vendor :: any).ZonePart
end

Il problema con questo approccio è che impatta sulla leggibilità. Invece, il progetto utilizza un modulo generico chiamato getInstance per attraversare le gerarchie del modello dei dati che fa il cast a any internamente.

local function enableVendor(vendor: Model)
local zonePart: BasePart = getInstance(vendor, "ZonePart")
end

Man mano che la comprensione del motore di tipo del modello dei dati evolve, è possibile che schemi come questo non siano più necessari.

Interfaccia utente

Plant include una varietà di interfacce utente 2D complesse e semplici. Queste includono elementi non interattivi dell'interfaccia di visualizzazione (HUD) come il contatore di monete e menu interattivi complessi come il negozio.

Approccio UI

Puoi confrontare vagamente l'UI di Roblox con il DOM HTML, poiché è una gerarchia di oggetti che descrivono cosa l'utente dovrebbe vedere. Gli approcci per creare e aggiornare un'interfaccia utente di Roblox sono ampiamente divisi in pratiche imperative e declarative.

ApproccioVantaggi e svantaggi
Imperativo

Nell'approccio imperativo, l'interfaccia utente è trattata come qualsiasi altra gerarchia di istanze su Roblox. La struttura dell'interfaccia utente viene creata prima del runtime in Studio e aggiunta al modello dei dati, tipicamente direttamente in StarterGui. Poi, durante il runtime, il codice manipola pezzi specifici dell'interfaccia utente per riflettere lo stato richiesto dal creatore.

Questo approccio presenta alcuni vantaggi. Puoi creare l'interfaccia utente da zero in Studio e memorizzarla nel modello dei dati. Questa è un'esperienza di editing semplice e visiva che può accelerare la creazione dell'interfaccia utente. Poiché il codice dell'interfaccia utente imperativa si preoccupa solo di ciò che deve essere cambiato, rende anche facili da implementare semplici modifiche all'interfaccia utente.

Un notevole svantaggio è che, poiché gli approcci UI imperativi richiedono che lo stato venga implementato manualmente sotto forma di trasformazioni, rappresentazioni complesse dello stato possono diventare molto difficili da trovare e debug. È comune che si verifichino errori durante lo sviluppo del codice dell'interfaccia utente imperativa, specialmente quando lo stato e l'interfaccia utente diventano desincronizzati a causa di più aggiornamenti che interagiscono in un ordine inaspettato.

Un'altra sfida con gli approcci imperativi è che è più difficile suddividere l'interfaccia utente in componenti significativi che possono essere dichiarati una volta e riutilizzati. Poiché l'intero albero dell'interfaccia utente è dichiarato al momento della modifica, schemi comuni possono essere ripetuti in più parti del modello dei dati.

Declarativo

Nell'approccio dichiarativo, lo stato desiderato delle istanze dell'interfaccia utente è dichiarato esplicitamente e l'implementazione efficiente di questo stato è astratta da librerie come Roact o Fusion.

Il vantaggio di questo approccio è che l'implementazione dello stato diventa banale e devi solo descrivere come vuoi che appaia la tua interfaccia utente. Questo rende significativamente più facile identificare e risolvere bug.

Il principale svantaggio è dover dichiarare l'intero albero dell'interfaccia utente nel codice. Librerie come Roact e Fusion hanno una sintassi per rendere questo più facile, ma è comunque un processo che richiede tempo e un'esperienza di editing meno intuitiva quando si compone l'interfaccia utente.

Plant utilizza un approccio imperativo con l'idea che mostrare le trasformazioni direttamente fornisca una panoramica più efficace di come l'interfaccia utente viene creata e manipolata su Roblox. Questo non sarebbe possibile con un approccio dichiarativo. Alcune strutture e logiche dell'interfaccia utente ripetute sono anche astratte in componenti riutilizzabili per evitare un comune problema nel design dell'interfaccia utente imperativa.

Architettura di alto livello

Diagramma dell'architettura dell'interfaccia utente del progetto Plant

Livello e componenti

In Plant, tutte le strutture dell'interfaccia utente sono o un Layer o un Component.

  • Layer è definito come un singleton di raggruppamento di alto livello che avvolge strutture dell'interfaccia utente prefabbricate in ReplicatedStorage. Un livello può contenere un certo numero di componenti, oppure può racchiudere completamente la propria logica. Esempi di livelli sono il menu inventario o l'indicatore del numero di monete nell'interfaccia di visualizzazione.
  • Component è un elemento dell'interfaccia utente riutilizzabile. Quando un nuovo oggetto componente viene istanziato, clona un modello prefabbricato da ReplicatedStorage. I componenti possono a loro volta contenere altri componenti. Esempi di componenti sono una classe di pulsante generico o il concetto di un elenco di oggetti.

Gestione delle visualizzazioni

Un comune problema di gestione dell'interfaccia utente è la gestione delle visualizzazioni. Questo progetto ha una gamma di menu e elementi HUD, alcuni dei quali ascoltano l'input dell'utente, e una gestione attenta di quando sono visibili o abilitati è necessaria.

Plant affronta questo problema con il suo sistema UIHandler che gestisce quando un livello dell'interfaccia utente dovrebbe o non dovrebbe essere visibile. Tutti i livelli dell'interfaccia utente nel gioco sono categorizzati come HUD o Menu e la loro visibilità è gestita dalle seguenti regole:

  • Lo stato abilitato dei livelli Menu e HUD può essere attivato o disattivato.
  • I livelli HUD abilitati vengono mostrati solo se nessun livello Menu è abilitato.
  • I livelli Menu abilitati vengono memorizzati in uno stack, e solo un livello Menu è visibile alla volta. Quando un livello Menu è abilitato, viene inserito in cima allo stack e mostrato. Quando un livello Menu è disabilitato, viene rimosso dallo stack e il successivo livello Menu abilitato nella coda viene mostrato.

Questo approccio è intuitivo perché consente di navigare nei menu con la cronologia. Se un menu viene aperto da un altro menu, chiudere il nuovo menu mostrerà di nuovo il menu precedente.

I singleton dei livelli dell'interfaccia utente si registrano con il UIHandler e ricevono un segnale che si attiva quando la loro visibilità deve cambiare.

Ulteriori letture

Da questa panoramica approfondita del progetto Plant, potresti voler esplorare le seguenti guide che approfondiscono ulteriormente concetti e argomenti correlati.

  • Modello Client-Server — Una panoramica del modello client-server in Roblox.
  • Luau — Dettagli su Luau, il linguaggio di scripting creato da Roblox discendente da Lua 5.1.
  • Eventi e Callback Remoti — Tutto sugli eventi di rete remoti e callback per la comunicazione attraverso il confine client-server.
  • UI — Dettagli sugli oggetti e sul design dell'interfaccia utente su Roblox.
© 2026 Roblox Corporation. Roblox, il logo Roblox e Powering Imagination sono tra i nostri marchi registrati e non registrati negli Stati Uniti. e altri paesi.