Train Move System [Dead Rails]

Hi everyone! I recently became interested in how the train in the game Dead Rails is built! Initially, I did not have a specific idea of ​​how it was done until I came across this topic. To my surprise, the creator of the original game, @RiccoMiller , himself responded to this and was marked as a solution.
As a true fan of the game :money_mouth_face:, I decided to experiment with this… And so I would like to present my template that I managed to create!

You can download this place here:
Dead Rails Train System.rbxl

First, watch the demo.

As you can see, the player is riding a railway on a cart that curves to one side. He can also change its speed.

How it works

First, let’s figure out what sets the cart in motion.

As the creator of the original game himself said, he uses a special Mechanical constraint, which has the Prismatic class. This constraint is located inside the cart and its ActuatorType is Motor. This allows the cart to maintain speed and change it due to the ThrottleFloat property of the VehicleSeat. The start of the movement where Attachment0 is located is an invisible BasePart that moves forward every time a rail is passed.
The wheels of the cart are rotated by the HingeConstraint ActuatorType which is also on the Motor. AngularVelocity (speed) depends on the current AssemblyLinearVelocity of VehicleSeat.

Okay! Now let me explain how the automatic generation of rails and sand happens.

A rail is a model consisting of its visual part, as well as an invisible hitbox at the bottom and an invisible BasePart, which is its end (PrimaryPart). Every frame, a ray comes out from the base of the cart, shooting downwards and hitting the hitbox. If this happens, the game checks whether this rail is the last one, if so, it creates new rails at an angle of 0.25° from the end of the last one. Perhaps this system could have been made simpler and more reliable, for example by checking the nearest rail or check CurrentPosition from PrismaticConstraint
Sand is generated based on cart current position in the world and the border of the last sand base. If the distance becomes small, a new sand base appears.

That’s all! I hope I helped you figure out the basics of this game…

TOP SECRET!

I created this skull 2 years ago, and now it is used in Dead Rails
https://create.roblox.com/store/asset/15090308072

22 Likes

Hi Newbie,

Thanks for using my post. This system was a bit of a dilemma for me since I’m not a big fan of math and physics. @RiccoMiller (the creator of Dead Rails) responded to me and gave me a little hint, but he didn’t explain in detail how to fully make the system. After some time, I figured it out and wanted to share it so others could understand how it works.

1. Prismatic Constraint

A PrismaticConstraint is an instance that acts through Roblox physics to push a part, allowing you to transport players and objects. A single part is not enough to make a fully functional transporter (vehicle) — we will need to use WeldConstraints to create an assembly so physics apply to the entire vehicle.

To make the constraint smooth, we’ll set the ActuatorType to "Motor" , making it behave like a vehicle. Then, we’ll set the MotorMaxAcceleration to something smooth like 10–20. Finally, we’ll set MotorMaxForce to inf so it can push anything.

2. Mover

To make an assembly, we need a primary part — a part that transmits physics to the others. We’ll call this part Mover. The Mover is the part that owns the PrismaticConstraint, and all other parts will be welded to it.

Notes: You can make the Mover non-collidable to prevent the vehicle from being easily pushed. However, this method won’t make the vehicle completely unpushable. While it’s impossible or at least very hard to make the vehicle truly unpushable, you can use CustomPhysicalProperties to increase the density of each part to its maximum.

3. Scripting (The Complicated Part)

To make the vehicle drivable, we will need a VehicleSeat, which determines MaxSpeed, directions, and occupants.

First, we set up our variables:

local car = script.Parent
local vehicleSeat = car.VehicleSeat
local moverPart = car.Mover
local constraint = moverPart.PrismaticConstraint

local maxSpeed = 30

This will make our life easier (because that’s what variables do).

Next, we create our functions:

vehicleSeat:GetPropertyChangedSignal("Throttle"):Connect(function()
	constraint.Velocity = vehicleSeat.Throttle * maxSpeed
end)

Technically, our script is done — it works well — but it’s missing something important: physics ownership. Without it, the physics can still work, but might feel sloppy.

That’s where ownership comes in handy:

We set the seat’s (or the Mover’s) ownership to the occupant, like this:

vehicleSeat:GetPropertyChangedSignal("Occupant"):Connect(function()
	local humanoid = vehicleSeat.Occupant
	if humanoid then
		local player = game:GetService("Players"):GetPlayerFromCharacter(humanoid.Parent)
		if player then
			vehicleSeat:SetNetworkOwner(player)
		end
	else
		vehicleSeat:SetNetworkOwnershipAuto()
	end
end)

Our script should look like this:

local car = script.Parent
local vehicleSeat = car.VehicleSeat
local moverPart = car.Mover
local constraint = moverPart.PrismaticConstraint

local maxSpeed = 30

vehicleSeat:GetPropertyChangedSignal("Throttle"):Connect(function()
	constraint.Velocity = vehicleSeat.Throttle * maxSpeed
end)

