MAATRIX GAMES / Блог / FiveM: настройка спавнов и системы возрождения

FiveM: настройка спавнов и системы возрождения

MAATRIX GAMES

Игрок умирает в перестрелке, жмёт «Возродиться» — и оказывается либо голым в чистом поле без единого предмета, либо мгновенно на ногах у той же точки, будто ничего не было. Оба варианта убивают RP: первый бесит игроков технически, второй убивает саму идею смерти как последствия. Разберём, как собрать нормальную цепочку «смерть → больница → штраф → возрождение», которая работает на ESX и QBCore и не разваливается при большом онлайне.

Как вообще устроен спавн в FiveM

Базовый механизм спавна в FiveM держится на ресурсе spawnmanager — он идёт в стандартном наборе большинства сборок и отвечает за то, что происходит с игроком в момент коннекта или возрождения. Ключевые exports, с которыми вы будете работать:

-- отключаем автоспавн, чтобы фреймворк сам решал, где заспавнить игрока
exports.spawnmanager:setAutoSpawn(false)

-- задаём точку и коллбэк для конкретного спавна
exports.spawnmanager:spawnPlayer({
    x = -269.4, y = -955.3, z = 31.2, heading = 205.0,
    model = `mp_m_freemode_01`
}, function()
    TriggerEvent('playerSpawned')
end)

Важный момент: если setAutoSpawn(true) — spawnmanager сам выберет случайную точку из встроенного списка стандартных локаций GTA V, и вы получите спавн где попало (иногда в океане или на крыше). На RP-сервере это почти всегда не то, что нужно — отключайте автоспавн и передавайте координаты вручную.

Фреймворки (ESX, QBCore) оборачивают spawnmanager своей логикой: ESX слушает событие esx:onPlayerSpawn/esx:playerLoaded, QBCore — QBCore:Client:OnPlayerLoaded и QBCore:Client:OnGameEventTriggered для смерти. Если пишете кастомную систему спавна с нуля, отталкивайтесь от этих событий, а не от сырых GTA-нативов напрямую — фреймворк уже решает половину проблем с загрузкой персонажа и синхронизацией позиции.

Если сервер только разворачивается, сначала убедитесь, что база стоит и работает штатно — этот гайд предполагает, что у вас уже пройдена установка FiveM-сервера с нуля и выбран фреймворк, будь то ESX или QBCore — команды и таблицы ниже написаны под оба, но имена полей отличаются.

Возрождение в больнице: базовая механика ESX/QBCore

Стандартный сценарий RP-сервера — смерть от урона переводит персонажа в состояние "без сознания" (ragdoll/лежит), включается обратный отсчёт, а по истечении таймера или при подходе медика игрок либо возрождается на месте, либо телепортируется в больницу. За это отвечают esx_ambulancejob на ESX и qb-ambulancejob на QBCore — оба ресурса хранят точки больниц в config.lua.

Пример конфига точки больницы (структура похожа в обоих фреймворках, отличаются только имена таблиц):

Config.Hospitals = {
    ['pillbox'] = {
        label = 'Pillbox Hospital',
        blip = { x = 298.7, y = -584.9, z = 43.3 },
        spawn = vector4(298.7, -584.9, 43.3, 70.6), -- дефолтная точка возрождения
        cam = { x = 298.9, y = -589.3, z = 44.0 },
    },
}

Координаты — это стандартная точка входа в Pillbox Hospital, которая кочует почти во всех дефолтных сборках ESX/QBCore; если у вас кастомная MLO-карта больницы, замените их на реальный вход своей модели.

Логика возрождения на сервере обычно выглядит так:

RegisterNetEvent('hospital:server:revivePlayer', function()
    local src = source
    local xPlayer = ESX.GetPlayerFromId(src)

    TriggerClientEvent('hospital:client:revive', src, Config.Hospitals['pillbox'].spawn)
    -- штраф и лечение — см. следующий раздел
end)

На клиенте возрождение — это снятие ragdoll-флага, восстановление здоровья и телепорт нативами NetworkResurrectLocalPlayer и SetEntityCoords:

RegisterNetEvent('hospital:client:revive', function(coords)
    local ped = PlayerPedId()
    NetworkResurrectLocalPlayer(coords.x, coords.y, coords.z, coords.w, true, false)
    SetEntityHealth(ped, 200)
    ClearPedBloodDamage(ped)
    DoScreenFadeIn(1000)
end)

Обратите внимание на ClearPedBloodDamage — без неё персонаж возрождается со старыми ранами и кровью на текстуре, что смотрится странно после "лечения" в больнице.

