Heron
Back to Work

Trainline — Train Dispatch Simulator

A 3D top-down strategy game: designed and implemented a complete gameplay loop of station dispatching, train pathfinding and passenger flow.

Gameplay DesignSystems DesignUI SystemsUnityStrategy

Role

Designer & Developer

Engine

Unity

Team

2-person team

Duration

Oct – Dec 2025

Overview

Trainline is built from three things: railways, stations and trains. Each level hands the player an already-established railway network and a set of trains that are temporarily out of service. The task is to reroute those trains and manage the passenger flow building up at every station.

The player works in a locked 2.5D top-down view across four modes — observe, route planning, scheduling, and passenger flow — pausing time whenever they need to think.

Trainline control scheme showing the four modes, camera keys and the schedule slider
Controls — modes 1-4 (Observe / RouteSet / Schedule / PassengerFlow), QWE+ASD for train shifting and view movement, F to focus a train, and the 0:00-24:00 schedule slider. Routes must be cyclic.

Motivation & Purpose

The design did not start from a train fantasy; it started from research into how real railways are organised and why they fail.

Two organisational models. The British network separates responsibilities: a sole infrastructure provider (Network Rail) builds and maintains the track; Train Operating Companies supply drivers, crew, routine maintenance, ticketing, passenger information and timetable execution; and a Rail Operating Centre handles route planning, timetable adjustment and signalling, increasingly digitalised through ETCS on-board/track control plus a TMS handling rerouting, train priority and fault detours. The Chinese network concentrates all of this in a single operator. That contrast is what makes dispatching interesting: authority is split, so decisions have to be negotiated against a timetable.

Why trains are late. Roughly 40% of delays trace to infrastructure failure — signal path faults, switch failures, power supply failures, blocked or under-repair lines. The rest come from vehicle malfunction (traction, brakes, door control, air conditioning, control units, or an ETCS device dropping out and forcing manual running), personnel incidents (late drivers and crew, scheduling conflicts), accidents and severe weather, and finally systemic problems — above all a timetable so dense that one slow train makes every train slow.

The cascade that became the game. That last category produces a chain:

[Signal malfunction] → [Train A stopped] → [Train B waiting for signal] → [Train C rerouted and moving slowly] → [Station at the junction is occupied] → [Subsequent trains delayed] → [Timetable shift across the entire line]

This chain is the actual subject of the game. Rather than simulate every failure mode, I distilled the operational response to it — give way and train priority — into the player's core decision. The stated goal became: maximise passenger flow without delays or accidents.

Design Framing

Resources. Cities and stations carry a passenger flow, a train throughput and a boarding speed. Railway lines come in slow, express and high-speed grades, and trains in local, express, high-speed and maglev classes — with the constraint that a high-class train cannot run on a low-class track. Money ties it together: maintenance costs currency, passenger flow earns it, and construction, new trains and hiring spend it.

A daily cycle. Trains follow the same schedule every day, with departure and arrival times plus maintenance windows, and the schedule refreshes on arrival at each station.

Player verbs. Set a stop-and-wait so a train gives way to a higher-priority service; set a section speed so a priority train passes quickly; add services to move more passengers and earn more; add railway lines.

Strategy and the intended insight. Balancing passenger flow against revenue is what determines a railway's priorities, and the optimal solution the player is meant to discover is simply that the faster the train, the higher its priority. Success is evaluated on two numbers: delayed trains and accidents.

Scope decisions. To keep the loop legible I fixed several rules early: a surplus of passenger traffic is allowed to accumulate each day until the player finds a solution that meets demand; a train collision immediately ends the game; the player can always pause time to schedule; and difficulty grows across multiple levels and multiple trains rather than through added mechanics.

Greybox scene with spline railways connecting station blocks
22/11 — greybox network: spline-defined railways connecting stations, with trains following segments and reversing direction when a spline runs backwards

System Architecture

I decomposed the game into modules and described each one as a View / Control / Mediate triple, so that presentation, input and the authoritative state stayed separable:

  • Camera Controller — locked 2.5D top-down view with WASD translation and zoom.
  • Train Mobility SystemView: route-following animation and sound. Control: train data (current station, speed, route guidance). Level tooling: railway paths as splines, which restrict where a train may run. Mediate: route maintenance — add a route, modify a route, record modification time — exposing interfaces for the dispatch and level systems to call.
  • Train Dispatching SystemView: a route settings panel kept independent of train movement, plus a timetable settings panel. Control: point-and-click route input and slider-based arrival/departure editing. Mediate: the timetable currently in effect.
  • Passenger Flow SystemView: daily station traffic UI. Control: a level driver, where each station's flow per region (Scotland / Europe / China) is set through level design and difficulty testing, and a train passing through reduces flow. Mediate: a daily refresh on a read of the clock.
  • Time SystemView: clock UI. Control: adjustable time flow rate.
  • Track EventsView: an express closely following a slow train and sounding its horn. Control: the express decelerates to match the slower train ahead.