vehicleSeat:GetPropertyChangedSignal("Occupant"):Connect(function()
	local humanoid = vehicleSeat.Occupant
	if humanoid then
		local player = game:GetService("Players"):GetPlayerFromCharacter(humanoid.Parent)
		if player then
			vehicleSeat:SetNetworkOwner(player)
		end
	else
		vehicleSeat:SetNetworkOwnershipAuto()
	end
end)

On my side, that’s not what i did, since i have a fuel system so i used RunService

Last Note : If you want you can have other functions for example to change the MaxSpeed in case you have a system where a player can upgrade the speed.

8 Likes

Thank you very much for adding to this topic. I appreciate it!
But I don’t see the point in setting the network owner of VehicleSeat to a player whose Humanoid character is already seated. Since when adding SeatWeld all train children associated with VehicleSeat through any kind of constraints already have the network owner of that player… This situation is similar to the first case of 2 players being connected by rope in my previous topic.

1 Like

No problem!

Believe it or not, setting the network ownership actually does something special. When an assembly is controlled by a player, it reduces latency, makes the controls more responsive, and results in smoother movement. A client, of course, knows what’s happening locally better than the server.

I understand your point of view, but no, when a player sits in a VehicleSeat, it does not automatically give the player’s network ownership to the entire assembly. You still need to explicitly set network ownership for the parts you want to be controlled by the player.

Here’s a fun fact: the Roblox documentation actually has an example that perfectly matches our context: Here

2 Likes

How can we make a track that is static and is built to go in certain areas?

also how can we build a track that has multiple ways to go, like it goes straight or you can go left where it forks off to another track?

Perhaps you meant that if some other Seat or VehicleSeat (as stated in the documentation) is attached to the train via constraint and someone sits in it, and then another player jumps into the VehicleSeat, which is necessary to control the train, then yes, it is necessary to issue network ownership to him.
Thanks for the suggestion, I didn’t know that network ownership doesn’t automatically switch to the last person to enter the vehicle.

1 Like

Also this is another OS train system that has a rocking motion that is pretty cool… if you want to check it out and add it to yours…

2 Likes

For your request, you need to create a few more road templates and rewrite the script logic for path generation. If you need a static road, I don’t think you’ll need autogeneration. Usually games of this type have a road already built, not Dead Rails…

I would also like to note that when automatically transferring network ownership, for example to the server, when a player gets up from a VehicleSeat, there is a small jolt and the cart freezes. When I played the original, this did not happen. I can assume that network ownership does not change immediately, but until the train finally stops or another player gets on the VehicleSeat.
This can be seen by the remaining smoothness of movement of the limbs of the horses, outlaws or other entities attached to it…

That’s weird, for now, i didn’t have this bug with my system.
I might know what causes this, it might be the server Overload, you can try to make a fix by optimizing your game, maybe it also has something to do with the assembly.

If you find what’s the problem or a solution, feel free to respond!

I just created a test place, there is nothing but a cart, a road and a sand base with spawn location and a rig for decoration, I don’t think it can be overloaded. There are some events related to RunService and loops, but I don’t think it can affect… The cart freezes for only a split second and I think it is related to the player’s ping, when transferring the ownership of the network to the server.

In that case we’re talking about how you use RunService and how you set the NetworkOwnership.

You suggested that this problem could be related to server overload, I don’t think so. Then I replied that I think it is related to the player’s ping.

I don’t understand English very well and use a translator in some cases, it can write something incorrectly. Sorry(

Don’t worry, it’s okay if you don’t speak English. Even though you don’t speak the language, we can still understand you. The issue might be related to player ping, since it can vary depending on the player. The best approach is to optimize.

NOTE: Even if this is a problem, there’s no need to feel like your system is bad. Games like GTA and other popular titles also experience issues like this.

I found some topics that is close to my problem, but there is no exact solution. A small teleportation of an object to some distance will always be present when changing network ownership. You can optimize the game as much as possible and reduce the ping of players to a minimum, but it will still be present. There is no ideal solution…

How do I make it so the train can go on curved tracks? Like 90 degree turns.

The automatic path generation system is based on pre-made road models that are stored in ServerStorage, it is important to note that they are completely straight and long. If you want to make your train turn 90 degrees, you need to reduce the length of the original road part and increase the offset angle in the code.
To be honest, I don’t recommend making large degree offsets, as there may be glicthes with determining the current rail using ray. You can rework the logic of its determination at any time to make it better.

1 Like

This is dope. One question I have though is do you have a solution to players jittering whenever the train is moving at high speeds. I understand seats work but sometimes players just want to stand up and it looks weird whenever you look at someone else and they look like they are lagging back and forth.

Unfortunately, this cannot be fixed. It may be related to the ping of the player you are looking at. Other games that use platforms to move players using Body/Mechanical constraints also have this visual glitch.

Do you know if custom character controllers would work as an alternative to using constraints?