Wróć do bloga
Guide8 min czytania

Skrypt systemu pociągów w FiveM: własne trasy kolejowe i stacje

Tchnij życie w pociągi w FiveM. Własne trasy, sterowane lokomotywy, miejsca dla pasażerów i najlepsze skrypty pociągów do immersyjnego roleplayu transportowego.

Agency Scripts

Założyciel i główny deweloper w Agency Scripts

Transport publiczny jako infrastruktura serwera

System pociągów i transportu publicznego przekształca twój serwer FiveM z miasta skupionego na samochodach w tętniącą życiem metropolię z funkcjonalną komunikacją miejską. Pociągi kursujące według rozkładu po istniejącej sieci kolejowej GTA, autobusy poruszające się po trasach na ulicach miasta oraz stacje metra zapewniające szybką podróż między dzielnicami sprawiają, że świat wydaje się w pełni żywy. Transport publiczny przynosi korzyści każdemu graczowi na serwerze, niezależnie od majątku czy pracy. Nowi gracze, których nie stać na samochód, mogą dojechać autobusem do miejsc pracy. Doświadczeni gracze korzystają z metra w godzinach szczytu, aby ominąć zakorkowane drogi. Gracze ze świata przestępczego używają transportu miejskiego, aby przemieszczać się po mieście bez możliwego do namierzenia pojazdu prywatnego. System transportu tworzy również unikalne możliwości zatrudnienia dla graczy w rolach kierowców autobusów, konduktorów czy pracowników nadzoru ruchu, którzy dbają o rozkłady, pobierają opłaty za przejazd i reagują na incydenty na trasach. Z perspektywy technicznej pociągi i autobusy kursujące na stałych trasach wypełniają świat poruszającymi się jednostkami, dzięki czemu miasto wydaje się zatłoczone bez konieczności zwiększania liczby graczy.

Konfiguracja trasy i rozkładu jazdy pociągów

GTA V posiada wbudowaną sieć kolejową oplatającą mapę, a twój system pociągów powinien wykorzystywać te istniejące tory dla maksymalnej kompatybilności. Zdefiniuj trasy pociągów jako sekwencje przystanków na stacjach wzdłuż torów, z czasami przyjazdu i odjazdu obliczanymi na podstawie średniej prędkości pociągu między stacjami. Każda stacja wymaga strefy peronu, na której gracze wsiadają i wysiadają, punktu interakcji z biletomatem oraz tablic informacyjnych pokazujących nadjeżdżające pociągi. Skonfiguruj wiele połączeń kolejowych kursujących jednocześnie na różnych odcinkach sieci, z rozkładami jazdy powtarzającymi się w konfigurowalnym cyklu dopasowanym do upływu dnia i nocy na twoim serwerze:

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 },
        },
    },
}

Ten frequency wartość określa, jak często nowy pociąg odjeżdża z pierwszej stacji na trasie. Wiele pociągów może kursować na tej samej trasie jednocześnie, w odstępach określonych przez interwał częstotliwości. Wartość stopDuration na każdej stacji daje graczom wystarczająco dużo czasu na wejście lub wyjście, z zapowiedzią dźwiękową odtwarzaną po przyjeździe pociągu i sygnałem ostrzegawczym o odjeździe na kilka sekund przed ruszeniem.

Spawnowanie i ruch entity pociągu

Spawnowanie i kontrolowanie pociągów w FiveM wymaga specyficznych funkcji native, które różnią się od zwykłej obsługi pojazdów. Użyj CreateMissionTrain do spawnowania pociągów na sieci kolejowej, co automatycznie umieszcza je na najbliższym torze i obsługuje fizykę poruszania się po szynach. Kontroluj prędkość pociągu za pomocą SetTrainSpeed i SetTrainCruiseSpeed, oraz zatrzymuj pociągi na stacjach, stopniowo zmniejszając prędkość w miarę zbliżania się do współrzędnych peronu. Pociąg musi zatrzymać się dokładnie na pozycji peronu, aby drzwi zrównały się ze strefami wejścia, co wymaga dokładnych obliczeń redukcji prędkości w oparciu o odległość do następnej stacji:

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)

Wejście pasażerów na pokład i bilety

Gdy pociąg zatrzymuje się na stacji, wyświetl pobliskim graczom komunikaty interakcji umożliwiające wejście na pokład. Sprawdź, czy gracz ma ważny bilet lub bilet okresowy, zanim wpuścisz go do środka. Bilety można kupić w biletomatach (props) na każdej stacji za pomocą prostego interfejsu NUI, który pokazuje dostępne trasy i ceny. Po wejściu do pociągu przeteleportuj gracza do wnętrza wagonu i ustaw jego pozycję względem encji pociągu, aby poruszał się razem z nim. Wbudowane wnętrza pociągów GTA sprawdzają się w podstawowych implementacjach, ale niestandardowe wnętrza wykorzystujące dołączone obiekty lub instancje zapewniają lepsze wrażenia dzięki miejscom siedzącym, strefom stojącym i oknom pokazującym mijane krajobrazy. Śledź, na której stacji wsiadł każdy pasażer, aby system mógł obliczyć różnice w opłatach w oparciu o odległość. Wyświetlaj nazwę następnej stacji i szacowany czas przyjazdu wewnątrz wagonu za pomocą elementu 3D text lub dołączonego wyświetlacza NUI, który aktualizuje się w miarę postępu pociągu na trasie.

