NIMBY Rails devblog 2020-09
Internal development notes, very slightly cleaned up and commented a month later.
TL;DR: final train scheduler and multi train scheduler, new game options, autosaving, prototype passenger simulation
Final train scheduler
Continuing with the August work on the train scheduler, a lot more editing commands were added, including clipboards for slots, days and the entire week, usable between trains. The entire train editor mode was also redesigned to fit new features, and the train details editor was made more compact yet it shows more information than ever. This work is meant to try to fit the game UI at 1x scale for 1366x768 users.
The idea behind this editor is to make it easy to handle any line schedule for an individual train the user may imagine. For now it only supports running lines and depot storage orders, but other kinds of orders may be possible in the future, like explicitly waiting at waypoints independent of a line order to wait closer to a station before starting the day run.
Functional depots
Depots were added in August but depot orders were still a placeholder. It is now possible to send trains to depots and have them stored away for the duration of the depot order, as intended.
Multitrain scheduler
I was expecting the new editing commands in the train scheduler to be enough to easily handle a fleet of trains, but it clearly wasn’t. It was still necessary to select trains one by one and carefully select the right day to copy orders, and most importantly, it wasn’t possible to visualize the kind of staggered start times that are probably desired to run a line. For this reason I added another scheduling edit mode: the multitrain scheduler.
In this mode the train listing allows to select any number of trains at the same time just by clicking on them. Or even faster by for example, first filtering by line (which matches any line order a train may have in the whole week), then using the “select all” command in the listing. The scheduling table is limited to one day at a time, but it shows all selected trains as a column. This makes it very easy to copy the same day to another train, or to all of them with a single command. And then tweak each one individually so they start their day at different times, to space out the line.
“Release MUST” features
There’s a list of features that I consider a must for the release of the game, but are not yet started. I decided to start focusing more on them and at the same time move other more “blue sky” features, like the procedural building generator, to post Early Access. There’s a couple in September: new game options and autosave.
Having a “no cash” mode is already one of top requested features, so it was the inaugural game option for the new game options dialog.
I also used the dialog to finally ask for a game name, in order to use in saved games by default. Fans of Galactology will recognize the random name feature working as intended :)
Autosave was also implemented, since it’s a must for any offline sim game. I also improved the file dialog to have sorting options.
Moonshot of the month: individual passenger simulation
Passengers are a very important feature for NIMBY Rails, and until now they were handled by some test code that just keep some counters in trains and stations. It was time to tackle the final design for them. When I have to design and implement a feature I’m not sure about, I sometimes take a maximalist approach to it. In this case I wasn’t sure about what level of detail to use to represent passengers. The current system, just some counters, was too coarse and also very dependent on some pending statistical modelling to make it more realistic.
Wouldn’t it be nicer if the game actually attempted to model individual passengers? A lot of natural individual behaviors are relatively simple to implement, but in aggregate they are very complex and much more interesting than finding the right statistical distribution. So I decided I’m going to try to do it. I’m implementing individual passengers on a game that wants to model a worldwide train network supporting thousands of trains over tens of thousands of stations, with multiplayer. I estimate a big game will need to support in excess of a million spawned passengers.
Those passengers won’t just be a random spawn that gets inside a train and randomly gets out. They are spawned with a destination in mind, in a station that has access to such a destination, including indirectly via line transfers in other stations. The passengers will hop in, and then hop out of the train when they reach their destination station, or when they reach a station that is a connection to a line they have to ride to reach it, for any amount of steps they have to make. They then wait in that station until the right train of the right line arrives and takes them to the next leg of their trip.
This may sound crazy but it is actually relatively simple to implement, and I already have a functional prototype. First a graph of all the stations in lines is created. It links every station with every other station it has a connection to, based on the lines that stop in them, not on the tracks. It’s not a track map, it’s a logical connection map.
Then passengers are spawned in stations. For now this happens on demand, when a train arrives. I don’t see other options, since allowing spawns without a train would very quickly create immense amounts of passengers that consume RAM and must be streamed in multiplayer. So for now the station counter remains, and it is used to control this spawn. In the future, if/when procedural buildings are implemented, I would like to use them as spawn points.
These passengers are spawned with a destination station in mind. It is critically important these destinations are possible to reach, so this is already implemented by doing a quick random walk in the network connectivity graph until a certain random distance threshold has been reached. I’m not happy with this. A better idea is to have traveler archetypes, and depending on the area, create some travelers who want to go very far, and some others who are just local commuters and just want a subway ride, and other in between. Then find a reachable destination that fits their preferred distance by k-nearest algorithms or similar. I will revisit passenger spawn for sure.
After getting into a train, passengers then make a decision every time it stops in a station: do they get off, or do they stay. They get off when the station is either the final destination, or when, after a heavily cached pathfind, they decide it’s better to get off and wait for a different train on a different line. These passengers are kept as fully instanced passengers in a waiting list in the station, and given priority in case their next train is close to full.
Despite being a first pass prototype, the system works, and it is already capable of very interesting behaviors like passengers hoping into a subway, getting out in a central big station, then waiting for a high speed long distance train to take them 1000km away.
It would be a shame to hide this fascinating system behind a single number in the UI, so I want to make this visible to the user. For now I’ve implemented a debug window to inspect the passengers riding a train:
In only shows two values per passenger, because there’s only these two values. It will be critical to minimize the amount of memory consumed by the passenger instance and to make sure it all fits in flat arrays. For now it shows the time the passenger has been traveling since it first boarded a train, and the final destination station they have.
Another debug window shows the waiting transfer passengers in a station:
It shows the list for the central station. This station is a stop for two lines, red and green. A green line train just departed, and left some passengers in the station waiting a train. All the destinations listed are in the red line, which makes sense, since the green line trains will never visit them. The passengers, when running the pathfinder, noticed this, and also noticed the station they just stopped into is a connection to reach these stations, so they decided to get off and wait.