Después de crear algunos scripts, no pasa mucho tiempo antes de que quieras reutilizar algún código entre ellos. Dependiendo de la ubicación, ModuleScripts te permite reutilizar código entre scripts en diferentes lados del límite cliente-servidor o en el mismo lado del límite.
Puedes colocar scripts de módulo en cualquier lugar donde coloques scripts, pero ReplicatedStorage es una ubicación popular; almacenar scripts de módulo aquí te permite reutilizar código entre el servidor y los clientes.
Anatomía de un script de módulo
En Roblox Studio, agrega un script de módulo a ReplicatedStorage y renómbralo a PickupManager. Cada ModuleScript comienza con el siguiente código:
local module = {}return module
Este código crea una tabla de Luau vacía y la devuelve a cualquier script que requiera el script de módulo.
El valor de retorno puede ser cualquier tipo de dato excepto nil, pero la mayoría de los scripts de módulo devuelven una función, una tabla o una tabla de funciones. Para generar su valor de retorno, los scripts de módulo pueden, por supuesto, ejecutar código arbitrario, lo que incluye requerir otros scripts de módulo.
El siguiente ejemplo devuelve una tabla con una sola función llamada getPickupBonus. Pégalo en el nuevo script de módulo:
-- ModuleScript en ReplicatedStorage
local PickupManager = {}
local defaultMultiplier = 1.25
local rarityMultipliers = {
común = 10,
no común = 20,
raro = 50,
legendario = 100
}
-- Agrega la función getPickupBonus a la tabla PickupManager
PickupManager.getPickupBonus = function(rarity)
local bonus = rarityMultipliers[rarity] * defaultMultiplier
return bonus
end
return PickupManager
Agregar la función a una tabla no es estrictamente necesario; podrías simplemente devolver la función en sí, pero es un buen patrón a seguir; te da una sintaxis fácil de entender cuando llamas a la función desde otro script y te permite agregar fácilmente más funciones al script de módulo con el tiempo.
Requerir scripts de módulo
Para cargar un script de módulo, llamas a la función require(). En ReplicatedStorage, agrega un nuevo script y cambia su RunContext a Client. Luego, agrega el siguiente código para llamar a la función PickupManager.getPickupBonus:
Script del cliente en ReplicatedStoragelocal ReplicatedStorage = game:GetService("ReplicatedStorage")-- Obtener el valor devuelto por el ModuleScriptlocal PickupManager = require(ReplicatedStorage:WaitForChild("PickupManager"))-- Llamar a una función del ModuleScriptlocal bonus = PickupManager.getPickupBonus("legendario")print(bonus) --> 125
Almacenar scripts de módulo en ReplicatedStorage te permite compartir código entre el servidor y el cliente, por lo que puedes usar el mismo código para requerir el script desde ServerScriptService:
Script en ServerScriptServicelocal ReplicatedStorage = game:GetService("ReplicatedStorage")local PickupManager = require(ReplicatedStorage:WaitForChild("PickupManager"))
Cuando llamas a require() en un ModuleScript, se ejecuta una vez y devuelve un solo elemento como referencia. Llamar a require() de nuevo devuelve la misma referencia exacta, lo que significa que si modificas una tabla devuelta o un Instance, las llamadas posteriores a require() retornan esa referencia modificada. El módulo en sí no se ejecuta múltiples veces.
Si requieres un ModuleScript desde ambos lados del límite cliente-servidor, como en el ejemplo anterior, el ModuleScript devuelve una referencia única para cada lado.
Patrones
Los scripts de módulo tienen algunos patrones comunes que puedes usar para simplificar tu código y evitar trampas a medida que tu juego crece en tamaño y complejidad.
Compartir datos
Para asociar datos con objetos individuales, puedes asignar atributos a ellos o crear carpetas de Configuration con objetos de valor como StringValue o IntValue. Sin embargo, ambos enfoques son problemáticos si quieres agregar o modificar docenas de objetos o valores de datos. También, no almacenan tablas o funciones.
Si deseas modificar los mismos datos para múltiples copias del mismo objeto o reutilizar los mismos datos para diferentes objetos, almacena los datos en ModuleScripts. Es una forma más fácil para ti de reutilizar los datos en otros scripts, y puedes almacenar tablas y funciones.
El siguiente ejemplo de ModuleScript en ReplicatedStorage almacena los valores de configuración para una pistola genérica:
ModuleScript en ReplicatedStoragelocal GunConfig = {}GunConfig.MagazineSize = 20GunConfig.AmmoCount = 100GunConfig.Firerate = 600GunConfig.Damage = {["Cabeza"] = 50;["Torso"] = 40;["Cuerpo"] = 25;}return GunConfig
Eventos personalizados
Los eventos personalizados permiten que los scripts se comuniquen entre sí, pero tener que hacer un seguimiento de las referencias a objetos individuales BindableEvent puede desordenar tu código.
Puedes usar ModuleScripts para almacenar BindableEvents y proporcionar controladores de eventos personalizados que están directamente vinculados a los métodos del ModuleScript.
El siguiente ModuleScript en ReplicatedStorage tiene un evento personalizado que se activa cuando el interruptor cambia de estado:
ModuleScript en ReplicatedStorage
local Switch = {}
-- Creando un bindable para que cualquier script pueda escuchar cuando el interruptor fue cambiado
local bindableEvent = Instance.new("BindableEvent")
Switch.Changed = bindableEvent.Event
local state = false
function Switch.flip()
state = not state
bindableEvent:Fire(state)
end
return Switch
El siguiente script del cliente en ReplicatedStorage conecta una función para llamar cuando el evento Switch.Changed se activa.
Script en ReplicatedStorage
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Switch = require(ReplicatedStorage:WaitForChild("Switch"))
Switch.Changed:Connect(function(newState)
print("El estado del interruptor ahora es", newState)
end)
-- Probar el cambio unas cuantas veces
task.wait(1)
Switch.flip()
task.wait(1)
Switch.flip()
Encapsulación
La encapsulación es la práctica de crear una capa de abstracción alrededor de objetos o lógica de scripting para ocultar la complejidad. Puedes usar ModuleScripts para encapsular objetos de Roblox con funciones personalizadas de Luau para simplificar el código.
Por ejemplo, puedes usar la encapsulación para:
- Simplificar la comunicación entre redes con un solo objeto RemoteEvent.
- Envolver el manejo de errores alrededor de servicios sensibles como DataStoreService.
- Definir métodos personalizados para controlar o extender las características de los objetos de Roblox.
Es difícil hacer un seguimiento de docenas de objetos individuales RemoteEvent para implementar redes en un juego. Puedes usar un ModuleScript para encapsular un solo RemoteEvent para ayudar a simplificar este problema. Al incluir un argumento id único, aún puedes enviar diferentes mensajes de red mientras solo usas un solo RemoteEvent.
En el siguiente ejemplo, el ModuleScript llamado NetworkManagerClient encapsula el método RemoteEvent:FireServer() para incluir este argumento adicional id. Además, este ModuleScript hace referencia al objeto RemoteEvent en sí, por lo que no tienes que referenciarlo en otras partes de tu código. Solo necesitas requerir este ModuleScript para enviar mensajes de red y no necesitas tratar con objetos RemoteEvent en el resto de tu base de código.
El siguiente ModuleScript en ReplicatedFirst proporciona una función encapsulada que puedes llamar en tus scripts de cliente para enviar un mensaje de red:
Módulo de Red
-- ModuleScript en ReplicatedFirst llamado NetworkManagerClient
local NetworkManagerClient = {}
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteEvent = ReplicatedStorage:WaitForChild("RemoteEvent")
-- Encapsulando la función FireServer del objeto remoto
function NetworkManagerClient.FireServer(id, ...)
remoteEvent:FireServer(id, ...)
end
return NetworkManagerClient
El siguiente ModuleScript en ServerScriptService usa BindableEvents para que cada script se conecte a un ID específico. Cuando un cliente envía un mensaje de red, cada BindableEvent asociado con el ID especificado se activa.
-- ModuleScript en ServerScriptService llamado NetworkManagerServer
local NetworkManagerServer = {}
local networkSignalList = {}
function NetworkManagerServer.GetServerEventSignal(id)
local bindableEvent = Instance.new("BindableEvent")
-- Vinculando el nuevo BindableEvent al id
table.insert(networkSignalList, {
id = id,
bindableEvent = bindableEvent,
})
return bindableEvent.Event
end
-- Conectando a
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteEvent = ReplicatedStorage:WaitForChild("RemoteEvent")
remoteEvent.OnServerEvent:Connect(function(player, id, ...)
-- Encontrando cada evento vinculable que coincide con el ID del evento remoto recibido
for _, signal in networkSignalList do
if signal.id == id then
signal.bindableEvent:Fire(player, ...)
end
end
end)
return NetworkManagerServer
El siguiente LocalScript envía un mensaje con el ID RequestA con un argumento opcional Hello.
-- LocalScript en ReplicatedFirstlocal ReplicatedFirst = game:GetService("ReplicatedFirst")local NetworkManagerClient = require(ReplicatedFirst:WaitForChild("NetworkManagerClient"))NetworkManagerClient.FireServer("RequestA", "Hello")
El siguiente Script se conecta al mensaje de red con ID RequestA e imprime una declaración con cualquier parámetro adicional cuando recibe la solicitud.
-- Script en ServerScriptService
local ServerScriptService = game:GetService("ServerScriptService")
local NetworkManagerServer = require(ServerScriptService:WaitForChild("NetworkManagerServer"))
NetworkManagerServer.GetServerEventSignal("RequestA"):Connect(function(player, ...)
print("Recibido RequestA de", player, ...)
end)