Proyecto de referencia de planta

*Este contenido se traduce usando la IA (Beta) y puede contener errores. Para ver esta página en inglés, haz clic en aquí.

Planta es una experiencia de referencia en la que los jugadores plantan y riegan semillas, para que luego puedan cosechar y vender las plantas resultantes.

Plant project banner

El proyecto se centra en casos de uso comunes que puedes encontrar al desarrollar una experiencia en Roblox.Donde sea aplicable, encontrarás notas sobre las compensaciones, los compromisos y la lógica de varias opciones de implementación, para que puedas tomar la mejor decisión para tus propias experiencias.

Obtener el archivo

  1. Navegue hasta la página de experiencia Planta.
  2. Haga clic en el botón y Editar en Studio .

Usar casos

Planta cubre los siguientes casos de uso:

  • Persistencia de datos de sesión y datos del jugador
  • Administración de vista de UI
  • Red de cliente-servidor
  • Experiencia de usuario por primera vez (FTUE)
  • Compras de divisas duras y suaves

Además, este proyecto resuelve conjuntos más estrechos de problemas que son aplicables a muchas experiencias, incluyendo:

  • Personalización de una zona en el lugar que está asociada con un jugador
  • Administrar la velocidad de movimiento del personaje del jugador
  • Crear un objeto que sigue a los personajes alrededor
  • Detectar qué parte del mundo está en un personaje

Tenga en cuenta que hay varios casos de uso en esta experiencia que son demasiado pequeños, demasiado nicho o no demuestran una solución a un desafío de diseño interesante; estos no están cubiertos.

Estructura del proyecto

La primera decisión al crear una experiencia es decidir cómo estructurar el proyecto, que principalmente incluye dónde colocar instancias específicas en el modelo de datos y cómo organizar y estructurar puntos de entrada para tanto el código del cliente como del servidor.

aplicación de modeladode datos

La siguiente tabla describe en qué contenedores se colocan los servicios en las instancias del modelo de datos.

ServicioTipos de instancias
Workspace

Contiene modelos estáticos que representan el mundo 3D, específicamente partes del mundo que no pertenecen a ningún jugador.No necesitas crear, modificar o destruir dinámicamente estas instancias en tiempo de ejecución, por lo que es aceptable dejarlo aquí.:

También hay un modelo vacío de Folder , al que se agregarán los modelos de granja de los jugadores en tiempo de ejecución.

Lighting

Efectos atmosféricos y de iluminación.

ReplicatedFirst

Contiene el subconjunto más pequeño de instancias necesario para mostrar la pantalla de carga y inicializar el juego.Cuantas más instancias se coloquen en ReplicatedFirst, más tiempo tardarán en replicarse antes de que el código en ReplicatedFirst pueda ejecutarse.:

  • En la carpeta Instancias existe la interfaz de usuario de la pantalla de carga.:
  • En la carpeta Fuente existe el código de la pantalla de cargary el código necesario para esperar a que se cargue el resto del juego.El start``Class.LocalScript es el punto de entrada para todo el código del lado del cliente en el proyecto.
ReplicatedStorage

Sirve como contenedor de almacenamiento para todas las instancias para las que se requiere acceso tanto en el cliente como en el servidor.:

  • En la carpeta dependencias existe alguna biblioteca de terceros utilizada por el proyecto.:
  • En la carpeta Instancias existe un amplio rango de instancias precfabricadas utilizadas por varias clases en el juego.:
  • En la carpeta Fuente existe todo el código no requerido para el proceso de carga que debe ser accesible tanto desde el cliente como desde el servidor.
ServerScriptService

Contiene un Script que sirve como punto de entrada para todo el código del lado del servidor en el proyecto.

ServerStorage

Sirve como contenedor de almacenamiento para todas las instancias que no necesitan replicarse al cliente.:

  • En la carpeta Instancias existe un aplicación de modeladode plantilla Granja .Se coloca una copia de esto en Workspace cuando el jugador se una al juego, donde se replicará a todos los jugadores.:
  • En la carpeta Fuente existe todo el código que es exclusivo del servidor.
SoundService

Contiene los objetos Sound usados para efectos de sonido en el juego.Debajo de SoundService , estos objetos Sound no tienen posición y no se simulan en el espacio 3D.

Puntos de entrada

