TrackWorx - Guide

Overview


TrackWorx is a system to create any kind of game that needs things to run on rails.
Whether it’s a gentle monorail ride, a mine-cart obstacle course, a puzzle game or something more exotic, TrackWorx can help you. I’ll keep this guide up-to-date as new features/updates/questions come along.

Head over to TrackWorx Showcase to see it in action!

Tutorials


Quick Glossary


Transform: Anything that can has a position on a track and knows how to move along it.
Vehicle: A transform that knows how to move around the track with acceleration/braking, taking any coupled vehicles with it.
Track Section: A piece of track placed in the world that a transform can attach to. Typically a single mesh entity (eg MonorailStraight).
Blocker: A transform that prevents vehicles from moving through it. Typically used for signals.
Rider: A player that’s riding in a vehicle that has a seat.
Seat: A location on a vehicle that a rider can sit.
Prototype: The setup for a new type of track or a new type of vehicle.
Stitching: The process that combines multiple track sections together to make a complete run of track.
Module: A script that extends the behaviour of an existing script (eg a track section module, or a vehicle module).
Points: A set of points; a junction on a railway, also known as a switch, switchtrack or turnout.

User Guide


Track

Track is made up of individual sections, each section is an entity associated with a path. Sections can be anything you like, but typically they’ll be a track mesh (eg MonorailStraight) - you could make your own track pieces out of voxels, or even have invisible track (eg for a plane ride). TrackWorx comes with track setups for the Monorail and Minecart meshes.
The TrackWorx layout engine stitches track sections together to make a complete track. Vehicles can be placed on a track and made to move; they will automatically follow the track.
Track sections provide events for when a vehicle enters or exits them; you could use these to trigger signals, scenics, explosions, change to a special tunnel camera etc.
You can move track sections around (eg to make a turntable, switch/points/turnout or even a lift), or spawn them dynamically - you just tell the layout engine that you’ve made a change, and it’ll update the stitching.

When laying track I would recommend using the Grid Snap feature in Advanced Mode to make it easier to line them up. Set to 4 for the monorail and 5 for minecart.

Track Modules

A few modules are provided to extend the functionality of track section. Just drop the scripts on any TWTrackSection entity.

  • TWTrackSectionModuleBoost can accelerate and/or brake any vehicle on a track section. You can use this to create rollercoaster lift hills, braking runs, or a “launch” start.
  • TWTrackSectionModuleVehicleStartStop has events for when a vehicle starts or stops on that track section

Vehicles

Vehicles can also be anything you like - TrackWorx comes with vehicle setups for Monorail and Minecart, but you could create your own out of voxels, or use the planes, cars, toilets etc. You can couple multiple vehicles together to make a train, or let them run solo. Vehicles can be placed in the world, or spawned dynamically. They automatically snap to the closest track.
Vehicles optionally support rail physics, so they’ll accelerate down hills, slowdown on inclines (and possibly roll back), and reduce to a halt via friction.

Vehicle Modules

Vehicle modules extend the functionality of vehicles. Just drop the scripts on any TWVehicle entity.
Two driver modules are provided. You don’t need to use either, but they make it easy to create powered vehicles (rather than those than just use rail physics)

  • TWVehicleModuleAutoDriver will automatically drive its vehicle along the track, keeping to a designated speed and stopping for blockers.
  • TWVehicleModuleManualDriver makes it easy for a player to take control, with optional safeties to keep to a max speed and stop for blockers.
  • TWVehicleModuleRandomRouting will have the vehicle choose a random track to follow when it arrives at a set of points.
  • TWVehicleModuleAnimation can play a movement animation on the vehicle (eg to make the wheels turn), updating the animation speed as the vehicle accelerates or slows.
  • TWVehicleModuleSnapToTrack (experimental) allows vehicles that are ragdolling (ie have physics enabled) to snap to any track that they come close to. You could use this to create a “jump” mechanic for mine carts, where you want it to automatically snap back onto the track when the jump finishes.
  • TWVehicleModuleShuttle switches a vehicle’s direction when it reaches the end of the track.
  • TWVehicleModuleSound updates a sound entity’s volume and pitch based on the speed of the vehicle. Use this to create engine noise. Experimental - this can now also play track joint noises (ie clickety clack) as the vehicle travels.

