Proyecto de referencia de plantas

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

Plant es una experiencia de referencia donde los jugadores siembran y riegan semillas, para poder cosechar y vender las plantas resultantes.

Banner del proyecto Plant

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 compensaciones, compromisos y la lógica detrás de varias elecciones de implementación, para que puedas tomar la mejor decisión para tus propias experiencias.

Obtén el archivo

  1. Navega a la página de experiencia de Plant.
  2. Haz clic en el botón y Editar en Studio.

Casos de uso

Plant cubre los siguientes casos de uso:

  • Persistencia de datos de sesión y datos de jugador
  • Gestión de vistas de UI
  • Redes cliente-servidor
  • Experiencia de Primer Usuario (FTUE)
  • Compras de moneda dura y blanda

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

  • Personalización de un área en el lugar que está asociada con un jugador
  • Gestión de la velocidad de movimiento del personaje del jugador
  • Creación de un objeto que sigue a los personajes
  • Detección de en qué parte del mundo se encuentra un personaje

Ten 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 incluye principalmente dónde colocar instancias específicas en el modelo de datos y cómo organizar y estructurar los puntos de entrada tanto para el código del cliente como para el del servidor.

Modelo de datos

La siguiente tabla describe en qué servicios contenedor se colocan 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, así que es aceptable dejarlas aquí.

También hay una Folder vacía, a la que se añadirá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 posible de instancias necesarias para mostrar la pantalla de carga e inicializar la experiencia. Cuantas más instancias se coloquen en ReplicatedFirst, más tiempo se tardará en replicarlas antes de que se ejecute el código en ReplicatedFirst.

  • En la carpeta Instances existe la GUI de la pantalla de carga.
  • En la carpeta Source existe el código de la pantalla de carga y el código necesario para esperar a que se cargue el resto de la experiencia. El start LocalScript es el punto de entrada para todo el código del lado del cliente en el proyecto.
ReplicatedStorage

Sirve como un contenedor de almacenamiento para todas las instancias que requieren acceso tanto en el cliente como en el servidor.

  • En la carpeta Dependencies existen algunas bibliotecas de terceros utilizadas por el proyecto.
  • En la carpeta Instances existe una amplia gama de instancias prefabricadas.
  • En la carpeta Source existe todo código no requerido para el proceso de carga que necesita ser accesible tanto desde el cliente como desde el servidor.
ServerScriptService

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

ServerStorage

Sirve como un contenedor de almacenamiento para todas las instancias que no necesitan ser replicadas al cliente.

  • En la carpeta Instances existe un modelo de plantilla Farm. Una copia de esta se coloca en Workspace cuando el jugador se une a la experiencia, donde se replicará a todos los jugadores.
  • En la carpeta Source existe todo el código que es exclusivo para el servidor.
SoundService

Contiene los objetos Sound utilizados para efectos de sonido en la experiencia. Bajo 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 ModuleScripts reutilizables que se pueden importar en toda la base de código. ModuleScripts son reutilizables pero no se ejecutan por sí solos; necesitan ser importados por un Script o un LocalScript. Muchos proyectos de Roblox tendrán un gran número de objetos Script y LocalScript, cada uno referente a un comportamiento o sistema particular en la experiencia, creando múltiples puntos de entrada.

Para el microjuego Plant, 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 compensaciones de ambos enfoques:

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

Arquitectura de sistemas de alto nivel

Los sistemas de nivel superior 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.

Diagrama de arquitectura de sistemas del proyecto Plant

Cada uno de estos sistemas es un "singleton", en el sentido de que es una clase no instanciable que se inicializa mediante el script start pertinente del cliente o del servidor. Puedes leer más sobre el patrón singleton más adelante en esta guía.

Servidor

Los siguientes sistemas están asociados con el servidor.

SistemaDescripción
Red
  • Crea todas las instancias de RemoteEvent y RemoteFunction.
  • Expone métodos para enviar y escuchar mensajes desde el cliente.
  • Validación de tipo para argumentos recibidos del cliente en tiempo de ejecución.
PlayerDataServer
  • Guarda y carga datos persistentes del jugador utilizando DataStoreService.
  • Almacena datos del jugador en memoria y replica las mutaciones al cliente.
  • Expone señales y métodos para suscribirse, consultar y actualizar datos del jugador.
Mercado
  • Maneja transacciones de moneda blanda desde el cliente.
  • Expone un método para vender plantas cosechadas.
CollisionGroupManager
  • Asigna modelos de personajes de jugadores a grupos de colisión.
  • Configura grupos de colisión para que los personajes de los jugadores no colisionen con los carros de plantas.
