Back on track(s)


Not a lot has gotten done on the game recently and there is one particular problem to blame: animation and movement of trains on tracks.

When I introduced the train yard to add carriages to trains, there was a lot of nastiness involved with correctly initialising new carriages and placing them in the world. The spline system I had written was very simple, but it was also very stateful: It relied on the locomotive accumulating state as it moved in order to perform layout of its carriages. Statically laying out the train - e.g. having new carriages added and having them nicely span around corners and track junctions - just did not work. 

Additionally, putting this state into save games was a bit of a nightmare, and not very reliable.

Struggling against this system it became obvious I needed a new approach, and it had to solve a lot of different problems simultaneously:

  • Be largely stateless - be able to lay out an entire train on an area of track with very little information (ideally just a position)
  • Be able to add/remove carriages seamlessly
  • Avoid hacks for moving between junctions - the spline system had a flakey interpolated animation that was used to move from one track segment to another, which was an additional type of state to go wrong especially when restoring save games
  • Permit bidirectional travel - something that the existing system was theoretically capable of, but had so many edge cases and problems I never got it fully working

After a lot of thinking, testing and some procrastinating, I have made some decent progress on this, and I'm hoping to up the pace of development again

The new system

The new system is inspired by g-code and models the track like a sequence of instructions. The only piece of information a train needs is a pointer (think: instruction pointer) into this set of instructions that represents where it is on the track.

[1055]  J 45 (63, -6) 
[1056]  POINT 63,-7 
[1057]  POINT 63,-8
[1058]  POINT 63,-9 
[1059]  POINT 63,-10 
[1060]  POINT 63,-11 
[1061]  POINT 63,-12 
[1062]  POINT 63,-13 
[1063]  POINT 63,-14 
[1064]  POINT 63,-15 
[1065]  J 46 (63, -15)

An example. Note this is rendered as text but is not stored as text in memory.

Unlike normal code/bytecode though, the instructions are designed to be read in either direction - going downward or upward. This permits bidirectional movement.

The forks (switches) in the track are represented by junction numbers (the J instructions) which can arbitrarily be 'bridged' together, to make one track segment jump to another. In this way, each set of points in between junction numbers can be seen to be an individual spline. But: unlike the previous system, because the train only 'sees' the point data from the POINT instructions, interpolation from one spline to the next (junction to junction) is seamless and requires no special state/behaviours. 

To allow carriages to be laid out arbitrarily relative to a locomotive, the instructions are simply 'peeked'-at relative to the train's own pointer, by applying a plus or minus offset (depending on direction of travel).

This system is by no means perfect, and its current implementation is not very efficient - but it is now working:

  • Trains can be driven in reverse - properly, with the locomotive remaining at the rear and driving the carriages backwards, even through junctions/turns
  • Locomotives and carriages can be repositioned and automatically laid out at any arbitrary location with only two pieces of information (an 8 byte index and a floating point t value)

Screenshot of a typical debug session that has occupied most of the past ~month of work on the game 

A locomotive (black) driving carriages backward through junctions into a train yard 

I will almost certainly revisit and revise this stuff further, but for now it works well enough I can finally scrub this problem from my brain and concentrate on other areas of the game again. I hope to be devlogging more regularly again now too.

Leave a comment

Log in with itch.io to leave a comment.