
Lua es un lenguaje popular en el desarrollo de scripts para FiveM, Garry’s Mod y otros entornos de videojuegos. Sin embargo, una mala gestión de ciclos y threads puede generar problemas de rendimiento. En esta guía, exploraremos técnicas para optimizar ciclos for
, while
, repeat
y mejorar el manejo de Citizen.CreateThread
en FiveM, además de explicar conceptos clave.
Cómo optimizar Iteradores en Lua
Optimizar iteradores en Lua es clave para mejorar la eficiencia de un script, especialmente en entornos donde el rendimiento es crítico, como FiveM.
Impacto en el rendimiento
Cuando recorremos una tabla con pairs
o ipairs
, Lua realiza múltiples operaciones en cada iteración:
- Busca la clave o índice correspondiente.
- Recupera el valor de la tabla.
- Ejecuta el código dentro del bucle.
Si una tabla contiene cientos o miles de elementos, una mala elección del iterador puede causar caídas de FPS, tiempos de respuesta lentos y uso innecesario de CPU.
Optimizar iteradores en Lua es clave para mejorar la eficiencia de un script, especialmente en entornos donde el rendimiento es crítico, como FiveM.
Diferencias clave en optimización
ipairs
es generalmente más rápido quepairs
para listas ordenadas porque usa acceso secuencial a memoria. Recorre todos los elementos de una tabla, sin importar si las claves son números o cadenas. Es adecuado para tablas no ordenadas o asociativas.pairs
puede ser menos eficiente en tablas grandes y no ordenadas, ya que busca elementos de forma más flexible. Itera secuencialmente sobre tablas con claves numéricas consecutivas (arrays). Es más eficiente en estos casos.
Ejemplo de optimización con ipairs
en una gran lista
Ejemplo de pairs
:
local myTable = {a = 1, b = 2, c = 3}
for key, value in pairs(myTable) do
print(key, value)
end
-- Salida
a 1
b 2
c 3
Ejemplo de ipairs
:
local myTable = {10, 20, 30}
for i, v in ipairs(myTable) do
print(i, v)
end
-- Salida
1 10
2 20
3 30
Optimización en el entorno FiveM