Blockers

A blocker can be created out of any TWTransform. They can be placed anywhere on the track, turned on or off or moved around - useful for signals, the end of station platforms, or other vehicles on the line. Running out of track is also an automatic blocker, as is track that’s been set inactive. Vehicles with a TWVehicleModuleAutoDriver or TWVehicleModuleManualDriver will detect blockers ahead that are within their braking distance - once one is detected the driver will apply the brakes to stop in the right place.

The simplest blocker is a locator with a TWTransform - set the Track and T properties to attach it to a track piece, then tick Blocker, and untick TriggerTrackEvents (to stop it triggering TWTrackSection’s onEnter etc events that you normally only want for vehicles). You can enable/disable the blocker during play by changing calling SetBlocked or SetUnblocked on the transform.

Riders

You can place seat(s) on a vehicle, and allow riders (players) to “sit” in them. Riders can have a TWRiderCamera on their player template that they use to provide a suitable viewpoint on the vehicle. Optionally, each seat can specify a template to spawn onto any rider to enable special behaviours or UI, eg driver controls, speedometer etc.
Each track section can decide whether or not a rider can disembark while their vehicle is on it, and where the rider should teleport to if they do. This is useful in stations, where you can setup spawn points along the platform that closely match the track positions. Or on a rollercoaster you can prevent the rider from getting out whereever they like and only allow it in the station.

The System

The TrackWorx core system is a single entity that you place in the root of your world. You only need one, no matter how many tracks you have in the world. Place all of your vehicle prototypes and track prototypes as children of the system. You can use the template (TWSystem) provided as it’s already setup with the three scripts it needs:

  • TWSystem manages the prototypes - place prototypes as children of the system entity
  • TWLayout manages the stitching of tracks and initialisation of vehicles
  • TWHelpers is a collection of helper functions that make it easier to do things to vehicles from track events.

Add the templates TWMonorailPrototypes and/or TWMineCartPrototypes as children of the TWSystem to use those track/vehicle setups.

TWSystem

Script Style

Every script and template that comes with TrackWorx is prefixed TW for easy searching. Script functions and variables that begin with an underscore _ are intended for internal use, and may change or not exist in future updates. On the off-chance that I do need to make breaking changes, I’ll point them out here and in the update notes.

How do I…?


Place track

You can use the templates provided with TrackWorx, or simply add a TWTrackSection script to any supported mesh entity - TrackWorx will detect the mesh used (eg MonorailStraight or MonorailCurve) and set it up correctly. Put the mesh down so that the ends line-up, and that’s it!
The stitching will stitch tracks that end in the same location (with a tiny tolerance) and whose ends are at approximately the same angle.

Place a vehicle

Vehicles are an entity with TWTransform and TWVehicle scripts on them - there are templates provided with these added for you. TrackWorx will detect any vehicles and snap them to the closest track.

Couple vehicles together into a train

Place down multiple vehicles and link them together by setting up prevVehicle and nextVehicle properties on each vehicle entity. Designate one vehicle as ‘isDriving’ - this is the one that controls the movement of the whole train - the rest will follow it along the rail.

Create new track and vehicle types

Track types are created as “prototypes”. Each prototype tells TrackWorx System about the shape of that track piece type. TrackWorx comes with prototypes for Monorail and Minecart assets, but you can create new ones if you want a special track system for your game (eg voxel tracks).

Create a new mesh or voxel entity as a child of TrackWorx System, add a TWTrackSectionPrototype script and one of the TWPath* scripts - TWPathLine is a straight line, and TWPathSpline is a bezier spline for curves/slopes. Configure the path to match the shape of the mesh - you can use TWTrackSectionDebug to help visualise the path during preview by placing it on a mesh child of a TWTrackSection or the TWTrackSectionPrototype - the mesh “Bench” is a good choice for this.