FarmManagerServer
  • Recrea el modelo de granja de un jugador a partir de sus datos cuando se une a la experiencia.
  • Elimina el modelo de granja cuando un jugador sale.
  • Actualiza los datos del jugador cuando se cambia la granja de un jugador.
  • Expone un método para acceder a la clase Farm asociada con un jugador dado.
PlayerObjectsContainer
  • Crea varios objetos asociados con la vida de un jugador y proporciona un método para recuperarlos.
TagPlayers
FtueManagerServer
  • Durante el FTUE, ejecuta cada etapa y espera a que se complete.
CharacterSpawner
  • Respawnea personajes cuando mueren. Ten en cuenta que Players.CharacterAutoLoads ha sido desactivado para que el respawn se pause 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 instancias de RemoteEvent y RemoteFunction.
  • Expone métodos para enviar y escuchar mensajes hacia y desde el servidor.
  • Impone validación de tipo de parámetros en tiempo de ejecución.
  • Ejecuta pcall() en funciones remotas.
PlayerDataClient
  • Almacena los datos del jugador local en memoria.
  • Expone métodos y señales para consultar y suscribirse a cambios en los datos del jugador.
MarketClient
  • Expone un método para solicitar al servidor que compre un artículo por moneda blanda.
LocalWalkJumpManager
  • Expone métodos para modificar la WalkSpeed o JumpHeight de un personaje a través de multiplicadores para evitar conflictos al modificar estos valores desde múltiples lugares.
FarmManagerClient
  • Escucha las etiquetas de CollectionService específicas 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 añade una etiqueta de CollectionService a una instancia y se destruye cuando se elimina; estas son utilizadas para los avisos CTA en la granja y varias clases que informan sobre el estado de la granja al jugador.
UISetup
  • Inicializa todas las capas de UI.
  • Configura ciertas capas para que sean visibles solo en secciones físicas del mundo.
  • Conecta un efecto especial de cámara para cuando se habilitan los menús.
FtueManagerClient
  • Configura etapas de FTUE en el cliente.
CharacterSprint
  • Utiliza LocalWalkJumpManager para aumentar la WalkSpeed cuando un personaje jugador está fuera de su granja.

Comunicación cliente-servidor

La mayoría de las experiencias de Roblox involucran algún elemento de comunicación entre el cliente y el servidor. Esto puede incluir que el cliente solicite al servidor realizar una cierta acción y el servidor replicar actualizaciones al cliente.

En este proyecto, la comunicación cliente-servidor se mantiene lo más genérica posible limitando el uso de objetos RemoteEvent y RemoteFunction a fin de disminuir la cantidad de reglas especiales a tener en cuenta. Este proyecto utiliza los siguientes métodos, en orden de preferencia:

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

El sistema de datos del jugador permite asociar datos con el jugador que persisten entre sesiones de guardado. Este sistema proporciona replicación del cliente al servidor y un conjunto de API que se pueden usar para consultar datos y suscribirse a cambios, lo que lo hace ideal para replicar cambios en el estado del jugador desde el servidor al cliente.

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


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

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

  • La etapa actual del 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 donde el servidor necesita replicar un valor personalizado al cliente que es específico de una determinada Instance, puedes usar atributos. Roblox replica automáticamente los valores de atributos, por lo que no necesitas mantener ninguna ruta de código para replicar el estado asociado a un objeto. Otra ventaja es que esta replicación ocurre junto con la propia instancia.

Esto es particularmente útil para instancias creadas en tiempo de ejecución, ya que los atributos establecidos en una nueva instancia antes de que se parenten al modelo de datos se replicarán de forma atómica con la propia instancia. Esto elude cualquier necesidad de escribir código para "esperar" que se replique información adicional a través de un RemoteEvent o StringValue.

También puedes leer atributos directamente desde el modelo de datos, tanto desde el cliente como desde el servidor, con el método GetAttribute() método y suscribirte a cambios con el GetAttributeChangedSignal() método. En el proyecto Plant, este enfoque se utiliza 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 una 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 significar al cliente que una maceta determinada puede recibir una planta.

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

Para situaciones en las que ninguna de las opciones anteriores se aplica, 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 del cliente al servidor y, por lo tanto, es más útil para transmitir solicitudes del cliente y recibir una respuesta del servidor.

Plant utiliza llamadas de red directas para una variedad de solicitudes del cliente, incluyendo:

  • Regar una planta
  • Plantar una semilla
  • Comprar un artículo