Поднять сервер FiveM (GTA V) за пару минут

Готовый образ MAATRIX GAMES: NVMe, AMD EPYC, DDoS-защита, панель управления. Локации UK, US, RU. Оплата картой РФ, СБП или криптой.

Создать сервер

Штраф за респавн: деньги, время, предметы

Смерть без последствий обесценивает весь RP — если умереть ничего не стоит, никто не боится перестрелок и не пытается их избегать. Рабочая система штрафа обычно комбинирует три рычага, и не обязательно включать все сразу:

  1. Денежный штраф (медицинский счёт). Списывается с банковского счёта при возрождении — символизирует оплату лечения.
  2. Таймер возрождения. Игрок не может нажать "Возродиться" сразу, а ждёт отсчёт — это и есть цена смерти во времени, а не только в деньгах.
  3. Потеря предметов/оружия. Часть инвентаря или всё оружие сбрасывается на месте смерти либо конфискуется — сильнее всего ощущается в PvP-ориентированных RP.
RegisterNetEvent('hospital:server:revivePlayer', function()
    local src = source
    local xPlayer = ESX.GetPlayerFromId(src)
    local medBill = 500 -- сумму подбирайте под свою экономику

    if xPlayer.getAccount('bank').money >= medBill then
        xPlayer.removeAccountMoney('bank', medBill)
    else
        -- денег не хватает — не блокируем возрождение, но фиксируем долг
        xPlayer.setMeta('medDebt', (xPlayer.getMeta('medDebt') or 0) + medBill)
    end

    TriggerClientEvent('hospital:client:revive', src, Config.Hospitals['pillbox'].spawn)
end)

Таймер обычно реализуют на клиенте — NUI-оверлей с обратным отсчётом и заблокированными контролами, пока игрок "без сознания":

CreateThread(function()
    local timer = Config.RespawnTimer -- держите в районе 20-90 секунд, это не готовая цифра, подбирайте под темп RP
    while timer > 0 do
        Wait(1000)
        timer -= 1
        SendNUIMessage({ action = 'updateTimer', time = timer })
    end
    SendNUIMessage({ action = 'showRespawnButton' })
end)

Ориентируйтесь не на абсолютную сумму штрафа, а на долю от типичного заработка на сервере — этот же принцип разбирался в статье про штрафы ГИБДД и баланс экономики: если штраф за смерть равен часовой зарплате, игроки будут избегать риска патологически; если это мелочь — смерть перестаёт что-то значить.

Кастомные точки спавна для разных фракций

На развитом RP-сервере не у всех должна быть одна точка возрождения. Полиция логично возрождается у участка, банда — на своей территории, гражданский персонаж — у стандартной больницы. Это не только про иммерсивность: если у вас фракции враждуют за территорию, единая точка спавна превращает больницу в спавн-килл зону.

Конфиг с точками по фракциям:

Config.FactionSpawns = {
    ['police']      = vector4(441.8, -981.4, 30.6, 90.0),
    ['ambulance']   = vector4(294.6, -584.6, 43.2, 70.0),
    ['ballas']      = vector4(-91.2, -1616.9, 26.2, 210.0),
    ['default']     = vector4(298.7, -584.9, 43.3, 70.6),
}

function GetFactionSpawn(job)
    return Config.FactionSpawns[job] or Config.FactionSpawns['default']
end

Серверная сторона при возрождении подставляет нужную точку в зависимости от job.name игрока (ESX) или PlayerData.job.name (QBCore):

RegisterNetEvent('hospital:server:revivePlayer', function()
    local src = source
    local xPlayer = ESX.GetPlayerFromId(src)
    local spawn = GetFactionSpawn(xPlayer.job.name)

    TriggerClientEvent('hospital:client:revive', src, spawn)
end)

Если фракции нелегальные (банды, картели) и завязаны на систему территорий, точку спавна логично привязывать не к статичным координатам в конфиге, а к текущей "домашней" зоне банды — это уже требует связки со системой фракций и банд, где территория хранится в базе и может переходить от одной группировки к другой. В таком случае GetFactionSpawn стоит превратить в запрос к базе, а не в статичную таблицу.

Отдельно продумайте PvP-балансировку: если банда контролирует территорию и там же возрождается после смерти, противник может держать точку под прицелом и фармить respawn kill. Частое решение — временная неуязвимость на 3-5 секунд после спавна или невидимость до первого движения игрока.

Экран выбора персонажа и первый спавн после захода

Первый спавн (не после смерти, а после захода на сервер или выбора персонажа в esx_multicharacter/qb-multicharacter) — отдельный случай. Для нового персонажа нужна точка "по умолчанию", для существующего — последняя сохранённая позиция из базы.