La mayoría de los proyectos organizan el código dentro de reutilizable ModuleScripts que se puede importar a través de toda la base de código.ModuleScripts son reutilizables, pero no se ejecutan por sí mismos; deben ser importados por un Script o LocalScript .Muchos proyectos de Roblox tendrán un gran número de Script y LocalScript objetos, cada uno perteneciente a un comportamiento o sistema particular en el juego, creando múltiples puntos de entrada.

Para el microjuego Planta, se implementa un enfoque diferente a través de un solo LocalScript que es el punto de entrada para todo el código del cliente, y un solo Script que es el punto de entrada para todo el código del servidor.El enfoque correcto para tu proyecto depende de tus requisitos, pero un solo punto de entrada proporciona un mayor control sobre el orden en que se ejecutan los sistemas.

Las siguientes listas describen las desventajas de ambos enfoques:

  • Un solo Script y un solo LocalScript cubren el código del servidor y del cliente respectivamente.
  • Mayor control sobre el orden en que se inician diferentes sistemas porque todo el código se inicializa desde un solo script.
  • Puede pasar objetos por referencia entre sistemas.

Arquitectura de alto nivel de sistemas

Los sistemas de alto nivel en el proyecto se detallan a continuación.Algunos de estos sistemas son sustancialmente más complejos que otros, y en muchos casos su funcionalidad se abstrae a través de una jerarquía de otras Clases.

Plant project systems architecture diagram

Cada uno de estos sistemas es un "singleton", en el sentido de que es una clase no instantánea que se inicializa en cambio por el script relevante del cliente o del servidor start.Puede leer más sobre el patrón singleton más tarde en esta guía.

Servidor

Los siguientes sistemas están asociados con el servidor.

SistemaDescripción
Red
  • Crea todas las instancias RemoteEvent y RemoteFunction .:
  • Exponen métodos para enviar y escuchar mensajes del cliente.:
  • Validez de tipo para argumentos recibidos por el cliente en tiempo de ejecución.
Servidor de datos del jugador
  • Guarda y carga datos persistentes del jugador usando DataStoreService .:
  • Almacena los datos del jugador en memoria y replica las mutaciones al cliente.:
  • Exponen señales y métodos para suscribirse, consultar y actualizar los datos del reproductor.
Mercado
  • Administra las transacciones de moneda suave desde el cliente.
  • Explica un método para vender plantas cosechadas.
Gerente de grupo de colisión
  • Asigna modelos de personaje de jugador a grupos de colisión .:
  • Configura grupos de colisión para que los personajes de los jugadores no puedan chocar con vagones de planta.
Servidor de administrador de granja
  • Re-crea el modelo de granja de un jugador a partir de sus datos de jugador cuando se unen al juego.:
  • Elimina el modelo de granja cuando un jugador se va.:
  • Actualiza los datos del jugador cuando se cambia la granja de un jugador.:
  • Exponer un método para acceder a la clase Granja asociada con un jugador dado.
Contenedor de objetos de jugador
  • Crea varios objetos asociados con la vida de un jugador y proporciona un método para recuperarlos.
Etiquetar jugadores
Servidor de gestión de ftue
  • Durante el FTUE, ejecuta cada etapa y espera a que se complete.
Generador de personajes
  • Reaparece personajes cuando mueren.Tenga en cuenta que Players.CharacterAutoLoads se ha desactivado para que el generado se detenga hasta que se hayan cargado los datos del jugador.

Cliente

Los siguientes sistemas están asociados con el cliente.

SistemaDescripción
Red
  • Espera a que el servidor cree todas las RemoteEvent y RemoteFunction instancias.:
  • Exponen métodos para enviar y escuchar mensajes al y desde el servidor.:
  • Aplica la validación del tipo de parámetro de tiempo de ejecución.:
  • Ejecuta pcall() en funciones remotas.
JugadorDataClient
  • Almacena los datos del jugador local en memoria.:
  • Exponen métodos y señales para consultar y suscribirse a cambios en los datos del jugador. >
Cliente de mercado
  • Explica un método para solicitar al servidor que compre un artículo por moneda suave.
Manager de salto de caminata local
  • Exponen métodos para modificar el WalkSpeed o JumpHeight de un personaje a través de multiplicadores para evitar conflictos al modificar estos valores desde múltiples lugares.