El inconveniente de este enfoque es que cada mensaje individual requiere una configuración personalizada que puede aumentar la complejidad del proyecto, aunque esto se ha evitado siempre que ha sido posible, particularmente para la comunicación del servidor al cliente.

Clases y singletons

Las clases en el proyecto Plant, al igual que las instancias en Roblox, pueden ser creadas y destruidas. Su sintaxis de clase se inspira en el enfoque idiomático de Lua para la programación orientada a objetos con una serie de cambios para habilitar el soporte de verificación de tipo estricta.

Instanciación

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

Este patrón se utiliza generalmente para objetos donde la clase tiene una representación física en el modelo de datos, y la clase extiende su funcionalidad. Un buen ejemplo es BeamBetween, que crea un objeto Beam entre dos objetos Attachment dados y mantiene esas uniones orientadas para que el haz siempre esté mirando hacia arriba. Estas instancias podrían ser clonadas desde una versión prefabricada en ReplicatedStorage o pasadas a new() como un argumento y almacenadas dentro del objeto bajo self.

Instancias correspondientes

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

En lugar de crear estas instancias cuando se instancia un objeto de clase, el código generalmente opta por Clone() una versión prefabricada de la Instance almacenada bajo ReplicatedStorage o ServerStorage. Aunque sería posible serializar las propiedades de estas instancias y crearlas desde cero en las funciones new() de la clase, hacerlo haría que editar los objetos fuera muy engorroso y más difícil de entender 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 utilizando metatables, 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 composición, el objeto "hijo" se instancia en el método new() de la clase y se incluye como un miembro bajo self.

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

Limpieza

Similar a cómo una Instance puede ser destruida con el método Destroy(), las clases que pueden ser instanciadas también pueden ser destruidas. El método destructor para las clases del proyecto es destroy() con una d minúscula para mantener la consistencia de camelCase a través de los métodos de la base de código, así como para distinguir entre las clases del proyecto y las instancias de Roblox.

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

Singletons

Los singletons, como su nombre lo indica, son clases para las cuales solo un objeto puede existir. Son el equivalente del proyecto a los Servicios de Roblox. En lugar de almacenar una referencia al objeto singleton y pasarla en el código de Luau, Plant aprovecha el hecho de que requerir un ModuleScript guarda en caché su valor devuelto. Esto significa que requerir el mismo ModuleScript singleton desde diferentes lugares proporciona de manera consistente el mismo objeto devuelto. La única excepción a esta regla sería si diferentes entornos (cliente o servidor) acceden al ModuleScript.

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

Inferencia de tipos estricta

Luau admite tipado gradual, lo que significa que eres libre de añadir definiciones de tipo opcionales a algunos o todos tus códigos. En este proyecto, se utiliza verificación de tipo strict 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 propensa a capturar errores de tipo antes de tiempo de ejecución.

Sintaxis de clase tipada

El enfoque establecido para crear clases en Lua está bien documentado, sin embargo, no se adapta bien al tipado fuerte en 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, los objetos Player. Además, la suposición hecha en la sintaxis de clase idiomática de Lua es que declarar un método en self siempre será una instancia de esa clase; esta no es una suposición que el motor de inferencia de tipos pueda hacer.

Para poder soportar la inferencia de tipos estricta, el proyecto Plant utiliza una solución que difiere de la sintaxis de clase idiomática de Lua en varias maneras, 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 se señalarán advertencias si las dos definiciones no están sincronizadas.
  • Los métodos de clase se declaran con un punto, de modo que self puede ser explícitamente declarado como de tipo ClassType. Los métodos aún pueden ser llamados con dos puntos como se espera.

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

Tipos de casting después de guardas lógicas

En el momento de escribir este documento, el tipo de un valor no se reduce después de una declaración de guardia condicional. Por ejemplo, después de la guardia a continuación, el tipo de optionalParameter no se reduce a number.


--!strict
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 estas guardas con su tipo expresamente convertido.


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

Traversar jerarquías de DataModel

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

Un enfoque para este desafío es convertir 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 impacta la legibilidad. En su lugar, el proyecto usa un módulo genérico llamado getInstance para recorrer jerarquías de modelo de datos que convierte a any internamente.


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

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

Interfaz de usuario

Plant incluye una variedad de interfaces de usuario 2D complejas y sencillas. Estas incluyen elementos de visualización no interactivos como el contador de monedas y menús interactivos complejos como la tienda.

Enfoque de UI

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

EnfoqueVentajas y desventajas
Imperativo