By default TrackWorx System will use the mesh asset/voxel mesh asset on the entity as the ‘key’ to match it up with track sections you place in the world that use the same mesh. You can also set the key manually as a string if you’re doing something exotic that doesn’t use meshes (eg invisible sky track, or a boat course), but it’s advisable to use an invisible voxel mesh anyway to help with lining sections up so they stitch together.

Vehicles are very similar, but us TWVehiclePrototype instead. The only thing to configure is the “couplerOffset”, which tells the vehicle how far the ends of the vehicle are from its centre. This is used to adjust the distance between vehicles in a train.

Make a vehicle fly off the track

You can call Ragdoll() on any TWTransform to detach it from its rail, and enable physics while preserving its existing momentum.

Make a set of points


If you make a ‘Y’ shape with track, there are a few ways to indicate which route a vehicle should take:

  1. Physical Routing
    Physically move the track (eg with TWMovableTrack) to disconnect all paths except one this is how real trains work!

  2. Static Routing
    Let the track section decide by weighing up the various options:

  • First it’ll disregard any tracks that are not ‘active’
  • From the remaining options it’ll choose the track with the highest ‘switchPriority’
  • If it’s still undecided, it’ll use the first track in the list - this can be unreliable, so you don’t want to let it do this if possible.
  1. Dynamic Routing (experimental)
    After static routing has decided on a default route, if there were multiple options available you can choose if you want to override it.
    Implement a function GetBestTrackLink(options, default, result) in a script on the vehicle (TWVehicle has a similar GetDefaultBestTrackLink that you can use as an example). From the options available, choose the one you want, eg based on a previous route-finding calculation, and set “result.link = bestOption”. If you leave result.link as nil, it’ll go the default route. Be warned that this function may be called a lot more often than you think (eg when checking the line ahead for blockers), so you should keep it as simple as possible, and also ensure it returns the same result on server & client.
    TWVehicleModuleRandomRouting uses Dynamic Routing to pick a random track to follow.

Make movable track (eg turntable/lift)


Add a locator with a TWMovableTrack script, and add the track piece(s) as children of it. The MovableTrack script can be setup with a list of position & rotation “stops”, and then ChangeToStop(stopNum) will move the locator to the chosen position & rotation. It’ll automatically restitch any track that’s a child of the “restitchTrack” entities as the track moves.

The world tree for a turntable with one movable track piece (“turntableStraight”) and four entry points might be setup like this:

  • turntablePivot ← TWMovableTrack
    ** turntableStraight
  • entryTrack1
  • entryTrack2
  • entryTrack3
  • entryTrack4

The entry tracks are added to the TWMovableTrack as restitchTracks. They are optional, but it is much much more efficient to use them, otherwise the layout engine will try to restitch every piece of track in the world, rather than just those provided. It’ll restitch any children of the tracks selected, so you could group them all together and use that as the restitchTrack for simplicity.

One-off track piece

If you’re in a situation where you just need a one-of-a-kind custom track section, you can add a TWPathLine or TWPathSpline directly on the track section it and configure the path just for that one piece. The path will override whatever’s setup on the prototype track. You can configure the TWPathSpline’s control points with locator entities rather than the vector properties (it’s less efficient, but it can be easier to visualise). You can also make the path move if you set the handle type to MovableLocators; this will be quite expensive and I’ve not found a reason to use it yet, but it seems like it could have a funky use!

Future features


There’s a lot I’d like to add, and I intend to expand upon the basic TrackWorx system. This is a rough list of things I’d like to do, but didn’t quite make it in to the first release:

  • Easier coupling in the editor - in the same way that vehicles snap to their closest track, vehicles could also snap together
  • More TWPath* types - eg arc, b-spline
  • Track path roll - track paths can only run “flat” - you can’t make them barrel roll. Could be useful for planes or camera splines
  • Track section roll - track sections only work propertly when they have zero roll - you can’t tilt them at an angle or place them upside-down. Vehicles will follow them, but they don’t rotate properly.

