Sistema de carreras para FiveM: circuitos, clasificaciones y apuestas
Añade carreras callejeras a tu servidor de FiveM. Crea circuitos, puntos de control, apuestas y clasificaciones para comunidades de tuning.
Agency Scripts
Fundador y desarrollador principal en Agency Scripts
Diseño del sistema de creación de carreras
Un sistema atractivo de carreras callejeras comienza dando a los jugadores la posibilidad de crear y gestionar sus propias rutas. En lugar de depender únicamente de circuitos predefinidos, los mejores scripts de carreras permiten que los organizadores coloquen puntos de control de forma dinámica mientras conducen por una ruta y marcan posiciones. Guarda cada carrera como una estructura de datos que contenga un identificador único, el ID del jugador que la creó, un nombre, la lista ordenada de coordenadas de los puntos de control con ángulos de orientación y metadatos como la distancia y el tiempo estimado por vuelta. El proceso de creación debe ser intuitivo: el jugador entra en el modo de edición de carreras, conduce hasta cada punto de control, confirma su ubicación con una tecla y termina volviendo a la línea de salida. Guarda las carreras completadas en tu base de datos para que persistan tras los reinicios del servidor y puedan compartirse con la comunidad. Implementa un sistema de revisión en el que los administradores puedan aprobar las rutas creadas por los jugadores antes de que aparezcan en la lista pública de carreras, evitando así el griefing con diseños de circuitos imposibles o injustos.
Sistema de puntos de control y lógica de carreras
Los puntos de control son la mecánica principal que valida el progreso de la carrera e impide tomar atajos. Cada punto de control debe definirse como una zona 3D con un radio configurable, normalmente de entre 8 y 15 metros, según la anchura de la carretera. Usa las funciones nativas de GTA para dibujar marcadores y mostrar los puntos de control visualmente, con el punto actual resaltado en un color brillante y el siguiente como una vista previa transparente. Registra el progreso de cada corredor mediante una lista indexada de puntos de control y haz que avancen únicamente cuando entren en la zona del siguiente punto de control correcto, en el orden correspondiente. Implementa medidas antitrampas registrando el tiempo entre puntos de control y marcando o descalificando a los corredores que completen tramos en tiempos imposibles, lo que indicaría teletransportación o trampas de velocidad.
-- Client-side checkpoint detection
local currentCheckpoint = 1
local raceCheckpoints = {} -- populated when race starts
CreateThread(function()
while isRacing do
local playerCoords = GetEntityCoords(PlayerPedId())
local checkpoint = raceCheckpoints[currentCheckpoint]
if checkpoint then
local dist = #(playerCoords - checkpoint.coords)
-- Draw current checkpoint marker
DrawMarker(1, checkpoint.coords.x, checkpoint.coords.y,
checkpoint.coords.z - 1.0, 0, 0, 0, 0, 0, 0,
checkpoint.radius * 2, checkpoint.radius * 2, 2.0,
45, 212, 191, 120, false, true, 2, false, nil, nil, false)
if dist < checkpoint.radius then
PlaySoundFrontend(-1, 'CHECKPOINT_NORMAL', 'HUD_MINI_GAME_SOUNDSET', true)
currentCheckpoint = currentCheckpoint + 1
if currentCheckpoint > #raceCheckpoints then
-- Race finished
local finishTime = GetGameTimer() - raceStartTime
TriggerServerEvent('racing:finished', raceId, finishTime)
isRacing = false
else
-- Notify server of checkpoint progress
TriggerServerEvent('racing:checkpoint', raceId, currentCheckpoint)
end
end
end
Wait(50)
end
end)
Sistema de clasificación y tabla de posiciones
Las tablas de posiciones fomentan la competencia y hacen que los jugadores vuelvan para mejorar sus tiempos. Guarda los resultados de las carreras en una tabla de la base de datos que registre el identificador del jugador, el ID de la carrera, el tiempo de finalización en milisegundos, el modelo del vehículo utilizado, la fecha y si la marca fue un récord personal. Muestra las tablas de posiciones mediante una interfaz NUI que presente los mejores tiempos de cada circuito, con filtros por clase de vehículo para que los jugadores puedan comparar resultados de forma justa en categorías como deportivos, superdeportivos, muscle o compactos. Implementa un sistema de clasificación por temporadas que se reinicie periódicamente para mantener la competencia fresca, a la vez que conservas una tabla de récords históricos. Calcula una clasificación de carreras al estilo ELO a partir del rendimiento en enfrentamientos directos en carreras multijugador, dando a los corredores expertos un rango visible que aumenta cuando vencen a rivales de mayor rango y disminuye cuando pierden contra rivales de menor rango. Muestra esta clasificación en la interfaz de carreras y úsala para el emparejamiento opcional al organizar carreras aleatorias.
Sistema de apuestas y premios
Un sistema de apuestas añade dinero en juego a las carreras e integra la escena de las carreras con la economía general de tu servidor. Permite que los organizadores fijen una cuota de inscripción que deban pagar todos los participantes, y distribuye el bote total entre los primeros puestos según un reparto configurable, como 70-20-10 para el primer, segundo y tercer lugar. Implementa un sistema de apuestas para espectadores en el que los jugadores que no compiten puedan apostar por el resultado y elegir a qué corredor apoyar con su dinero. Usa exclusivamente la lógica del servidor para todas las transacciones financieras y así evitar exploits. Añade una comisión de la casa del 5-10% a todas las apuestas para que actúe como sumidero de dinero de tu economía. En eventos organizados de mayor envergadura, permite que los administradores añadan fondos de premios extra financiados por el tesoro del servidor. Registra las estadísticas de apuestas de cada jugador para detectar y prevenir patrones de amaño, como que un corredor pierda constantemente carreras en las que se han realizado grandes apuestas en su contra.
-- Server-side betting handler
local raceBets = {}
RegisterNetEvent('racing:placeBet')
AddEventHandler('racing:placeBet', function(raceId, targetRacerId, amount)
local src = source
if not activeRaces[raceId] then return end
if activeRaces[raceId].state ~= 'lobby' then
return notify(src, 'Betting is closed once the race starts.', 'error')
end
-- Validate the target racer is in this race
local racerFound = false
for _, racer in ipairs(activeRaces[raceId].participants) do
if racer.src == targetRacerId then racerFound = true break end
end
if not racerFound then return end
-- Check and deduct funds
local playerMoney = GetPlayerMoney(src)
if playerMoney < amount or amount < Config.MinBet then
return notify(src, 'Insufficient funds or below minimum bet.', 'error')
end
RemoveMoney(src, amount)
if not raceBets[raceId] then raceBets[raceId] = {} end
table.insert(raceBets[raceId], {
bettor = src,
target = targetRacerId,
amount = amount,
})
notify(src, ('Bet $%s on %s'):format(amount, GetPlayerName(targetRacerId)), 'success')
end)
Creación de la interfaz de carreras con NUI
La interfaz de carreras debe comunicar información importante en tiempo real sin recargar la pantalla ni distraer al conductor. Diseña un HUD minimalista que muestre la posición actual entre los corredores, el número de vuelta si corresponde, un cronómetro parcial que compare la carrera actual con el récord personal o del circuito y una superposición del minimapa que indique la dirección del próximo punto de control. Usa un velocímetro que combine con la temática de la carrera y muestre la velocidad en mph o km/h, según las preferencias del jugador. La pantalla del lobby previa a la carrera debe mostrar a todos los participantes inscritos con sus vehículos, el nombre y la distancia del circuito, el desglose del bote de premios y un temporizador de cuenta regresiva. Crea la interfaz con HTML, CSS y JavaScript mediante el sistema NUI y comunica las actualizaciones del estado de la carrera desde Lua en el cliente al marco NUI mediante SendNUIMessageMantén la interfaz adaptable limitando las actualizaciones de posición a 4-5 veces por segundo en lugar de actualizarlas en cada fotograma. Así reducirás significativamente la carga de NUI sin ningún impacto visible en la información mostrada. Considera añadir una pantalla de resultados posterior a la carrera con estadísticas detalladas, como la velocidad máxima alcanzada, la velocidad media y la comparación de tiempos por tramo entre puntos de control.
Categorías de carreras y clases de vehículos
Organiza las carreras en categorías distintas para garantizar una competición justa y variada. Define las clases de vehículos según parámetros de rendimiento como la velocidad máxima, la aceleración y las valoraciones de manejo obtenidas de los datos nativos de handling del juego. Establece restricciones de clase para carreras específicas, de modo que un jugador con un Dominator no pueda participar en una carrera reservada para compactos, ni viceversa. Además de las clases de vehículos, ofrece distintos formatos de carrera: sprints punto a punto que van de A a B por la ciudad, carreras de circuito con varias vueltas alrededor de un trazado cerrado, carreras de aceleración en carreteras rectas con salidas basadas en el tiempo de reacción y competiciones de derrapes puntuadas según el ángulo, la velocidad y la proximidad a los puntos de clipping. Cada formato requiere una lógica algo distinta para la puntuación y la detección de finalización. En particular, la puntuación de derrapes requiere un algoritmo personalizado que evalúe el ángulo de deslizamiento del vehículo con respecto a su vector de velocidad, la velocidad mantenida durante el derrape y los multiplicadores extra por encadenar derrapes consecutivos sin perder el control. Permite que los organizadores de carreras activen o desactiven opciones como las mecánicas de recuperación, el paso fantasma a través de otros corredores para evitar colisiones intencionadas y los ajustes de clima o de hora del día para crear ambiente.
Integración policial y carreras ilegales
Las carreras callejeras deben ser una actividad ilegal que genere interacciones espontáneas con las fuerzas del orden. Cuando comience una carrera, genera denuncias por ruido de los NPC de la zona que aparezcan en el sistema de despacho policial tras un retraso configurable. Así los corredores tendrán una ventaja inicial, pero las carreras largas acabarán atrayendo atención. Los agentes que acudan pueden intentar detener la carrera desplegando bandas de clavos, montando controles de carretera o persiguiendo a corredores concretos. Si la policía atrapa a un corredor, este se enfrentará a multas, a la incautación del vehículo y posiblemente a tiempo en prisión, lo que añade consecuencias reales y hace que las carreras parezcan peligrosas y emocionantes. Implementa un sistema de nivel de búsqueda específico para las carreras, que aumente con las infracciones reiteradas para que los corredores callejeros habituales atraigan respuestas policiales cada vez más agresivas. Permite que la policía use cámaras de vigilancia en lugares conocidos por las carreras para reunir pruebas contra los organizadores y crear una capa de investigación que recompense el trabajo policial paciente con operativos importantes que desmantelen grupos enteros de corredores en lugar de detener a conductores individuales.
Optimización del rendimiento de las carreras
Los sistemas de carreras requieren un rendimiento fluido, ya que incluso un retraso menor puede arruinar la experiencia competitiva. Minimiza la comunicación entre el servidor y el cliente durante las carreras activas gestionando la detección de puntos de control y el seguimiento de posiciones en el lado del cliente, y enviando actualizaciones al servidor únicamente cuando se alcance un punto de control o termine la carrera. Usa state bags de entidades para difundir las posiciones en lugar de eventos de red personalizados, ya que están optimizados para actualizaciones frecuentes y gestionan el ancho de banda de forma más eficiente. Para dibujar los puntos de control, muestra solo el actual y el siguiente en lugar de todos a la vez, y usa lógica LOD (Level of Detail) para reducir la complejidad de los marcadores a mayor distancia. Agrupa las solicitudes de datos de carreras obteniendo las tablas de posiciones y las listas de carreras en operaciones por lotes en lugar de realizar consultas individuales, y almacena los resultados en caché en el cliente con un TTL breve para reducir la carga de la base de datos durante las horas punta. Prueba el sistema con el número máximo previsto de corredores simultáneos, normalmente 8-16 por carrera, para asegurarte de que la sincronización de posiciones y la validación de puntos de control escalen correctamente, sin caídas de fotogramas ni problemas de desincronización.