Uno de los errores más comunes en FiveM es realizar iteraciones sobre grandes conjuntos de datos en cada frame.
¿Por qué es un problema?
Cada tick ocurre aproximadamente 60 veces por segundo, dependiendo del rendimiento del cliente o servidor. Si dentro de cada uno realizamos operaciones como:
- Recorrer todos los jugadores conectados.
- Llamar a funciones costosas como
GetEntityCoords
,GetPlayerPed
,GetPlayers
. - Enviar datos a la base de datos o al servidor.
Entonces estamos consumiendo recursos de manera innecesaria y afectando el rendimiento general del servidor.
Mal ejemplo (Costoso en rendimiento)
El siguiente código recorre todos los jugadores en cada tick del juego, lo cual es una mala práctica:
Citizen.CreateThread(function()
while true do
for _, player in ipairs(GetPlayers()) do
local ped = GetPlayerPed(player)
local coords = GetEntityCoords(ped)
print("Coordenadas del jugador:", coords)
end
Wait(0) -- Se ejecuta en cada tick
end
end)
🔴 ¿Por qué es malo?
- Se ejecuta sin pausa en cada tick, lo que significa 60 veces por segundo o más.
- Consume muchos recursos, ya que
GetPlayers()
recorre todos los jugadores en cada iteración. GetEntityCoords(ped)
es una llamada costosa y se ejecuta innecesariamente muchas veces.- Si el servidor tiene muchos jugadores, esta carga se multiplica y puede causar lag o crasheos.
Optimización (Reducir la frecuencia de iteración)
Podemos mejorar esto reduciendo la frecuencia de ejecución a una vez por segundo en lugar de en cada tick:
Citizen.CreateThread(function()
while true do
for _, player in ipairs(GetPlayers()) do
local ped = GetPlayerPed(player)
local coords = GetEntityCoords(ped)
print("Coordenadas del jugador:", coords)
end
Wait(1000) -- Se ejecuta cada 1 segundo
end
end)
🟢 ¿Por qué es mejor?
✅ Se ejecuta una vez por segundo en lugar de 60 veces por segundo.
✅ Reduce significativamente la carga en el CPU.
✅ Mantiene la funcionalidad sin afectar el rendimiento.
¿Cuándo usar una iteración en cada tick?
A veces, sí es necesario ejecutar código en cada frame, pero solo en situaciones donde realmente lo necesitemos, como:
✅ Manejo de inputs del jugador (por ejemplo, detección de teclas presionadas).
✅ Renderización o efectos gráficos.
✅ Colisiones en tiempo real.
Si lo que estás haciendo no necesita ejecutarse cada frame, NO USES Wait(0)
. Opta por tiempos de espera más largos (Wait(1000)
, Wait(5000)
, etc.), dependiendo de la necesidad.
Evitar recorrer tablas grandes innecesariamente en LUA
Iterar sobre tablas grandes, como la lista de jugadores o vehículos, puede ser costoso si se hace repetidamente sin necesidad. Optimizar esto puede mejorar considerablemente el rendimiento.
¡No hagas esto!
Citizen.CreateThread(function()
while true do
for _, player in ipairs(GetPlayers()) do
local ped = GetPlayerPed(player)
local coords = GetEntityCoords(ped)
-- Aquí no filtramos si el jugador está cerca
if #(coords - vector3(100.0, 100.0, 100.0)) < 50.0 then
print("Jugador cercano:", player)
end
end
Wait(0)
end
end)
¿Por qué es malo?
- Recorre todos los jugadores, aunque no todos estén cerca.
- Esto genera un costo innecesario al procesar jugadores que no están en la zona relevante.
¡Haz esto!
Citizen.CreateThread(function()
while true do
local myCoords = GetEntityCoords(PlayerPedId()) -- Coordenadas del jugador actual
for _, player in ipairs(GetPlayers()) do
local ped = GetPlayerPed(player)
local coords = GetEntityCoords(ped)
-- Solo procesar jugadores cercanos
if #(coords - myCoords) < 50.0 then
print("Jugador cercano:", player)
end
end
Wait(1000) -- Solo una vez por segundo
end
end)
¿Por qué es mejor?
- Filtra a los jugadores cercanos antes de hacer la iteración, reduciendo el número de jugadores procesados.
- Disminuye el impacto en el rendimiento, procesando solo los jugadores relevantes.
Evitar llamar funciones costosas dentro del ciclo lua
Funciones como GetEntityCoords
y GetPlayerPed
son costosas, especialmente si las llamas repetidamente dentro de un ciclo. Esto afecta el rendimiento y puede ralentizar el servidor.
¡No hagas esto!
Citizen.CreateThread(function()
while true do
for _, player in ipairs(GetPlayers()) do
local ped = GetPlayerPed(player)
local coords = GetEntityCoords(ped)
-- Se llama dos veces a GetEntityCoords, lo cual es ineficiente
if #(coords - vector3(100.0, 100.0, 100.0)) < 10.0 then
print("Jugador cercano")
end
if coords.z > 50 then
print("Jugador en altura")
end
end
Wait(0)
end
end)
¿Por qué es malo?
- Llamar a
GetEntityCoords
dos veces duplicará la carga de la función.
¡Haz esto!
Citizen.CreateThread(function()
while true do
for _, player in ipairs(GetPlayers()) do
local ped = GetPlayerPed(player)
local coords = GetEntityCoords(ped) -- Guardamos el valor en una variable
if #(coords - vector3(100.0, 100.0, 100.0)) < 10.0 then
print("Jugador cercano")
end
if coords.z > 50 then
print("Jugador en altura")
end
end
Wait(1000) -- Solo una vez por segundo
end
end)
¿Por qué es mejor?
- Evita llamadas repetidas a funciones costosas, como
GetEntityCoords
. - Almacena el resultado en una variable y reutilízalo dentro del ciclo.
La clave para optimizar los ciclos de iteración en FiveM es reducir la frecuencia de ejecución, filtrar los datos antes de iterar y evitar funciones costosas dentro de los bucles. Al aplicar estas buenas prácticas, puedes mejorar significativamente el rendimiento de tu servidor y la experiencia de los jugadores.
Consejos clave:
- Controla cuándo se deben ejecutar los ciclos.
- No itera sobre grandes tablas innecesariamente.
- Utiliza
Wait()
para espaciar las iteraciones y reducir la carga.
Lista de Funciones Costosas en un Ciclo en FiveM

