Назад к блогу
Tutorial7 мин чтения

FiveM Racing System: Треки, таблицы лидеров и ставки

Добавьте уличные гонки на ваш сервер FiveM. Конструкторы трасс, контрольные точки, ставки, таблицы лидеров и лучшие скрипты гонок для серьёзных сообществ тюнеров.

Agency Scripts

Основатель и ведущий разработчик Agency Scripts

Проектирование системы создания гонок

Увлекательная система уличных гонок начинается с предоставления игрокам возможности создавать и управлять собственными маршрутами гонок. Вместо использования только заранее заданных трасс лучшие скрипты позволяют организаторам динамически размещать контрольные точки, проезжая по маршруту и отмечая позиции. Каждая гонка хранится как структура данных с уникальным идентификатором, ID создателя, названием, упорядоченным списком координат контрольных точек с углами направления и метаданными, такими как дистанция и предполагаемое время круга. Процесс создания должен быть интуитивным: игрок входит в режим редактора гонок, проезжает к каждой контрольной точке, подтверждает её нажатием клавиши и завершает, вернувшись к стартовой линии. Сохраняйте готовые гонки в базе данных для сохранения между перезапусками сервера и возможности делиться ими с сообществом. Реализуйте систему проверки, где администраторы одобряют маршруты игроков перед появлением в публичном списке гонок, чтобы предотвратить гриферство через невозможные или несправедливые трассы.

Система контрольных точек и логика гонок

Контрольные точки , основной механизм, который подтверждает прогресс гонки и предотвращает сокращения пути. Каждая контрольная точка должна быть определена как 3D-зона с настраиваемым радиусом, обычно от 8 до 15 метров в зависимости от ширины дороги. Используйте нативные функции GTA для отображения маркеров контрольных точек визуально, выделяя текущую контрольную точку ярким цветом, а следующую показывая как прозрачный предварительный просмотр. Отслеживайте прогресс каждого гонщика через индексированный список контрольных точек, продвигая их только при входе в правильную следующую зону по порядку. Реализуйте античит меры, отслеживая время между контрольными точками и помечая или дисквалифицируя гонщиков, которые проходят сегменты с невозможной скоростью, что указывает на телепортацию или использование читов скорости.

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

Таблица лидеров и система ранжирования

Таблицы лидеров стимулируют конкуренцию и заставляют игроков возвращаться, чтобы улучшить свои результаты. Сохраняйте результаты гонок в таблице базы данных, которая записывает идентификатор игрока, ID гонки, время прохождения в миллисекундах, модель транспортного средства, дату и является ли результат личным рекордом. Отображайте таблицы лидеров через интерфейс NUI, показывающий лучшие времена для каждой трассы, с возможностью фильтрации по классу транспортного средства, чтобы игроки могли честно сравнивать результаты в категориях спорт, супер, маслкар или компакт. Реализуйте сезонную систему ранжирования с периодическим сбросом, чтобы поддерживать свежесть соревнований, сохраняя при этом общую таблицу рекордов для исторических достижений. Рассчитывайте рейтинг в стиле ELO на основе личных встреч в многопользовательских гонках, давая опытным гонщикам видимый ранг, который повышается при победах над более высокоранговыми соперниками и понижается при поражениях от низкоранговых. Отображайте этот рейтинг в интерфейсе гонки и используйте его для опционального подбора соперников при организации случайных гонок.

Система ставок и призов

Система ставок добавляет финансовые ставки в гонки и интегрирует гоночную сцену с общей экономикой сервера. Позволяет организаторам гонок устанавливать вступительный взнос, который платят все участники, а общий призовой фонд распределяется между призёрами по настраиваемому соотношению, например 70-20-10 для первого, второго и третьего места. Реализуйте систему ставок для зрителей, где игроки, не участвующие в гонке, могут ставить деньги на выбранного гонщика. Используйте исключительно серверную логику для всех финансовых операций, чтобы предотвратить эксплойты. Добавьте комиссию дома в размере 5-10% со всех ставок как денежный слив для экономики. Для крупных организованных событий разрешите администраторам добавлять бонусные призовые фонды из казны сервера. Отслеживайте статистику ставок по каждому игроку, чтобы выявлять и предотвращать договорные матчи, например, когда гонщик постоянно проигрывает гонки с крупными ставками против него.

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

Создание гоночного UI с помощью NUI

