FiveM-Zugsystem-Skript: Individuelle Strecken und Bahnhöfe
Erwecke Züge in FiveM zum Leben. Individuelle Strecken, steuerbare Lokomotiven, Fahrgastsitze und erstklassige Zugskripte für immersives Transport-Rollenspiel.
Agency Scripts
Gründer und leitender Entwickler bei Agency Scripts
Öffentlicher Nahverkehr als Infrastruktur des Servers
Ein Zug- und Nahverkehrssystem macht aus deinem FiveM-Server eine lebendige Metropole statt einer autofokussierten Stadt, mit funktionierendem öffentlichen Nahverkehr. Züge, die nach Fahrplan auf dem bestehenden Schienennetz von GTA verkehren, Busse, die festgelegte Routen durch die Straßen der Stadt fahren, und U-Bahn-Stationen, die schnelle Fahrten zwischen Stadtteilen ermöglichen, tragen alle zu einer Welt bei, die sich wirklich lebendig anfühlt. Der öffentliche Nahverkehr kommt allen Spielern auf dem Server zugute, unabhängig von ihrem Vermögen oder Beruf. Neue Spieler, die sich kein Auto leisten können, fahren mit dem Bus zu ihren Arbeitsplätzen. Erfahrene Spieler nutzen die U-Bahn während der Hauptverkehrszeiten, um überfüllte Straßen zu vermeiden. Kriminelle Spieler nutzen den öffentlichen Nahverkehr, um sich ohne ein zurückverfolgbares Privatfahrzeug in der Stadt zu bewegen. Das Verkehrssystem schafft außerdem einzigartige Jobmöglichkeiten für Spieler, die als Busfahrer, Zugführer oder Mitarbeiter der Verkehrsbehörde Fahrpläne einhalten, Fahrpreise kassieren und Vorfälle im Netz bearbeiten. Aus technischer Sicht beleben Züge und Busse, die auf festen Routen verkehren, die Welt mit beweglichen Entities, sodass sich die Stadt geschäftig anfühlt, ohne dass mehr Spieler online sein müssen.
Zugstrecken und Fahrpläne konfigurieren
GTA V verfügt über ein integriertes Schienennetz, das die Karte umrundet. Dein Zugsystem sollte diese bestehenden Gleise nutzen, um die bestmögliche Kompatibilität sicherzustellen. Definiere Zugstrecken als Abfolge von Haltestellen entlang der Strecke, wobei die Ankunfts- und Abfahrtszeiten anhand der durchschnittlichen Geschwindigkeit des Zuges zwischen den Stationen berechnet werden. Jede Station benötigt einen Bahnsteigbereich, an dem Spieler ein- und aussteigen, einen Interaktionspunkt für den Ticketautomaten und Anzeigetafeln mit den nächsten Ankünften. Konfiguriere mehrere Zugverbindungen, die gleichzeitig auf unterschiedlichen Streckenabschnitten verkehren, mit Fahrplänen, die sich in einem konfigurierbaren Zyklus wiederholen und an den Tag-Nacht-Rhythmus deines Servers angepasst sind:
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 },
},
},
}
Die frequency Der Wert bestimmt, wie oft ein neuer Zug am ersten Bahnhof der Strecke abfährt. Auf derselben Strecke können mehrere Züge gleichzeitig verkehren, mit dem in diesem Intervall festgelegten Abstand zwischen ihnen. Der stopDuration Aufenthalt an jeder Station gibt Spielern genug Zeit zum Ein- und Aussteigen. Bei der Ankunft des Zuges wird eine Durchsage abgespielt, und wenige Sekunden vor der Abfahrt ertönt ein Warnton.
Zug-Entities erstellen und bewegen
Für das Erstellen und Steuern von Zügen in FiveM sind spezielle Native-Funktionen erforderlich, die sich von der Steuerung normaler Fahrzeuge unterscheiden. Verwende CreateMissionTrain um Züge auf dem Schienennetz zu erstellen. Sie werden dadurch automatisch auf den nächstgelegenen Gleisen platziert und folgen der Schienenführung mithilfe der integrierten Physik. Steuere die Zuggeschwindigkeit mit SetTrainSpeed und SetTrainCruiseSpeedund bringe Züge an Stationen zum Stehen, indem du die Geschwindigkeit beim Anfahren der Plattformkoordinaten schrittweise verringerst. Der Zug muss exakt an der Bahnsteigposition halten, damit die Türen mit den Einstiegsbereichen übereinstimmen. Dafür ist es nötig, die Geschwindigkeit anhand der Entfernung zur nächsten Station sorgfältig zu reduzieren:
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)
Fahrgasteinstieg und Tickets
Wenn ein Zug an einer Station hält, zeigst du Spielern in der Nähe Interaktionshinweise zum Einsteigen an. Prüfe vor dem Einsteigen, ob der Spieler ein gültiges Ticket oder eine Fahrkarte besitzt. Tickets lassen sich an Ticketautomaten an jeder Station über eine einfache NUI-Oberfläche kaufen, auf der verfügbare Strecken und Preise angezeigt werden. Nach dem Einstieg teleportierst du den Spieler ins Innere des Zugwaggons und legst seine Position relativ zum Zug-Entity fest, damit er sich mit dem Zug bewegt. Die integrierten Zug-Innenräume von GTA eignen sich für einfache Implementierungen. Benutzerdefinierte Innenräume mit angehängten Objekten oder Instanzen bieten jedoch mit Sitzplätzen, Stehbereichen und Fenstern mit vorbeiziehender Landschaft ein besseres Erlebnis. Speichere, an welcher Station jeder Fahrgast eingestiegen ist, damit das System bei streckenabhängigen Preisen die Fahrpreisdifferenz berechnen kann. Zeige im Waggon über ein 3D-Textelement oder eine angehängte NUI-Anzeige den Namen der nächsten Station und die voraussichtliche Ankunftszeit an. Die Anzeige wird aktualisiert, während der Zug seine Strecke zurücklegt.
Bussystem und NPC-Fahrer
Busse ergänzen das Schienennetz und bedienen Gebiete, die nicht von der Bahn erreicht werden. Erstelle Busse mit NPC-Fahrern, die festgelegte Routen durch die Straßen der Stadt fahren und an markierten Haltestellen anhalten, um Spieler ein- und aussteigen zu lassen. Der NPC-Fahrer nutzt das Task-System von GTA, um mit TaskVehicleDriveToCoordLongrange zwischen Haltestellen zu navigieren, wobei passende Fahrparameter das Einhalten der Verkehrsregeln und das Anhalten an festgelegten Orten sicherstellen. An jeder Haltestelle wartet der Bus eine konfigurierbare Dauer, während Spieler ein- oder aussteigen. Anschließend fährt der NPC-Fahrer zur nächsten Haltestelle weiter. Zeige an Haltestellen-Props die Routennummer, das Ziel und die voraussichtliche Wartezeit bis zur Ankunft des nächsten Busses an. Für eine Variante mit Spielern als Fahrern ermöglichst du Spielern mit dem Job bei der Verkehrsbehörde, Busse selbst zu fahren. Sie erhalten Lohn pro befördertem Fahrgast sowie einen Bonus für die Einhaltung des Fahrplans.
Nahverkehrskarte und Stations-UI
Erstelle eine Nahverkehrskarte, die Spieler über ihr Telefon, Stationskioske oder Anzeigetafeln an Bushaltestellen aufrufen können. Die Karte sollte alle aktiven, nach Linien farblich gekennzeichneten Routen, Stationsstandorte mit Umsteigehinweisen an Verbindungen zwischen mehreren Linien und die alle paar Sekunden aktualisierten Echtzeitpositionen der Züge anzeigen. Die Stations-UI an Kiosken sollte die aktuelle Uhrzeit, die nächsten Ankünfte jeder Linie an dieser Station, eventuelle Betriebsstörungen oder Verspätungen und eine Ticketkauf-Oberfläche anzeigen. Implementiere die Nahverkehrskarte als NUI-Overlay, das das Schienennetz schematisch statt geografisch darstellt, ähnlich wie echte U-Bahn-Karten, bei denen Übersichtlichkeit wichtiger ist als geografische Genauigkeit. Füge Echtzeitanzeigen hinzu, die die aktuelle Position jedes Zuges als bewegten Punkt entlang seiner Strecke darstellen. So sehen Spieler sofort, wie lange sie auf die nächste Verbindung warten müssen.
Fahrpreissystem und Fahrkarten
Implementiere ein gestaffeltes Fahrpreissystem mit Einzelfahrscheinen, Tageskarten und Monatsabonnements. Einzelfahrscheine kosten den Grundfahrpreis und gelten für eine Fahrt. Tageskarten ermöglichen vierundzwanzig Spielstunden lang unbegrenzt viele Fahrten zu einem Festpreis. Ab drei oder vier Fahrten sind sie günstiger. Monatskarten bieten Pendlern den besten Preis pro Fahrt und werden als Item mit einem Ablaufzeitpunkt im Inventar des Spielers gespeichert. Prüfe die Fahrkarten an den Zugangsschranken der Stationen über Interaktionen mit Drehkreuz-Props und in Bussen beim Einstieg über den Fahrer. Bei streckenabhängigen Preisen berechnest du den Fahrpreis beim Aussteigen anhand der Anzahl der gefahrenen Stationen. Spieler ohne gültiges Ticket, die bei Stichprobenkontrollen durch Mitarbeiter der Verkehrsbehörde erwischt werden, erhalten Bußgelder. So entsteht zusätzliches Gameplay für den Job bei der Verkehrsbehörde.
Performance und Synchronisierung
Züge und Busse sind große Entities, die mit allen Clients synchronisiert werden müssen. Daher sind sie leistungsintensive Funktionen, die sorgfältige Optimierung erfordern. Verwalte den Status serverseitig, um Zugpositionen zu steuern und Updates an Clients zu übertragen. Jeder Client rendert das Zug-Entity dann lokal anhand der empfangenen Koordinaten. Entferne Zug-Entities bei Spielern, die weit von allen Stationen oder Schienenstrecken entfernt sind, um den Renderaufwand zu verringern. Erstelle sie erst, wenn ein Spieler in die Nähe des Schienennetzes kommt. Weise NPC-Busfahrern nur dann aktiv Aufgaben zu, wenn Spieler in der Nähe sind. Verwende bei weit entfernten Bussen stattdessen eine vereinfachte Positionsinterpolation, statt die vollständige KI-Pfadfindung zu nutzen. Begrenze die Gesamtzahl gleichzeitig aktiver Zug- und Bus-Entities auf dem Server, um Leistungseinbußen während der Hauptverkehrszeiten zu vermeiden. Staffel die Abfahrtszeiten verschiedener Routen, damit nicht mehrere Fahrzeuge gleichzeitig erstellt werden. Räume bei einem Ressourcen-Neustart alle Nahverkehrs-Entities auf und implementiere eine Wiederherstellungslogik, die beim Start der Ressource die Zugpositionen anhand des Fahrplans neu berechnet. So erscheinen Züge sofort an den richtigen Positionen, statt jedes Mal am ersten Bahnhof zu starten.