Cliente de administrador de granja
  • Escucha etiquetas específicas CollectionService que se aplican a instancias y crea "componentes" que añaden comportamiento a estas instancias.Un " componente " se refiere a una clase que se crea cuando se agrega una etiqueta CollectionService a una instancia y se destruye cuando se elimina; estas se utilizan para los mensajes de CTA en la granja y varias clases que teletransportan el estado de la granja al jugador.
Configuración de la interfaz de usuario
  • Inicializa todas las capas de UI.:
  • Configura ciertas capas para que solo sean visibles en secciones físicas del mundo del juego.:
  • Conecta un efecto de cámara especial para cuando los menús estén habilitados.
Cliente de FtueManager
  • Configura etapas de FTUE en el cliente.
Carrera de personajes
  • Usa LocalWalkJumpManager para aumentar WalkSpeed cuando un personaje de jugador está fuera de su granja.

Comunicación cliente-servidor

La mayoría de las experiencias de Roblox implican algún elemento de comunicación entre el cliente y el servidor.Esto puede incluir la solicitud del cliente al servidor de realizar una determinada acción y el servidor replicando actualizaciones al cliente.

En este proyecto, la comunicación cliente-servidor se mantiene lo más genérica posible al limitar el uso de RemoteEvent y RemoteFunction objetos para reducir la cantidad de reglas especiales para realizar un seguimiento.Este proyecto usa los siguientes métodos, en orden de preferencia:

  • Replicación a través del sistema de datos del reproductor .
  • Replicación a través de atributos.
  • Replicación a través de etiquetas.
  • Mensajería directamente a través del módulo Red de la red.

Replicación a través del sistema de datos del reproductor

El sistema de datos del jugador permite que los datos se asocien con el jugador que persiste entre sesiones de guardado .Este sistema proporciona replicación desde el cliente al servidor y un conjunto de API que se puede utilizar para consultar datos y suscribirse a cambios, lo que lo hace ideal para replicar cambios al estado del jugador desde el servidor al cliente.

Por ejemplo, en lugar de disparar un personalizado UpdateCoins``Class.RemoteEvent para decirle al cliente cuántas monedas tiene, puede llamar al siguiente y dejar que el cliente se suscriba a él a través del evento PlayerDataClient.updated.


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

Por supuesto, esto solo es útil para la replicación de servidor a cliente y para los valores que desea persistir entre sesiones, pero esto se aplica a un número sorprendente de casos en el proyecto, incluyendo:

  • La etapa actual de FTUE
  • El inventario del jugador
  • La cantidad de monedas que tiene el jugador
  • El estado de la granja del jugador

Replicación a través de atributos

En situaciones en las que el servidor debe replicar un valor personalizado al cliente que sea específico para un determinado Instance , puedes usar atributos .Roblox replica automáticamente los valores de atributos, por lo que no necesitas mantener ningún camino de código para replicar el estado asociado con un objeto.Otra ventaja es que esta replicación ocurre junto con la propia instancia.

Esto es particularmente útil para las instancias creadas en tiempo de ejecución, ya que los atributos establecidos en una nueva instancia antes de que se vincule al modelo de datos se replicarán atómicamente con la propia instancia.Esto evita la necesidad de escribir código para "esperar" que se re複ique datos adicionales a través de un RemoteEvent o StringValue .

También puedes leer atributos directamente desde el aplicación de modeladode datos, desde el cliente o el servidor, con el método GetAttribute() , y suscribirte a cambios con el método GetAttributeChangedSignal().En el proyecto Planta, se usa este enfoque para, entre otras cosas, replicar el estado actual de las plantas a los clientes.

Replicación a través de etiquetas

CollectionService te permite aplicar una etiqueta de cadena a un Instance. Esto es útil para categorizar instancias y replicar esa categorización al cliente.

Por ejemplo, la etiqueta CanPlant se aplica en el servidor para indicar al cliente que una dada olla es capaz de recibir una planta.

Mensaje directamente a través del módulo de red

Para situaciones en las que ninguna de las opciones anteriores se aplique, puedes usar llamadas de red personalizadas a través del módulo Red .Esta es la única opción en el proyecto que permite la comunicación cliente-servidor y, por lo tanto, es más útil para transmitir solicitudes del cliente y recibir una respuesta del servidor.

