Con el aplicación de modeladode programación Luau Paralelo, puedes ejecutar el código en múltiples hilos simultáneamente, lo que puede mejorar el rendimiento de tu experiencia. A medida que expandes tu experiencia con más contenido, puedes adoptar este modelo para ayudar a mantener el rendimiento y la seguridad de tus scripts Luau.
Modelo de programación paralela
Por defecto, los scripts se ejecutan secuencialmente. Si tu experiencia tiene lógica o contenido complejo, como personajes no jugadores (NPC), validación de raycasting y generación procedural, entonces la ejecución secuencial puede causar un rendimiento lento para tus usuarios. Con el aplicación de modeladode programación paralela, puedes dividir tareas en múltiples scripts y ejecutarlos en paralelo. Esto hace que tu código de experiencia se ejecute más rápido, lo
El modelo de programación paralela también agrega beneficios de seguridad a su código. Al dividir el código en múltiples hilos, cuando edita el código en un subproceso, no afecta a otros códigos que se ejecutan en paralelo. Esto reduce el riesgo de tener un error en su código que corrompa toda la experiencia y minimiza el retraso para los usuarios en los servidores en vivo cuando pushes una actualización.
Adoptar el modelo de programación paralela no significa poner todo en múltiples subprocesos. Por ejemplo, la validación de raycasting del lado del servidor establece para cada usuario individual un evento remoto en paralelo, pero todavía requiere que el código inicial se ejecute en serie para cambiar las propiedades globales, que es un patrón común para la ejecución paralela.
La mayoría de las veces es necesario combinar fases seriales y paralelas para lograr el Salidadeseado, ya que actualmente hay algunas operaciones que no se admiten en paralelo que pueden impedir que se ejecuten los scripts, como modificar instancias en fases paralelas. Para obtener más información sobre el nivel de uso de las API en paralelo, see Seguridad de hilo .
Dividir el código en múltiples subprocesos
Para ejecutar los scripts de tu experiencia en múltiples subprocesos simultáneamente, necesitas dividirlos en pedazos lógicos bajo diferentes actores en el modelo de datos. Los actores se representan por instancias Actor que heredan de 2> Class.DataModel2> . Funcionan como unidades de aislamiento de ejecución que distribuyen la carga entre
Colocando Instancias de Actor
Puedes poner a los actores en contenedores apropiados o usarlos para reemplazar los tipos de instancia de nivel superior de tus entidades 3D, como NPCs y raycasters, luego agregar los scripts correspondientes scripts .
Para la mayoría de las situaciones, no deberías poner a un actor como hijo de otro actor en el aplicación de modeladode datos. Sin embargo, si decides colocar un script anidado entre múltiples actores para tu caso de uso específico, el script pertenece a su actor ancestral más cercano.
Desincronización de hilos
Aunque poner scripts debajo de los actores les otorga la capacidad de ejecución paralela, por defecto, el código todavía se ejecuta en el hilo único en serie, lo que no mejora el rendimiento de tiempo de ejecución. Debes llamar a la función de rendimiento de task.desynchronize(), que es una función de rendimiento que suspende la ejecución de la coroutine actual para ej
Alternativamente, puede usar el método RBXScriptSignal:ConnectParallel() cuando quieras programar una llamada de señal para ejecutar inmediatamente tu código en paralelo al momento de activar. No es necesario llamar task.desynchronize() dentro de la devolución de llamadade señal.
Dessincronizar un hilo
local RunService = game:GetService("RunService")
RunService.Heartbeat:ConnectParallel(function()
... -- Algunos códigos paralelos que calculan una actualización de estado
task.synchronize()
... -- Algunos códigos seriales que cambian el estado de las instancias
end)
Los scripts que forman parte del mismo actor siempre se ejecutan secuencialmente en relación con cada otro, por lo que necesitas múltiples actores. Por ejemplo, si pones todos los scripts de comportamiento habilitados en paralelo para tu NPC en un actor, siempre se ejecutan en serie en un solo subproceso, pero si tienes múltiples actores para diferentes lógicas de NPC, cada uno de ellos se ejecuta en paralelo en su propio subproceso. Para obtener más información, consulta
Seguridad del hilo
Durante la ejecución paralela, puede acceder a la mayoría de las instancias de la DataModel hierarquía como de costumbre, pero algunas propiedades y funciones de la API no son seguras para leer o escribir. Si las usa en su código paralelo, el motor de Roblox puede detectar y prevenir automáticamente que se produzcan estos accesos.
Los miembros de la API tienen un nivel de seguridad de subprocesso que indica si y cómo puede usarlos en su código paralelo, como muestra la siguiente tabla:
Nivel de seguridad | Para Propiedades | Para Funciones |
---|---|---|
No seguro | No se puede leer o escribir en paralelo. | No se puede llamar en paralelo. |
Leer Paralelo | Puede leerse pero no escribirse en paralelo. | N/A |
Caja fuerte local | Puede usarse dentro del mismo Actor; puede ser leído pero no escrito por otros Actors en paralelo. | Puede ser llamado dentro del mismo Actor; no se puede llamar por otros Actors en paralelo. |
Caja fuerte | Puede ser leído y escrito. | Puede ser llamado. |
Puede encontrar etiquetas de seguridad de subproceso para los miembros de la API en la referencia de la API. Cuando las use, también debe considerar cómo las llamadas de API o los cambios de propiedad pueden interactuar entre subprocesos paralelos. Por lo general, es seguro que varios actores lean los mismos datos que otros actores, pero no modifiquen el estado de otros actores.
Comunicación entre subprocesos
En el contexto de multihilo, todavía puede permitir que los scripts en diferentes actores se comuniquen entre sí para intercambiar datos, coordinar tareas y sincronizar actividades. El motor soporta los siguientes mecanismos para la comunicación entre hilos:
- API de Mensajería de Actores para enviar mensajes a un actor usando scripts.
- Tabla Compartida estructura de datos para compartir eficientemente una gran cantidad de datos entre varios actores en un estado compartido.
- Comunicación Directa del Modelo de Datos para una comunicación simple con restricciones.
Puede admitir múltiples mecanismos para satisfacer sus necesidades de comunicación entre subprocesos. Por ejemplo, puede enviar una tabla compartida a través de la API de mensajería del actor.
Mensajería deActor
La API Mensajería de Actores permite a un script, en un contexto serial o paralelo, enviar datos a un actor en el mismo aplicación de modeladode datos. La comunicación a través de esta API es asíncrona, en la que el emisor no bloquea hasta que el receptor reciba el mensaje.
Al enviar mensajes con esta API, debe definir un tema para categorizar el mensaje. Cada mensaje solo se puede enviar a un solo actor, pero ese actor puede internamente tener múltiples llamadas de retorno vinculadas a un mensaje. Solo los scripts que son descendientes de un actor pueden recibir mensajes.
La API tiene los siguientes métodos:
- Actor:SendMessage() para enviar un mensaje a un actor.
- Actor:BindToMessage() para vincular una llamada de Luau a un mensaje con el tema especificado en un contexto serial.
- Actor:BindToMessageParallel() para vincular una llamada de Luau a un mensaje con el tema especificado en un contexto paralelo.
El siguiente ejemplo muestra cómo usar Actor:SendMessage() para definir un tema y enviar un mensaje del finalizardel remitente:
Ejemplo de Remitente de Mensaje
-- Enviar dos mensajes al actor de trabajo con un tema de "Saludo"local workerActor = workspace.WorkerActorworkerActor:SendMessage("Greeting", "Hello World!")workerActor:SendMessage("Greeting", "Welcome")print("Sent messages")
El siguiente ejemplo muestra cómo usar Actor:BindToMessageParallel() para vincular un llamado de retorno de llamada para un tema determinado en un contexto paralelo en el finalizardel receptor:
Receptor de mensaje de ejemplo
-- Obtén al actor al que este script está ligado
local actor = script:GetActor()
-- Vincular un llamado para el tema "Saludo"
actor:BindToMessageParallel("Greeting", function(greetingString)
print(actor.Name, "-", greetingString)
end)
print("Bound to messages")
Tabla Compartida
SharedTable es una estructura de datos semejante a una tabla accesible a los scripts que se ejecutan bajo múltiples actores. Es útil para situaciones que implican una gran cantidad de datos y requieren un estado compartido común entre múltiples hilos. Por ejemplo, cuando múltiples actores trabajan en un estado mundial común que no está almacenado en el aplicación de modeladode datos.
Enviar una tabla compartida a otro actor no hace una copia de los datos. En cambio, las tablas compartidas permiten actualizaciones seguras y atómicas por múltiples scripts simultáneamente. Cada actualización de una tabla compartida por un actor es inmediatamente visible para todos los actores. Las tablas compartidas también se pueden clonar en un proceso eficiente de recursos que utiliza el compartir estructural en lugar de copiar los datos subyacentes.
Directa del modelo de datos Comunicación
También puede facilitar la comunicación entre múltiples subprocesos directamente usando el aplicación de modeladode datos, en el que diferentes actores pueden escribir y luego leer propiedades o atributos. Sin embargo, para mantener la seguridad de los subprocesos, los scripts que se ejecutan en paralelo generalmente no pueden escribir en el aplicación de modeladode datos. Por lo tanto, el uso directo del modelo de datos para la comunicación viene con restricciones y puede obligar a los scripts a sincronizarse con frecuencia, lo que puede afectar el rendimiento de sus scripts
Ejemplos
Validación de Raycasting del lado del servidor
Para una experiencia de lucha y batalla, necesitas habilitar raycasting para las armas de tus usuarios. Con el cliente simulando las armas para alcanzar una buena latencia, el servidor tiene que confirmar el golpe, lo que implica hacer raycasts y una cantidad de heurísticas que calculan la velocidad esperada del personaje y se basan en el comportamiento pasado.
En lugar de usar un solo script centralizado que se conecta a un evento remoto que los clientes usan para comunicar información de hit, puede ejecutar cada proceso de validación de hit en el lado del servidor en paralelo con cada personaje de usuario teniendo un evento remoto separado.
El script del lado del servidor que se ejecuta bajo el Actor de ese personaje se conecta a este evento remoto utilizando una conexión paralela para ejecutar la lógica relevante para confirmar el golpe. Si la lógica encuentra una confirmación de un golpe, se deduce el daño, lo que implica cambiar propiedades, por lo que se ejecuta en serie inicialmente.
local tool = script.Parent.Parent
local remoteEvent = Instance.new("RemoteEvent") -- Crea un nuevo evento remoto y úsalo como padre de la herramienta
remoteEvent.Name = "RemoteMouseEvent" -- Cámbialo para que el script local pueda buscarlo
remoteEvent.Parent = tool
local remoteEventConnection -- Crea una referencia para la conexión del evento remoto
-- Función que escucha un evento remoto
local function onRemoteMouseEvent(player: Player, clickLocation: CFrame)
-- SERIAL: Ejecutar el código de configuración en serial
local character = player.Character
-- Ignora el personaje del usuario mientras intersección rayo-superficie, emisión de rayos
local params = RaycastParams.new()
params.FilterType = Enum.RaycastFilterType.Exclude
params.FilterDescendantsInstances = { character }
-- PARALELO: Realice el raycast en paralelo
task.desynchronize()
local origin = tool.Handle.CFrame.Position
local epsilon = 0.01 -- Se usa para extender el rayo ligeramente, ya que la ubicación del clic puede estar ligeramente desplazada del objeto
local lookDirection = (1 + epsilon) * (clickLocation.Position - origin)
local raycastResult = workspace:Raycast(origin, lookDirection, params)
if raycastResult then
local hitPart = raycastResult.Instance
if hitPart and hitPart.Name == "block" then
local explosion = Instance.new("Explosion")
-- SERIAL: El código a continuación modifica el estado fuera del actor
task.synchronize()
explosion.DestroyJointRadiusPercent = 0 -- Haz que la explosión no sea mortal
explosion.Position = clickLocation.Position
-- Múltiples actores podrían obtener la misma parte en un raycast y decidir destruirla
-- Esto es perfectamente seguro, pero resultaría en dos explosiones a la vez en lugar de una
-- Las siguientes pruebas dobles comprobar que la ejecución llegó a esta parte primero
if hitPart.Parent then
explosion.Parent = workspace
hitPart:Destroy() -- Destruyelo
end
end
end
end
-- Conecta el señal en serie inicialmente, ya que algunos códigos de configuración no son capaces de ejecutarse en paralelo
remoteEventConnection = remoteEvent.OnServerEvent:Connect(onRemoteMouseEvent)
Generación de terreno procedural del lado del servidor
Para crear un vast mundo para tu experiencia, puedes llenar el mundo dinámicamente. La generación procedural generalmente crea pedazos de terreno independientes, con el generador realizando cálculos relativamente complejos para colocar objetos, usar materiales y llenar voxel. Ejecutar el código de generación en paralelo puede mejorar la eficiencia del proceso. La siguiente muestra de código sirve como ejemplo.
-- La ejecución paralela requiere el uso de actores
-- Este script se clona; el original inicia el proceso, mientras que los clones actúan como trabajadores
local actor = script:GetActor()
if actor == nil then
local workers = {}
for i = 1, 32 do
local actor = Instance.new("Actor")
script:Clone().Parent = actor
table.insert(workers, actor)
end
-- Padre todos los actores debajo de sí mismo
for _, actor in workers do
actor.Parent = script
end
-- Instruye a los actores para generar terreno enviando mensajes
-- En este ejemplo, los actores se eligen aleatoriamente
task.defer(function()
local rand = Random.new()
local seed = rand:NextNumber()
local sz = 10
for x = -sz, sz do
for y = -sz, sz do
for z = -sz, sz do
workers[rand:NextInteger(1, #workers)]:SendMessage("GenerateChunk", x, y, z, seed)
end
end
end
end)
-- Salir del scriptoriginal; el resto del código se ejecuta en cada actor
return
end
function makeNdArray(numDim, size, elemValue)
if numDim == 0 then
return elemValue
end
local result = {}
for i = 1, size do
result[i] = makeNdArray(numDim - 1, size, elemValue)
end
return result
end
function generateVoxelsWithSeed(xd, yd, zd, seed)
local matEnums = {Enum.Material.CrackedLava, Enum.Material.Basalt, Enum.Material.Asphalt}
local materials = makeNdArray(3, 4, Enum.Material.CrackedLava)
local occupancy = makeNdArray(3, 4, 1)
local rand = Random.new()
for x = 0, 3 do
for y = 0, 3 do
for z = 0, 3 do
occupancy[x + 1][y + 1][z + 1] = math.noise(xd + 0.25 * x, yd + 0.25 * y, zd + 0.25 * z)
materials[x + 1][y + 1][z + 1] = matEnums[rand:NextInteger(1, #matEnums)]
end
end
end
return {materials = materials, occupancy = occupancy}
end
-- Vincula el callback para que se ejecute en paralelo en el contexto de ejecución
actor:BindToMessageParallel("GenerateChunk", function(x, y, z, seed)
local voxels = generateVoxelsWithSeed(x, y, z, seed)
local corner = Vector3.new(x * 16, y * 16, z * 16)
-- Actualmente, WriteVoxels() debe ser llamado en la fase serial
task.synchronize()
workspace.Terrain:WriteVoxels(
Region3.new(corner, corner + Vector3.new(16, 16, 16)),
4,
voxels.materials,
voxels.occupancy
)
end)
Mejores Prácticas
Para aplicar los máximos beneficios de la programación paralela, consulte las siguientes mejores prácticas al agregar su código Lua:
Evita cálculos largos — Incluso en paralelo, los cálculos largos pueden bloquear la ejecución de otros scripts y causar retraso. Evite usar programación paralela para manejar un gran volumen de cálculos largos y inflexibles.