Скрипт системы поездов для FiveM: кастомные железнодорожные маршруты и станции
Оживите поезда в FiveM. Пользовательские маршруты, управляемые локомотивы, пассажирские места и лучшие скрипты поездов для погружения в транспортный ролевой игровой процесс.
Agency Scripts
Основатель и ведущий разработчик Agency Scripts
Общественный транспорт как инфраструктура сервера
Система поездов и общественного транспорта превращает ваш FiveM сервер из ориентированного на автомобили города в живой мегаполис с функциональным массовым транспортом. Поезда ходят по расписанию по существующей железнодорожной сети GTA, автобусы следуют маршрутам по городским улицам, а станции метро обеспечивают быструю перевозку между районами, создавая ощущение живого мира. Общественный транспорт полезен всем игрокам независимо от богатства или профессии. Новички, не имеющие машины, могут добираться до работы на автобусе. Опытные игроки используют метро в часы пик, чтобы избежать пробок. Преступники перемещаются по городу без отслеживаемого личного транспорта. Система создаёт уникальные рабочие места для водителей автобусов, машинистов поездов и сотрудников транспортной службы, которые поддерживают расписание, собирают оплату и управляют инцидентами. С технической точки зрения, поезда и автобусы, движущиеся по фиксированным маршрутам, наполняют мир живыми сущностями, делая город оживлённым без необходимости увеличения числа игроков.
Настройка маршрута и расписания поезда
В GTA V есть встроенная железнодорожная сеть, которая окружает карту, и ваша система поездов должна использовать эти существующие пути для максимальной совместимости. Определяйте маршруты поездов как последовательности остановок на станции вдоль пути с расчетом времени прибытия и отправления на основе средней скорости поезда между станциями. Каждая станция нуждается в зоне платформы, где игроки садятся и выходят, точке взаимодействия с билетным автоматом и табло с предстоящими прибытием. Настройте несколько поездов, которые работают одновременно на разных сегментах сети, с расписаниями, повторяющимися по настраиваемому циклу, соответствующему прогрессии дня и ночи на вашем сервере:
Config.TrainSystem = {
ticketPrice = 50,
speedMultiplier = 1.0,
despawnDistance = 500.0,
announceArrival = true,
announceDeparture = true,
}
Config.TrainRoutes = {
['metro_line_1'] = {
label = 'Metro Line 1 - City Loop',
model = 'metrotrain',
carriages = 3,
frequency = 300, -- seconds between departures
stations = {
{
name = 'Los Santos Central',
coords = vector3(268.09, -1204.28, 38.90),
platform = vector3(264.55, -1200.12, 38.90),
stopDuration = 20,
announceText = 'Now arriving at Los Santos Central Station',
},
{
name = 'Strawberry',
coords = vector3(36.81, -1393.78, 29.36),
platform = vector3(40.22, -1389.45, 29.36),
stopDuration = 15,
announceText = 'Now arriving at Strawberry Station',
},
{
name = 'Del Perro',
coords = vector3(-1359.88, -474.17, 15.02),
platform = vector3(-1355.44, -470.89, 15.02),
stopDuration = 15,
announceText = 'Now arriving at Del Perro Station',
},
{
name = 'Rockford Hills',
coords = vector3(-512.47, -680.33, 33.42),
platform = vector3(-508.12, -676.88, 33.42),
stopDuration = 15,
announceText = 'Now arriving at Rockford Hills Station',
},
{
name = 'Burton',
coords = vector3(-283.66, -324.78, 10.07),
platform = vector3(-279.33, -321.44, 10.07),
stopDuration = 15,
announceText = 'Now arriving at Burton Station',
},
},
},
}
Config.BusRoutes = {
['route_1'] = {
label = 'Route 1 - Downtown Express',
model = 'bus',
frequency = 180,
stops = {
{ name = 'Legion Square', coords = vector3(208.24, -935.88, 30.69),
stopDuration = 12 },
{ name = 'Pillbox Hospital', coords = vector3(311.44, -592.33, 43.29),
stopDuration = 12 },
{ name = 'Alta Street', coords = vector3(-226.87, -381.44, 30.05),
stopDuration = 12 },
{ name = 'Vinewood Blvd', coords = vector3(302.33, 193.67, 104.38),
stopDuration = 12 },
},
},
}
Параметр frequency значение определяет, как часто новый поезд отправляется с первой станции на маршруте. Несколько поездов могут одновременно работать на одном маршруте, разделённые интервалом частоты. Значение stopDuration на каждой станции даёт игрокам достаточно времени для посадки или выхода, с аудиообъявлением при прибытии поезда и звуковым предупреждением за несколько секунд до отправления.
Создание и перемещение сущности поезда
Спавн и управление поездами в FiveM требует специфических нативных функций, отличающихся от обычного управления транспортом. Используйте CreateMissionTrain для спавна поездов на железнодорожной сети, которые автоматически размещаются на ближайшем пути и управляют физикой движения по рельсам. Управляйте скоростью поезда с помощью SetTrainSpeed и SetTrainCruiseSpeed и останавливать поезда на станциях, постепенно снижая скорость по мере приближения к координатам платформы. Поезд должен остановиться точно на позиции платформы, чтобы двери совпадали с зонами посадки, что требует тщательных расчетов снижения скорости на основе расстояния до следующей станции:
local activeTrains = {}
function SpawnTrain(routeId)
local route = Config.TrainRoutes[routeId]
if not route then return end
local firstStation = route.stations[1]
local variation = 24 -- metro train variation
-- Create the mission train
local train = CreateMissionTrain(variation, firstStation.coords.x,
firstStation.coords.y, firstStation.coords.z, true)
if not DoesEntityExist(train) then
print('[Transit] Failed to spawn train for route: ' .. routeId)
return
end
SetTrainSpeed(train, 0.0)
SetTrainCruiseSpeed(train, 0.0)
SetEntityAsMissionEntity(train, true, true)
-- Store train state
activeTrains[routeId] = {
entity = train,
route = route,
currentStation = 1,
state = 'stopped', -- stopped, departing, moving, arriving
stateTimer = GetGameTimer(),
passengers = {},
}
return train
end
-- Train movement controller
CreateThread(function()
while true do
for routeId, trainData in pairs(activeTrains) do
local train = trainData.entity
if not DoesEntityExist(train) then
activeTrains[routeId] = nil
goto continue
end
local station = trainData.route.stations[trainData.currentStation]
local elapsed = GetGameTimer() - trainData.stateTimer
if trainData.state == 'stopped' then
if elapsed >= (station.stopDuration * 1000) then
-- Depart from station
trainData.state = 'departing'
trainData.stateTimer = GetGameTimer()
AnnounceToPassengers(trainData, 'Doors closing. Next stop: '
.. GetNextStationName(trainData))
end
elseif trainData.state == 'departing' then
local speed = math.min(15.0, elapsed * 0.005)
SetTrainCruiseSpeed(train, speed)
SetTrainSpeed(train, speed)
if speed >= 15.0 then
trainData.state = 'moving'
end
elseif trainData.state == 'moving' then
local nextIdx = trainData.currentStation + 1
if nextIdx > #trainData.route.stations then
nextIdx = 1
end
local nextStation = trainData.route.stations[nextIdx]
local trainCoords = GetEntityCoords(train)
local dist = #(trainCoords - nextStation.coords)
if dist < 200.0 then
trainData.state = 'arriving'
trainData.stateTimer = GetGameTimer()
AnnounceToPassengers(trainData, nextStation.announceText)
end
elseif trainData.state == 'arriving' then
local nextIdx = trainData.currentStation + 1
if nextIdx > #trainData.route.stations then
nextIdx = 1
end
local nextStation = trainData.route.stations[nextIdx]
local trainCoords = GetEntityCoords(train)
local dist = #(trainCoords - nextStation.coords)
-- Gradually slow down
local speed = math.max(0.0, dist * 0.08)
SetTrainCruiseSpeed(train, speed)
SetTrainSpeed(train, speed)
if dist < 3.0 then
SetTrainSpeed(train, 0.0)
SetTrainCruiseSpeed(train, 0.0)
trainData.currentStation = nextIdx
trainData.state = 'stopped'
trainData.stateTimer = GetGameTimer()
end
end
:continue:
end
Wait(100)
end
end)
Пассажирская посадка и продажа билетов
Когда поезд останавливается на станции, показывайте подсказки взаимодействия игрокам поблизости, позволяя им сесть. Проверьте, что у игрока есть действительный билет или проездной, прежде чем разрешить посадку. Билеты можно купить у автоматов на каждой станции через простой NUI-интерфейс с отображением маршрутов и цен. После посадки телепортируйте игрока внутрь вагона и установите его позицию относительно объекта поезда, чтобы он двигался вместе с ним. Встроенные интерьеры поездов GTA подходят для базовых реализаций, но пользовательские интерьеры с прикреплёнными объектами или инстансами обеспечивают лучший опыт с сиденьями, стоячими зонами и окнами, показывающими проносящийся пейзаж. Отслеживайте, на какой станции каждый пассажир сел, чтобы система могла рассчитывать разницу в цене за расстояние. Показывайте название следующей станции и предполагаемое время прибытия внутри вагона через 3D-текст или прикреплённый NUI-дисплей, который обновляется по мере движения поезда.
Автобусная система и NPC-водители
Автобусы дополняют железнодорожную сеть, покрывая зоны, куда не доходят поезда. Спавните автобусы с NPC-водителями, которые следуют по заранее заданным маршрутам по городским улицам, останавливаясь на отмеченных остановках для посадки и высадки игроков. NPC-водитель использует систему задач GTA для навигации между остановками с помощью TaskVehicleDriveToCoordLongrange с соответствующими флагами вождения для соблюдения правил дорожного движения и остановок в назначенных местах. На каждой остановке автобус ожидает заданное время, пока игроки садятся или выходят, затем NPC-водитель продолжает путь к следующей остановке. Отображайте информацию о маршруте на объектах автобусных остановок, показывая номер маршрута, пункт назначения и предполагаемое время ожидания следующего автобуса. Для варианта с управлением игроком разрешите игрокам с работой в транспортной службе водить автобусы вручную, зарабатывая оплату за каждого перевезённого пассажира и бонус за соблюдение расписания.
Карта транспорта и интерфейс станции
Создайте карту транспорта, доступную игрокам с телефона, киосков на станциях или табло на остановках. Карта должна показывать все активные маршруты с цветовой кодировкой по линиям, расположение станций с индикаторами пересадок там, где соединяются несколько линий, и позиции поездов в реальном времени с обновлением каждые несколько секунд. Интерфейс станции на киосках должен отображать текущее время, следующие прибытия для каждой линии, обслуживающей станцию, любые сбои или задержки в обслуживании и интерфейс покупки билетов. Реализуйте карту транспорта как NUI-оверлей, который отображает железнодорожную сеть схематично, а не географически, подобно реальным картам метро, которые ставят ясность выше географической точности. Добавьте индикаторы в реальном времени, показывающие текущую позицию каждого поезда в виде движущейся точки по линии маршрута, давая игрокам мгновенную информацию о времени ожидания следующего рейса.
Система оплаты проезда и проездные билеты
Реализуйте многоуровневую систему оплаты с билетами на одну поездку, дневными пропусками и месячными подписками. Билеты на одну поездку стоят базовый тариф и действуют только на одну поездку. Дневные пропуска позволяют неограниченно ездить в течение двадцати четырёх игровых часов по фиксированной цене, которая становится выгодной после трёх-четырёх поездок. Месячные пропуска обеспечивают лучшую стоимость за поездку для регулярных пассажиров и хранятся как предмет в инвентаре игрока с отметкой срока действия. Проверяйте билеты на входных турникетах станции с помощью взаимодействия с объектами турникетов и в автобусах через взаимодействие с водителем при посадке. Для тарификации по расстоянию рассчитывайте стоимость при выходе пассажира на основе количества пройденных станций. Игроки без действительных билетов, пойманные во время случайных проверок сотрудниками транспортной службы, получают штрафы, добавляя элемент контроля для работы транспортной службы.
Производительность и синхронизация
Поезда и автобусы , это крупные сущности, которые должны синхронизироваться между всеми клиентами, что делает их чувствительными к производительности и требует тщательной оптимизации. Используйте серверное управление состоянием для контроля позиций поездов и рассылки обновлений клиентам, позволяя каждому клиенту локально отображать сущность поезда на основе полученных координат. Удаляйте сущности поездов для игроков, находящихся далеко от станции или железнодорожной линии, чтобы снизить нагрузку на рендеринг, и создавайте их только когда игрок входит в зону железнодорожной сети. Водители NPC автобусов должны активно работать только когда игроки находятся поблизости, а дальние автобусы использовать упрощенную интерполяцию позиций вместо полного AI-поиска пути. Ограничьте общее количество одновременно активных сущностей поездов и автобусов на сервере, чтобы предотвратить снижение производительности в часы пик. Разнесите время отправления по разным маршрутам, чтобы несколько транспортных средств не появлялись одновременно. Очищайте все транспортные сущности при перезапуске ресурса и реализуйте логику восстановления, которая пересчитывает позиции поездов на основе расписания при запуске ресурса, чтобы поезда сразу появлялись в правильных местах, а не начинали с первой станции каждый раз.