The TrackWorx package contains the bare minimum you need to get going and make vehicles go round a track. The showcase has a lot of ‘glue’ scripts to make the attractions fully functional (eg the signals, user control scripts/widgets), and I may release some of the reusable components separately to the core package.

What will you create?!

13 Likes

So much to explore! Thanks for this!

Is there an actual way to make players sit? I haven’t seen any information about sitting animations, etc…

There aren’t any sit animations available at the moment. You could up-vote this post for it on the Feature Requests board

1 Like

Great package and i still didn’t understand the magic behind it, but I’d have one additional wish. When i use PlayTimeline (because that’s how I usually handle movement personally) on the server, i send similar instructions to the client and sync it by waiting for the first client position change (that it receives from the server) and starting an adjusted version of the timeline, starting from the new position/rotation. So i try to reach the best possible sync by waiting for the first position update on the client instead of playing it independently from the server. Would this be something that’s implementable for TrackWorX?

Great work :+1:

I think i worded it weird. Here’s an alternative wording:
It would be cool if it wouldn’t relate on syncing between server and client more than once (to avoid stuttering).
So whenever a vehicle on the server changes it’s state, if should send some necessary data once to the client. When the client gets the instructions, it should check it’s current position/rotation and wait until one of these change once (trough server to client sync). After the client got synced the first time, it should predict the movement from an adjusted starting point and run independent from the server until it gets new instructions.

That’s roughly what TrackWorx already does :smiley: (or is supposed to do). The server is continuously updating the vehicle entity position (and rotation), and the client is doing this too - both run independently, with the client continuously overwriting the position sent down from the server. The position is calculated based on a set of track data - which track the transform is on, the ‘t’ along it, and the direction of travel.

Periodically the server updates the property version of the track data, and the client uses it to gradually blend to the server values (see TWTransform:CheckForServerCorrection).

In theory this should all be buttery smooth, however it does still stutter and I need to investigate why. I’m unsure if it’s numerical instability in the local spline calculations, or if it’s due to the server position somehow coming through even though the client overwrites it every frame, or if it’s something odd with the blend in CheckForServerCorrection.

1 Like

I can confirm that the server position always comes trough, even when you overwrite the position inside a Local-/ClientOnTick.

1 Like

Any tips on the best placement of tracks, I keep getting Train Jams (no gamejam pun) where two pieces of track have moved a small amount, or are slightly overlapping. At least, that is what I think makes my trains literally stop in their track. :man_shrugging:

The tracks do tolerate a tiny amount of overlap, but not much - probably only 1-2cm. All of the pieces should fit together perfectly though - I always have grid snapping turned on and set to 4 for the monorail track, and 5 for the minecart.

Check the position properties for your pieces after placing them - they should be a nice round number. If the track has a parent (eg the terrain) that’s been rotated, the positions tend to sit on numbers like 599.99. You can try retyping those to round them to a nice 600.

Package Update 5: Steam Train support

Add the TWSteamTrainPrototypes template to your TWSystem to enable the new vehicles and tracks.

Notes:

  • Support for vehicle bogies, where separate wheel sets will pivot independently of the vehicle. See this in action on the new Steam Train templates.
  • NEW TWVehicleModuleAnimation.
  • NEW TWVehicleModuleRandomRouting.
  • NEW TWVehicleModuleSnapToTrack (experimental).
  • TWVehicleModuleSound has added experimental support for playing track joint sounds (clickety clack)
  • Tracks that are placed far from the world origin now stitch together more reliably.
  • Fixed a few sources of jitter when vehicles move between track pieces.
  • TWTrackSectionDebug now works on both track prototypes and track instances.
1 Like