Интерфейс гонок должен передавать критически важную информацию в реальном времени без загромождения экрана и отвлечения водителя. Создайте минималистичный HUD, показывающий текущее место среди гонщиков, номер круга, если применимо, сплит-таймер, сравнивающий текущий заезд с личным рекордом или рекордом трассы, и миникарту с указанием направления следующего чекпойнта. Используйте спидометр, соответствующий теме гонки, показывающий скорость в милях в час или км/ч в зависимости от предпочтений игрока. Экран лобби перед гонкой должен отображать всех зарегистрированных участников с их машинами, название и длину трассы, распределение призового фонда и таймер обратного отсчёта. Создайте UI с помощью HTML, CSS и JavaScript через систему NUI и передавайте обновления состояния гонки с клиентского Lua в NUI-фрейм через SendNUIMessage. Поддерживайте отзывчивость UI, ограничивая обновления позиции до 4-5 раз в секунду вместо каждого кадра, что значительно снижает нагрузку NUI без видимого влияния на отображаемую информацию. Рассмотрите возможность добавления экрана результатов после гонки с подробной статистикой, такой как максимальная достигнутая скорость, средняя скорость и сравнение времени по сегментам контрольных точек.

Категории гонок и классы транспортных средств

Организуйте гонки по категориям для обеспечения честной конкуренции и разнообразия. Определите классы транспортных средств на основе показателей производительности, таких как максимальная скорость, ускорение и характеристики управления, взятые из нативных данных handling игры. Создайте ограничения классов для конкретных гонок, чтобы игрок на Dominator не мог участвовать в гонке, ограниченной компактами, и наоборот. Помимо класса транспортного средства, предложите разные форматы гонок: спринты от точки А до Б по городу, кольцевые гонки с несколькими кругами, драг-рейсы на прямых дорогах с реактивным стартом и дрифт-соревнования, оцениваемые по углу, скорости и близости к контрольным точкам. Каждый формат требует немного разной логики подсчёта очков и определения завершения. Оценка дрифта особенно нуждается в кастомном алгоритме, который учитывает угол скольжения относительно вектора скорости, поддерживаемую скорость и бонусные множители за цепочку последовательных дрифтов без потери контроля. Позвольте организаторам гонок переключать опции, такие как механика догоняния, прохождение сквозь других гонщиков для предотвращения столкновений и настройки погоды или времени суток для атмосферы.

Интеграция полиции и нелегальных гонок

Уличные гонки должны быть незаконной деятельностью, создающей органичные взаимодействия с правоохранительными органами. Когда гонка начинается, генерируются жалобы на шум от NPC в районе, которые появляются в системе диспетчеризации полиции с настраиваемой задержкой, давая гонщикам фору, но гарантируя, что более длинные гонки привлекают внимание. Полицейские, которые реагируют, могут попытаться остановить гонку, развернув шипы, установив блокпосты или преследуя отдельных гонщиков. Если гонщик пойман полицией, его ждут штрафы, конфискация автомобиля и возможно тюремное заключение, добавляя реальные последствия, которые делают сцену гонок опасной и захватывающей. Реализуйте систему уровня розыска специально для гонок, которая эскалируется при повторных нарушениях, чтобы хронические уличные гонщики привлекали более агрессивные реакции полиции со временем. Позвольте полиции использовать камеры наблюдения на известных местах гонок для сбора доказательств против организаторов, создавая слой расследования, который вознаграждает терпеливую работу полиции крупными задержаниями, которые разрушают целые гонщицкие банды, а не отдельных водителей.

Оптимизация производительности для гонок

Гоночные системы требуют плавной работы, так как даже небольшие задержки могут испортить соревновательный опыт. Минимизируйте коммуникацию сервер-клиент во время активных гонок, обрабатывая обнаружение чекпоинтов и отслеживание позиций на стороне клиента, отправляя обновления на сервер только при достижении чекпоинта или завершении гонки. Используйте entity state bags для передачи позиций вместо пользовательских сетевых событий, так как state bags оптимизированы для частых обновлений и эффективнее используют пропускную способность. Для отображения чекпоинтов рисуйте маркеры только для текущего и следующего чекпоинта, а не для всех одновременно, и применяйте логику LOD (уровень детализации) для уменьшения сложности маркеров на больших расстояниях. Объединяйте запросы данных гонок, получая таблицы лидеров и списки гонок пакетно, а результаты кешируйте на клиенте с коротким TTL для снижения нагрузки на базу данных в часы пик. Тестируйте систему с максимальным ожидаемым количеством одновременных гонщиков, обычно 8-16 на гонку, чтобы убедиться, что синхронизация позиций и проверка чекпоинтов масштабируются без падений FPS и рассинхронизации.

Готовы начать?

Возьмите скрипты в нашем магазине или заходите в Discord за поддержкой, обновлениями и анонсами.