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 (158,0 КБ)

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 something like that…
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

5 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.

3 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.

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

1 Like

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…

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…