A continuación se presenta una lista de funciones que suelen ser costosas cuando se ejecutan en ciclos continuos y deben optimizarse para evitar sobrecargar el servidor:
GetEntityCoords()
: Obtener las coordenadas de una entidad (jugador, vehículo, NPC) es una operación costosa si se realiza con frecuencia.GetPlayerPed()
: Obtener la entidad del jugador puede ser innecesario si se llama constantemente en un ciclo.IsPedInVehicle()
: Verificar si un jugador está dentro de un vehículo es costoso y debe optimizarse para verificarlo solo cuando sea necesario.IsControlPressed()
: Comprobar si una tecla está presionada puede ser costoso si se realiza en cada ciclo sin optimización.Task
funciones (comoTaskWarpPedIntoVehicle()
,TaskCombatPed()
): Las funciones de tareas como las de asignar comportamientos a NPCs o jugadores son costosas y deben usarse con precaución en ciclos.NetworkGetEntityFromNetworkId()
: Si se llama repetidamente, puede afectar la red, especialmente cuando hay muchas entidades activas.SetEntityVisible()
: Cambiar la visibilidad de una entidad puede ser costoso y debe evitarse en ciclos continuos sin necesidad.TriggerEvent()
: Activar eventos repetidamente puede generar una sobrecarga, por lo que es importante optimizar su uso en situaciones necesarias.GetEntityHealth()
: Verificar la salud de una entidad de forma constante puede ser costoso, especialmente en servidores con muchos jugadores y NPCs.SetEntityCoords()
: Mover una entidad de forma continua es costoso y puede generar una alta carga en el servidor.
Casos reales para optimizar tu sevidor de Fivem
Optimizar un sistema para detectar si el Jugador está presionando una tecla solo cuando está cerca de un punto
Ejemplo sin optimización (Mal uso)
Este ejemplo es el código sin optimización, donde verificamos constantemente si el jugador está presionando una tecla sin tener en cuenta si está cerca del punto o no.
Citizen.CreateThread(function()
while true do
-- Verificar si el jugador está presionando la tecla sin importar su ubicación
if IsControlPressed(0, 38) then -- "E" es el código 38 en FiveM
print("Tecla presionada")
end
Wait(0) -- El ciclo se ejecuta en cada tick, sin ningún control
end
end)
¿Por qué es malo?
- El ciclo se ejecuta constantemente en cada tick, lo que es innecesario si el jugador no está cerca del punto de interés.
- Se realiza la verificación de la tecla en cada frame sin tener en cuenta la posición del jugador, lo que incrementa innecesariamente el uso de recursos.
Optimización del Código
La idea es realizar la comprobación solo cuando el jugador esté cerca del punto, en lugar de ejecutarla constantemente. Esto reduce la carga en el servidor y mejora el rendimiento.
Pasos para optimizar:
- Obtener la posición del jugador: Utilizamos
GetEntityCoords
para obtener las coordenadas del jugador y comprobar si está cerca del punto. - Filtrar la verificación de la tecla solo cuando esté cerca: Solo ejecutamos la verificación de la tecla si la distancia entre el jugador y el punto es lo suficientemente pequeña (por ejemplo, dentro de un radio de 10 unidades).
- Controlar la frecuencia de las iteraciones: Añadimos un
Wait()
para espaciar las iteraciones y no ejecutarlo en cada tick.
local puntoDeInteres = vector3(100.0, 200.0, 300.0) -- Coordenadas del punto de interés
local distanciaMaxima = 10.0 -- Radio de proximidad al punto
Citizen.CreateThread(function()
while true do
local playerPed = PlayerPedId()
local playerCoords = GetEntityCoords(playerPed)
local msToWait = 500
-- Verificar si el jugador está cerca del punto
if #(playerCoords - puntoDeInteres) < distanciaMaxima then
-- Solo verificar si la tecla se presiona si el jugador está cerca
if IsControlPressed(0, 38) then -- "E" es el código 38 en FiveM
print("Tecla presionada cerca del punto")
end
-- Disminuimos los MS para que detecte el input al jugador.
msToWait = 5
end
-- Esperar 100 ms para evitar sobrecargar el servidor
Wait(msToWait)
end
end)
¿Por qué es mejor?
- Verificación de proximidad: Solo verificamos si el jugador está presionando la tecla cuando está cerca del punto de interés, lo que reduce la cantidad de veces que se ejecuta la verificación.
- Reducción de ciclos innecesarios: Al añadir
Wait(100)
, se reduce la frecuencia de ejecución, de modo que el código no se ejecuta en cada tick, sino cada 100 ms, lo que es suficiente para la mayoría de los casos. - Menor uso de recursos: Al limitar la verificación solo a cuando es relevante, se ahorra en el uso de recursos y se optimiza el rendimiento del servidor.
Consejo: La clave para optimizar estas funciones es limitar su uso dentro de ciclos y solo ejecutarlas cuando sean necesarias, manteniendo la carga del servidor al mínimo y mejorando el rendimiento del juego.
Explicación del Código Optimizado
- Definir el punto de interés: Se establece un
vector3
con las coordenadas del punto donde queremos que el jugador esté cerca para que se active la verificación de la tecla. - Calcular la distancia: Usamos el operador
#
para calcular la distancia entre el jugador y el punto de interés. Si la distancia es menor que el valor dedistanciaMaxima
(en este caso 10 unidades), procedemos con la verificación de la tecla. - Esperar entre iteraciones: Usamos
Wait(100)
para espaciar las iteraciones, reduciendo el número de veces que se ejecuta el ciclo y evitando el sobreuso de recursos.
La optimización en FiveM es esencial para mejorar el rendimiento del servidor y proporcionar una experiencia de juego fluida. Al optimizar las tareas recurrentes y las interacciones entre el servidor y el cliente, podemos reducir la carga innecesaria y mejorar la eficiencia del sistema. Algunos de los enfoques clave incluyen:
- Optimizar ciclos de iteración: Reducir la frecuencia de ejecución de ciclos innecesarios utilizando
Wait()
y limitando la verificación solo cuando sea necesario, como en el caso de la proximidad a un punto específico. - Optimizar consultas de datos costosos: Llamadas como
GetEntityCoords
deben ser realizadas con precaución, guardando los resultados en variables y evitando llamarlas repetidamente. - Optimizar eventos y callbacks: Evitar la activación de eventos innecesarios y asegurarse de que las funciones asociadas sean lo más eficientes posible.
- Optimizar el procesamiento de IA y NPCs: Limitar las actividades de NPCs a lo estrictamente necesario y solo actualizarlas cuando están cerca de los jugadores.
- Optimizar la gestión de entidades y objetos: Mantener solo las entidades necesarias activas y destruir o desactivar las que no sean relevantes.
- Optimizar la interfaz de usuario (UI): Minimizar las actualizaciones visuales innecesarias y realizar actualizaciones solo cuando sea necesario.
- Optimizar la gestión de jugadores: Limitar las verificaciones de jugadores a las áreas relevantes para reducir la carga en el servidor.
- Optimizar el uso de recursos del servidor: Distribuir las tareas de manera eficiente, monitorear el uso de recursos y liberar memoria cuando ya no se necesite.
¿Ya sabes que framework utilizar? Revisá aquí una comparación entre ESX, QBCore y QBox.