Planta usa llamadas de red directas para una variedad de solicitudes de clientes, incluyendo:

  • Regar una planta
  • Plantar una semilla
  • Comprar un objeto

La desventaja con este enfoque es que cada mensaje individual requiere una configuración personalizada que puede aumentar la complejidad del proyecto, aunque esto se ha evitado en la medida de lo posible, particularmente para la comunicación de servidor a cliente.

Clases y singletons

Las clases en el proyecto Planta se pueden crear y destruir, como instancias en Roblox.Su sintaxis de clase se inspira en el enfoque idiomático de Lua para programación orientada a objetos con una serie de cambios para habilitar el Soportede tipificación estricta.

Instantánea

Muchas clases en el proyecto están asociadas con una o más Instances .Los objetos de una clase determinada se crean utilizando un método new() , consistente con la forma en que se crean instancias en Roblox usando Instance.new() .

Este patrón se usa generalmente para objetos donde la clase tiene una representación física en el aplicación de modeladode datos, y la clase extiende su funcionalidad.Un buen ejemplo es que crea un objeto entre dos objetos dados y mantiene esos accesorios orientados para que el rayo siempre se dirija hacia arriba.Estas instancias se podrían clonar desde una versión prefabricada en ReplicatedStorage o pasarse a new() como argumento y almacenarse dentro del objeto bajo self .

Instancias correspondientes

Como se señaló anteriormente, muchas clases en este proyecto tienen una representación de modelo de datos, una instancia que coincide con la clase y es manipulada por ella.

En lugar de crear estas instancias cuando un objeto de clase se instancia, el código generalmente opta por Clone() una versión prefabricada del Instance almacenada bajo ReplicatedStorage o ServerStorage .Aunque sería posible serializar las propiedades de estas instancias y crearlas desde cero en las funciones de la clase new(), hacerlo haría que editar los objetos sea muy engorroso y los haga más difíciles de procesar para un lector.Además, clonar una instancia es generalmente una operación más rápida que crear una nueva instancia y personalizar sus propiedades en tiempo de ejecución.

Composición

Aunque la herencia es posible en Luau usando tablas metálicas, el proyecto opta por permitir que las clases se extiendan entre sí a través de composición .Al combinar clases a través de la composición, el objeto "hijo" se instanciaa en el método new() de la clase y se incluye como miembro bajo self .

Para un ejemplo de esto en acción, vea la clase CloseButton que envuelve la clase Button.

Limpieza

Al igual que cómo un Instance puede ser destruido con el método Destroy(), las clases que se pueden instanciar también se pueden destruir.El método destructor para las clases del proyecto es con una minúscula para la coherencia entre los métodos del código base, así como para distinguir entre las clases del proyecto y las instancias de Roblox.

El rol del método destroy() es destruir cualquier instancia creada por el objeto, desconectar cualquier conexión y llamar destroy() en cualquier objeto hijo.Esto es particularmente importante para las conexiones porque las instancias con conexiones activas no se limpian por el recolector de basura Luau, incluso si no quedan referencias a la instancia o conexiones a la instancia.

Únicos

Los singletons, como sugiere el nombre, son clases para las cuales solo puede existir un objeto.Son el equivalente del proyecto de los servicios de Roblox.En lugar de almacenar una referencia al objeto singleton y pasarla alrededor en el código Luau, Planta aprovecha el hecho de que requerir un ModuleScript cachea su valor devuelto.Esto significa que requerir el mismo singleton ModuleScript de diferentes lugares proporciona consistentemente el mismo objeto devuelto.La única excepción a esta regla sería si diferentes entornos (cliente o servidor) accedieran al ModuleScript .

Los singletons se distinguen de las clases instantáneas por el hecho de que no tienen un método new() .En cambio, el objeto junto con sus métodos y estado se devuelve directamente a través del ModuleScript .Como los singletons no se instancian, no se usa la sintaxis self y se llaman los métodos con un punto ( . ) en lugar de un colon ( : ).

Inferencia de tipo estricto

Luau admite la escritura gradual que significa que eres libre de agregar definiciones de tipo opcional a alguno o a todos tus códigos.En este proyecto, strict se usa la verificación de tipo para cada script.Esta es la opción menos permisiva para la herramienta de análisis de scripts de Roblox y, por lo tanto, la más probable de detectar errores de tipo antes de tiempo de ejecución.