Stations labelled with passenger flow deltas in the greybox scene
23/11 — passenger flow made visible per station; a train passing through subtracts from a station's waiting flow

Thinking Through the Build

Development ran from 11 October to 16 December 2025. The log records a clear change of emphasis partway through.

The preliminary stage was explicitly about building a framework and getting functions working: camera setup, TrainMoveOnSegment, SetRoute, highlighting chosen stations, arraying the stations and railways that make up a route, and flipping travel direction when a spline was authored in reverse. By 22/11 a timeline in TrainManager was setting the station sequence of a journey, and by 23/11 passenger flow existed as a real quantity with event actions and a UI readout.

At 24/11 the goal changed to improving the user experience. That pivot is what produced the game's texture. Time was tied to real time at a fixed ratio — one second of play equals thirty minutes, so a full day takes 48 seconds — split into a 5:00–24:00 working day and a 0:00–5:00 night shift. Trains gained states, and TrainManager began maintaining the departure schedule as events, reading TimeController to depart on time.

The following day exposed a modelling problem worth recording: a long-distance train may complete only one cycle a day while a short-distance train completes several. The state machine was iterated to Running / Docking / Waiting for departure / Maintenance, with boarding deliberately not waking a train, and one rule locked in — a train's departure station and its final return station must be the same every day. That single rule is why the interface insists routes be cyclic.

Station passenger flow figures across the network
27/11 — station traffic at level-design scale, the pressure the player is dispatching against

UI design followed on 27/11, then implementation: a RouteEditPanel that reads a selected train's information from the backend and lets the player connect stations, with collider-based station buttons, a List<Station> store, length detection and route-sequence appending; and a TimeTablePanel with a slide bar per railway section, arrival station display, train icons and timestamps progressing in real time.

Train ownership side panel mockup with showcase, focus and set route actions
27/11 — the "Train You Owned" panel: showcase, focus and set-route actions per owned train

A Game Mode Toggler arrived on 4/12 (Num 1 common, Num 2 route planning, Num 3 schedule planning), closing panels and switching input mode together, so the four modes on the controls card became a single coherent state machine rather than overlapping UI.

The SchedulePlanPanel on 7/12 is the most design-driven piece of implementation. When a train initialises, the panel spawns timeline columns and schedule blocks, and each block's width is computed from railway length divided by speed — so the timetable is a direct visual of physical travel time. Dragging a block's left edge sets a station's departure time and its right edge the arrival time at the target station, which makes scheduling a spatial act rather than a numeric one.

Timetable panel mockup with 0:00 to 24:00 timeline and movable schedule blocks across Edinburgh, Glasgow and Dundee
27/11 — timetable panel across a 0:00-24:00 day; movable blocks sized by distance and speed, here running Edinburgh - Glasgow - Dundee - Edinburgh

Difficulties & Solutions

Flickering when dragging adjacent sliders (15/12). Cause: the slider's DepartTime was being updated twice, because the dragged block also included the block sitting under the mouse cursor. Solution: only the change under the cursor is handled in OnDrag; every other affected block is updated through separate functions instead of being swept into the same drag.

The timetable not reflecting route changes in time (15/12). Cause: the moment the timetable opened was tied to the moment the block panel refreshed, and blocks auto-aligned to the leftmost position on every initialise, so edits were overwritten. Planned approach: re-read routes every time the timetable opens by initialising on each OnEnable, and keep the timetable running in the background, merely showing and hiding it. Final solution: a block aligns to the far left only after its first repositioning or after a reset by the route editor, and skips alignment from then on.

Train routes destroyed when set to non-loop (15/12). Resolved by optimising the loop detection logic — a direct consequence of the "same start and end station each day" rule.

Trains not relaunching on a new day (5/12). Fixed alongside TrainRouteEditor work, so the daily cycle actually restarts.

Selection ambiguity in passenger flow visualisation. Selecting a station to inspect flow was ambiguous, so selection now highlights the railway rather than the station.

Rear-end collisions between express and slow trains. This one was answered with design rather than code. Since a collision immediately ends the game, the player needs to read the risk before it becomes an accident: an express closing on a slower train automatically decelerates to follow it, flashes a red light, and sounds its horn, while the timetable panel surfaces the possibility of a crush in advance. The intended response — a slower train stopping at a station to give way — is exactly the "give way" behaviour the research identified.

Delivery & Next Steps

Trainline reached a working demo with a standalone level. Delivered in the window were the foundational operational logic of railway, station and train; three UI systems (RoutePlanPanel, TimeTablePanel and PassengerFlowUI); art for most scene resources matching the style setting; a daily mileage limit and daily station flow update; and a level 1 with a passenger traffic target and a victory screen. The two of us worked across separate versioned branches, integrating art and program in the final week.

Designed but not yet built, and the next things I would add:

  • Track event system — train collisions as a live event, and train maintenance time as a scheduling constraint.
  • Shop system — train trading and station upgrades, which would turn the money resource into a real decision layer.