En el enfoque imperativo, la UI se trata como cualquier otra jerarquía de instancias en Roblox. La estructura de la UI se crea antes del tiempo de ejecución en Studio y se agrega al modelo de datos, típicamente directamente en StarterGui. Luego, en tiempo de ejecución, el código manipula partes específicas de la UI para reflejar el estado que el creador requiere.

Este enfoque tiene algunas ventajas. Puedes crear la UI desde cero en Studio y almacenarla en el modelo de datos. Esta es una experiencia de edición simple y visual que puede acelerar la creación de la UI. Debido a que el código de UI imperativo solo se ocupa de lo que necesita cambiar, también hace que los cambios simples en la UI sean fáciles de implementar.

Un inconveniente notable es que, dado que los enfoques de UI imperativos requieren que el estado sea implementado manualmente en forma de transformaciones, representaciones complejas de estado pueden volverse muy difíciles de encontrar y depurar. Es común que surjan errores al desarrollar código de UI imperativa, especialmente cuando el estado y la UI se desincronizan debido a múltiples actualizaciones que interactúan en un orden inesperado.

Otro desafío con los enfoques imperativos es que es más difícil descomponer la UI en componentes significativos que se pueden declarar una vez y reutilizar. Dado que todo el árbol de la UI se declara en el tiempo de edición, patrones comunes pueden repetirse en múltiples partes del modelo de datos.

Declarativo

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

La ventaja de este enfoque es que la implementación del estado se vuelve trivial y solo necesitas describir cómo deseas que luzca tu UI. Esto hace que identificar y resolver errores sea significativamente más fácil.

La principal desventaja es tener que declarar todo el árbol de la UI en código. Bibliotecas como Roact y Fusion tienen sintaxis para facilitar esto, pero sigue siendo un proceso que consume tiempo y una experiencia de edición menos intuitiva al componer la UI.

Plant utiliza un enfoque imperativo bajo la noción de que mostrar las transformaciones directamente proporciona una visión más efectiva de cómo se crea y manipula la UI en Roblox. Esto no sería posible con un enfoque declarativo. Algunas estructuras y lógicas de UI repetidas también se abstraen en componentes reutilizables para evitar un problema común en el diseño de UI imperativa.

Arquitectura de alto nivel

Diagrama de arquitectura de UI del proyecto Plant

Capa y componentes

En Plant, todas las estructuras de UI son o una Layer o un Component.

  • Layer se define como un singleton de agrupación de nivel superior que envuelve estructuras de UI prefabricadas en ReplicatedStorage. Una capa puede contener una serie de componentes, o puede encapsular su propia lógica por completo. Ejemplos de capas son el menú de inventario o el indicador de cantidad de monedas en la visualización.
  • Component es un elemento de UI reutilizable. Cuando se instancia un nuevo objeto componente, clona una plantilla prefabricada de ReplicatedStorage. Los componentes pueden contener a su vez otros componentes. Ejemplos de componentes son una clase de botón genérico o el concepto de una lista de elementos.

Manejo de vistas

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

Plant aborda este problema con su sistema UIHandler que gestiona cuándo una capa de UI debería o no ser visible. Todas las capas de UI en la experiencia están categorizadas como HUD o Menu y su visibilidad se gestiona mediante las siguientes reglas:

  • El estado habilitado de las capas de Menu y HUD puede ser alternado.
  • Las capas de HUD habilitadas solo se muestran si no hay capas de Menu habilitadas.
  • Las capas de Menu habilitadas se almacenan en una pila, y solo una capa de Menu es visible a la vez. Cuando se habilita una capa de Menu, se inserta al frente de la pila y se muestra. Cuando se deshabilita una capa de Menu, se retira de la pila y se muestra la siguiente capa de Menu habilitada en la cola.

Este enfoque es intuitivo porque permite navegar por los menús con historial. Si se abre un menú desde otro menú, cerrar el nuevo menú volverá a mostrar el menú anterior.

Los singletons de la capa de UI se registran con el UIHandler y se les proporciona una señal que se activa cuando su visibilidad debe cambiar.

Lectura adicional

Desde esta exhaustiva visión general del proyecto Plant, es posible que desees explorar las siguientes guías que profundizan 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 scripting creado por Roblox descendiente de Lua 5.1.
  • Eventos Remotos y Callbacks — Todo sobre eventos de red remotos y callbacks 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.
©2026 Roblox Corporation. Roblox, el logotipo de Roblox y "Powering Imagination" son algunas de nuestras marcas registradas y no registradas en los Estados Unidos y otros países.