System autobusów i kierowcy NPC

Autobusy uzupełniają sieć kolejową, docierając tam, gdzie nie sięgają tory. Twórz autobusy prowadzone przez NPC, które poruszają się po wyznaczonych trasach ulicami miasta, zatrzymując się na oznaczonych przystankach, aby zabierać i wysadzać graczy. Kierowca NPC korzysta z systemu zadań GTA do nawigacji między przystankami za pomocą TaskVehicleDriveToCoordLongrange z odpowiednimi flagami jazdy do przestrzegania przepisów ruchu drogowego i zatrzymywania się w wyznaczonych miejscach. Na każdym przystanku autobus czeka przez skonfigurowany czas, podczas gdy gracze wsiadają lub wysiadają, a następnie kierowca NPC kontynuuje trasę do kolejnego przystanku. Wyświetlaj informacje o trasie na propach przystanków autobusowych, pokazując numer linii, cel podróży i szacowany czas oczekiwania na kolejny autobus. W wariancie prowadzonym przez graczy pozwól graczom z pracą w przedsiębiorstwie komunikacji miejskiej na ręczne prowadzenie autobusów, zdobywając wynagrodzenie za każdego przewiezionego pasażera oraz premię za punktualność.

Mapa transportu i UI stacji

Stwórz mapę komunikacji miejskiej, do której gracze mają dostęp z telefonu, kiosków na stacjach lub ekranów na przystankach autobusowych. Mapa powinna pokazywać wszystkie aktywne trasy oznaczone kolorami według linii, lokalizacje stacji ze znacznikami przesiadek na połączeniach wielu linii oraz pozycje pociągów w czasie rzeczywistym aktualizowane co kilka sekund. UI stacji w kioskach powinno wyświetlać aktualny czas, najbliższe przyjazdy dla każdej linii obsługującej tę stację, zakłócenia lub opóźnienia w kursowaniu oraz interfejs zakupu biletów. Zaimplementuj mapę transportu jako nakładkę NUI, która przedstawia sieć kolejową schematycznie, a nie geograficznie, na wzór rzeczywistych map metra stawiających czytelność ponad dokładność geograficzną. Dodaj wskaźniki czasu rzeczywistego pokazujące aktualną pozycję każdego pociągu jako poruszający się punkt wzdłuż linii trasy, dając graczom natychmiastową informację o czasie oczekiwania na kolejny kurs.

System opłat i bilety komunikacyjne

Wdróż stopniowany system opłat z biletami jednorazowymi, biletami dobowymi i subskrypcjami miesięcznymi. Bilety jednorazowe kosztują stawkę bazową i tracą ważność po jednym przejeździe. Bilety dobowe umożliwiają nielimitowane przejazdy przez dwadzieścia cztery godziny w grze w stałej cenie, która staje się opłacalna po trzech lub czterech przejazdach. Bilety miesięczne zapewniają najlepszą stawkę za przejazd dla stałych pasażerów i są przechowywane jako przedmiot w ekwipunku gracza ze znacznikiem czasu wygaśnięcia. Sprawdzaj bilety przy bramkach wejściowych na stacjach za pomocą interakcji target na rekwizytach kołowrotów, a w autobusach poprzez interakcję z kierowcą podczas wsiadania. W przypadku opłat zależnych od odległości obliczaj opłatę przy wyjściu pasażera na podstawie liczby przejechanych stacji. Gracze bez ważnych biletów, złapani podczas wyrywkowych kontroli przez pracowników transportu publicznego, otrzymują mandaty, co dodaje rozgrywkę egzekwowania prawa dla pracy w transporcie publicznym.

Wydajność i synchronizacja

Pociągi i autobusy to duże entities, które muszą być zsynchronizowane między wszystkimi klientami, co czyni je elementami wrażliwymi na wydajność i wymagającymi starannej optymalizacji. Używaj zarządzania stanem po stronie serwera do kontrolowania pozycji pociągów i rozsyłania aktualizacji do klientów, pozwalając każdemu klientowi renderować entity pociągu lokalnie na podstawie odebranych współrzędnych. Despawnuj entities pociągów dla graczy znajdujących się daleko od stacji lub linii kolejowej, aby zmniejszyć obciążenie renderowania, i spawnuj je tylko wtedy, gdy gracz zbliży się do sieci kolejowej. Kierowcy NPC autobusów powinni otrzymywać aktywne zadania tylko wtedy, gdy gracze są w pobliżu, natomiast odległe autobusy powinny korzystać z uproszczonej interpolacji pozycji zamiast pełnego wyszukiwania ścieżek AI. Ogranicz łączną liczbę jednoczesnych entities pociągów i autobusów na serwerze, aby zapobiec spadkom wydajności w godzinach szczytu. Rozłóż czasy odjazdów na różnych trasach, aby wiele pojazdów nie spawnowało się jednocześnie. Usuwaj wszystkie transit entities przy restarcie zasobu i zaimplementuj logikę przywracania, która przelicza pozycje pociągów na podstawie rozkładu jazdy podczas startu zasobu, dzięki czemu pociągi natychmiast pojawiają się we właściwych miejscach, zamiast zaczynać za każdym razem od pierwszej stacji.

Gotowy, aby zacząć?

Kup skrypty w naszym sklepie lub dołącz do Discorda, aby uzyskać wsparcie, aktualizacje i sprawdzić nadchodzące nowości.