В таблице users (ESX) или players (QBCore) обычно есть поле position (JSON с координатами). Логика:

local function GetSpawnForCharacter(xPlayer)
    local savedPos = xPlayer.get('position') -- уже распарсенный JSON, если персонаж не новый
    if savedPos and savedPos.x then
        return savedPos
    end
    return Config.FactionSpawns['default'] -- новый персонаж — стандартная точка
end

Частая ошибка на этом этапе — телепортировать игрока в сохранённые координаты до того, как загрузился мир вокруг (стрим-зона ещё не подгружена). Персонаж проваливается сквозь текстуры или зависает в воздухе. Решается ожиданием коллизии перед финальным спавном:

local function SafeTeleport(coords)
    RequestCollisionAtCoord(coords.x, coords.y, coords.z)
    local timeout = GetGameTimer() + 5000
    while not HasCollisionLoadedAroundEntity(PlayerPedId()) and GetGameTimer() < timeout do
        Wait(50)
    end
    SetEntityCoords(PlayerPedId(), coords.x, coords.y, coords.z, false, false, false, true)
end

Таймаут в 5 секунд — защита от вечного цикла, если коллизия по какой-то причине не подгрузится; реальное время загрузки у игроков разное.

Частые баги спавна и как их лечить

СимптомВероятная причинаЧто проверить
Игрок падает сквозь карту при спавнеКоллизия не успела загрузитьсяДобавить RequestCollisionAtCoord + ожидание перед телепортом
Спавн под текстурой/в объектеКоординаты не совпадают с актуальной MLO-картойСвериться с реальным входом в модель, а не с дефолтными координатами из другого конфига
Персонаж завис в чёрном экране после возрожденияDoScreenFadeIn не вызван или коллбэк spawnmanager отвалился с ошибкойПроверить консоль на клиенте (F8) на предмет Lua-ошибок в цепочке спавна
Респавн срабатывает дважды подрядСобытие playerSpawned/revivePlayer триггерится и клиентом, и сервером без проверки состоянияДобавить флаг "уже возрождается" и игнорировать повторные вызовы, пока флаг не сброшен
Игрок возрождается не у своей фракции, а в дефолтной точкеjob.name ещё не подгружен на момент спавна (гонка при первом заходе)Дождаться полной загрузки xPlayer/PlayerData перед вызовом GetFactionSpawn
Штраф списывается, а возрождение не происходитОшибка в серверном обработчике до TriggerClientEvent обрывает цепочкуОбернуть списание денег в pcall или проверку и не блокировать сам ивент возрождения

Если баги спавна усиливаются именно при заполненном сервере — это уже смежная тема нагрузки и десинхронизации ресурсов, а не логики спавна как таковой.

Поднять сервер FiveM (GTA V) за пару минут

Готовый образ MAATRIX GAMES: NVMe, AMD EPYC, DDoS-защита, панель управления. Локации UK, US, RU. Оплата картой РФ, СБП или криптой.

Создать сервер

Частые вопросы

Можно ли сделать разное время респавна для разных фракций?

Да, просто храните таймер не как единую константу, а как поле в той же таблице Config.FactionSpawns или в конфиге job'а — например, у медиков (которые и так постоянно рискуют) таймер короче, чем у гражданских.

Стоит ли делать точку возрождения случайной из нескольких вариантов?

Для больницы обычно нет смысла — игроки привыкают к одной точке и это часть RP-идентичности локации. А вот для нелегальных фракций без официальной "базы" рандом между несколькими явками снижает спавн-килл.

Как сделать, чтобы админы возрождались без штрафа и таймера?

Проверяйте xPlayer.getGroup() (ESX) или пермишены ACE перед списанием денег и запуском таймера — если группа admin/superadmin, просто пропускайте эти шаги в обработчике.

Что делать, если после обновления фреймворка (ESX Legacy, QBCore) спавн перестал работать?

Сверьте актуальные названия событий и экспортов в changelog обновления — между версиями меняются имена ивентов (esx:onPlayerSpawn вместо старого esx:onPlayerLoaded в некоторых форках), и кастомный код на старых именах просто перестаёт триггериться без ошибок в консоли.

Нужен ли отдельный ресурс для системы спавна или писать всё в death-скрипте?

Для маленького сервера можно держать в одном ресурсе вместе со смертью/лечением. При росте функционала (фракции, кастомные больницы, VIP-точки) стоит вынести спавн в отдельный ресурс — проще дебажить и обновлять независимо от остальной ambulance-логики.