Sintaxis de clase tipificada

El enfoque establecido para crear clases en Lua es bien documentado , sin embargo, no es adecuado para el fuerte tipado de Luau.En Luau, el enfoque más simple para obtener el tipo de una clase es el método typeof() :


type ClassType = typeof(Class.new())

Esto funciona, pero no es muy útil cuando tu clase se inicia con valores que solo existen en tiempo de ejecución, por ejemplo Player objetos.Además, la suposición hecha en la sintaxis de clase Lua idiomática es que declarar un método en una clase self siempre será una instancia de esa clase; esto no es una suposición que el motor de inferencia de tipo pueda hacer.

Con el fin de soportar la inferencia de tipo estricto, el proyecto Planta utiliza una solución que difiere de la sintaxis de clase idiomática de Lua en una serie de formas, algunas de las cuales pueden parecer no intuitivas:

  • La definición de self se duplica, tanto en la declaración de tipo como en el constructor.Esto introduce una carga de mantenibilidad, pero las advertencias se marcarán si las dos definiciones se salen de sincronía entre sí.
  • Los métodos de clase se declaran con un punto, por lo que self se puede declarar explícitamente como de tipo ClassType.Los métodos aún se pueden llamar con un colon como se esperaba.

--!stricto
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

Lanzar tipos después de guardias lógicos

Al momento de escribir, el tipo de un valor no se restringe después de una declaración condicional de guardia.Por ejemplo, siguiendo el guardia a continuación, el tipo de optionalParameter no se reduce a number .


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

Para mitigar esto, se crean nuevas variables después de estos guardias con su tipo explícitamente lanzado.


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

Transitar las jerarquías de modelo de datos

En algunos casos, el código base debe recorrer la jerarquía del modelo de datos de un árbol de objetos que se crean en tiempo de ejecución.Esto plantea un desafío interesante para la verificación de tipo.Al momento de escribir, no es posible definir una jerarquía de modelo de datos genérica como un introducir.Como resultado, hay casos en los que la única información de tipo disponible para una estructura de modelo de datos es el tipo de la instancia de nivel superior.

Una aproximación a este desafío es lanzar a any y luego refinar. Por ejemplo:


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

El problema con este enfoque es que afecta la legibilidad.En cambio, el proyecto utiliza un módulo genérico llamado getInstance para recorrer las jerarquías del modelo de datos que se lanzan a any internamente.


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

A medida que la comprensión del motor de tipo evoluciona sobre el modelo de datos, es posible que los patrones como este ya no sean necesarios.

Interfaz de usuario

Planta incluye una variedad de interfaces de usuario complejas y simples de 2D.Estos incluyen elementos de advertencia no interactivos (HUD) como el contador de monedas y menús interactivos complejos como la comprar.

Enfoque de UI

Puedes comparar de forma holgada la interfaz de usuario de Roblox UI con el DOM HTML, porque es una jerarquía de objetos que describen lo que el usuario debería ver.Los enfoques para crear y actualizar una interfaz de usuario de Roblox se dividen ampliamente en prácticas imperativas y declarativas .

AproximaciónVentajas y desventajas
Imperativo

En el enfoque imperativo, la interfaz se trata como cualquier otra jerarquía de instancias en Roblox.La estructura de la interfaz de usuario se crea antes de la ejecución en Studio y se agrega al aplicación de modeladode datos, generalmente directamente en StarterGui .Luego, en tiempo de ejecución, el código manipula piezas específicas de la interfaz de usuario para reflejar el estado que requiere el creador.:

Este enfoque viene con algunas ventajas.Puedes crear la interfaz de usuario desde cero en Studio y almacenarla en el aplicación de modeladode datos.Esta es una experiencia de edición simple y visual que puede acelerar la creacionesde interfaces de usuario.Debido a que el código de interfaz de usuario imperativo solo se preocupa por lo que necesita cambiar, también hace que las cambios de interfaz de usuario simples sean fáciles de implementar.:

Una desventaja notable es que, dado que los enfoques de interfaz imperativa requieren que el estado se implemente manualmente en forma de transformaciones, las representaciones complejas del estado pueden ser muy difíciles de encontrar y depurar.Es común que surjan errores al desarrollar código imperativo de interfaz de usuario, especialmente cuando el estado y la interfaz se des sincronizan debido a múltiples actualizaciones que interactúan en un orden inesperado.:

Otro desafío con enfoques imperativos es que es más difícil descomponer la interfaz de usuario en componentes significativos que se puedan declarar una vez y reutilizar.Debido a que el árbol completo de la interfaz de usuario se declara en el momento de edición, los patrones comunes pueden repetirse en múltiples partes del aplicación de modeladode datos.

Declarativo

En el enfoque declarativo, el estado deseado de instancias de UI se declara explícitamente, y la implementación eficiente de este estado se abstrae por bibliotecas como Roact o Fusion.:

La ventaja de este enfoque es que la implementación del estado se vuelve trivial y solo necesitas describir lo que quieres que tu interfaz de usuario se vea.Esto hace que la identificación y resolución de errores sea significativamente más fácil.:

La desventaja clave es tener que declarar todo el árbol de la interfaz en código.Bibliotecas como Roact y Fusion tienen sintaxis para hacer esto más fácil, pero aún es un proceso que consume tiempo y una experiencia de edición menos intuitiva al componer la interfaz de usuario.

Planta usa un enfoque imperativo bajo la noción de que mostrar las transformaciones directamente da una visión más efectiva de cómo se crean y manipulan las interfaces en Roblox.Esto no sería posible con un enfoque declarativo.Algunas estructuras y lógica de interfaz repetidas también se abstraen en componentes reutilizables para evitar una trampa común en el diseño de interfaz imperativa.

Arquitectura de alto nivel

Plant project UI architecture diagram

Capa y componentes

En Planta, todas las estructuras de interfaz de usuario son o un Layer o un Component.

  • Layer se define como un grupo de nivel superior que envuelve estructuras de interfaz de usuario precocinadas en ReplicatedStorage .Una capa puede contener una serie de componentes, o puede encapsular toda su propia lógica.Ejemplos de capas son el menú de inventario o el indicador de número de monedas en la pantalla de cabecera.
  • Component es un elemento de interfaz de usuario reutilizable.Cuando se instanciaa un nuevo objeto de componente, clona una plantilla prefabricada de ReplicatedStorage .Los componentes pueden contener en sí mismos otros componentes.Ejemplos de componentes son una clase de botón genérica o el concepto de una lista de elementos.

Ver manejo

Un problema común de gestión de interfaces de usuario es el manejo de vistas.Este proyecto tiene una serie de menús y elementos de HUD, algunos de los cuales escuchan la entrada del usuario y se requiere una gestión cuidadosa de cuándo están visibles o habilitados.

Planta aborda este problema con su sistema UIHandler que gestiona cuándo una capa de interfaz de usuario debe o no ser visible.Todas las capas de interfaz de usuario en el juego se categorizan como HUD o Menu y su visibilidad se gestiona según las siguientes reglas:

  • El estado habilitado de Menu y HUD capas se puede alternar.
  • Las capas habilitadas HUD solo se muestran si no se habilitan capas Menu.
  • Las capas habilitadas Menu se almacenan en una pila, y solo una capa Menu visible a la vez.Cuando se habilita una capa Menu, se inserta al frente de la pila y se muestra.Cuando una capa Menu se desactiva, se elimina de la pila y se muestra la siguiente capa habilitada Menu en la cola.

Este enfoque es intuitivo porque permite que los menús se naveguen con historia.Si se abre un menú desde otro menú, cerrar el nuevo menú mostrará el menú antiguo nuevamente.

Los singletons de la capa de interfaz de usuario se registran a sí mismos con el manejador de interfaz de usuario y se les proporciona una señal que se activa cuando su visibilidad debe cambiar.

Lectura adicional

Desde esta completa visión general del proyecto Planta, es posible que desees explorar los siguientes guías que profundizan más en conceptos y temas relacionados.

  • Modelo cliente-servidor — Una visión general del modelo cliente-servidor en Roblox.
  • Luau — Detalles sobre Luau , el lenguaje de programación creado por Roblox descendido de Lua 5.1.
  • Eventos y llamadas remotas — Todo sobre eventos y llamadas de red remotos para la comunicación a través de la frontera cliente-servidor.
  • UI — Detalles sobre objetos de interfaz de usuario y diseño en Roblox.