# Railroader Physics Overhaul A [Unity Mod Manager](https://www.nexusmods.com/site/mods/21) mod for [Railroader](https://store.steampowered.com/app/1638770/Railroader/) that reduces CPU time spent on train physics without sacrificing gameplay. This mod is **HEAVILY** experimental. Although it should be fully compatible and shouldnt corrupt your save, any use on your existing saves is your own risk and I accept no liability. ## Features ### Physics Optimizer (LOD fast-path) Cars farther than a configurable distance threshold (default 30 m) get a lightweight position update instead of running the full Bezier-curve solver every physics tick. Every few ticks the car resyncs against the full solver to stay numerically correct. The result: far fewer expensive curve evaluations per frame, with limited to no visible difference to gameplay. ### Auto Freeze Consists that are both slow (< 0.3 m/s) and far from the camera (> 200 m) are skipped entirely by the Verlet integration step. If running the wonderful Stock Optimizer mod please disable this feature and beware that there might be instability. ### In-Game Profiler Overlay A movable HUD window shows a stacked line chart of frame time over the last 300 physics ticks (~5 seconds). Layers from the bottom up: | Color | Layer | |-------|-------| | Blue | Render frame time (GPU + CPU render work) | | Green | `FixedUpdate` total (all physics) | | Yellow | `Tick()` only (Verlet integration) | | Orange | `PositionCars` (3D position update) | Reference lines at 16.7 ms (60 fps) and 33.3 ms (30 fps) are always on screen regardless of how well the game is running. Toggle the overlay with `/rpf overlay` or from the UMM settings panel. ## Theory of Operation ### Why physics is expensive in Railroader Each physics tick (`FixedUpdate`), `TrainController` calls `PositionWheelBoundsFront` for every car. This method walks the spline to find the 3D position and orientation for each truck — two expensive distance-to-parameter lookups per car. With long consists on curved track, this dominates frame time. ### Physics Optimizer detail The mod patches `Car.PositionWheelBoundsFront` with a Harmony prefix. When a car is far from the camera and all its couplers are coupled: 1. **Track bounds** (`WheelBoundsF`/`R`) are still updated every tick — the constraint solver needs them. 2. **Truck positions** are computed, but only every N ticks (controlled by `ResyncInterval`). Between resyncs, the car keeps the body rotation from the previous tick. 3. **`PositionAccuracy`** matches what the full path would use (`High` when visible, `Standard` otherwise) so there is no positional jump when the camera enters the threshold. 4. **`OnPosition`** is fired so `TrainController.CarDidPosition` runs — this keeps the car-culler bounding sphere, spatial hash, and segment cache current. 5. **`LocationF`/`LocationR`** are updated each tick so the segment cache never goes stale. ### Auto Freeze detail `ShouldSkipTick` is replaced. The stock implementation skips the entire solver when all cars are at rest; this mod tracks each car individually and skips cars that are far away and *very* slow, reducing wasteful simulation of parked cars. ### Sampling model (the "% of FixedUpdate" number) The profiler measures time with `Stopwatch.GetTimestamp()` (nanosecond resolution, no GC pressure). It accumulates over 60-tick windows. "% of FixedUpdate" is how much of the total physics budget `Tick()` alone consumed that window — a high percentage means Verlet integration dominates; a low percentage means air brakes, coupler forces, or position updates are the bottleneck. ## Installation ### Requirements - [Unity Mod Manager](https://www.nexusmods.com/site/mods/21) installed and configured for Railroader - Railroader (Steam) ### Installing with Unity Mod Manager (recommended) 1. Download the latest release zip. 2. Open Unity Mod Manager (Ctrl+F10 in game, or the standalone installer). 3. Drag the zip onto the **Mods** tab, or click **Install Mod** and select the zip. 4. Launch the game. ### Manual installation 1. Unzip the release into `\Mods\RailroaderPhysicsOverhaul\`. 2. The folder must contain `Info.json` and `RailroaderPhysicsOverhaul.dll`. 3. Launch the game. ## Console Commands Open the in-game console and type `/rpf `: | Command | Description | |---------|-------------| | `/rpf help` | List all subcommands | | `/rpf overlay` | Toggle the profiler HUD | | `/rpf timing` | Print the current timing report to the console | | `/rpf dump` | Dump the selected consist's state (cars, speed, coupling, segment) | | `/rpf lod ` | Set the LOD distance threshold | | `/rpf lod off` | Disable the LOD fast-path entirely | | `/rpf freeze` | Freeze the selected consist's physics | | `/rpf unfreeze` | Unfreeze the selected consist | | `/rpf forceactive` | Toggle ForceActive (bypasses the at-rest check for profiling) | ## Settings Open UMM (Ctrl+F10), select **Physics Overhaul**. All settings auto-save on change. | Setting | Default | Description | |---------|---------|-------------| | Physics Optimizer | On | Enable/disable the LOD fast-path | | Distance Threshold | 30 m | Cars beyond this distance use the fast path | | Resync Interval | 4 ticks | Full recalculation every N ticks (1 = every tick, no dead-reckoning) | | Auto Freeze | On | Enable/disable the individual-consist freeze | | Auto Freeze Distance | 200 m | Consists beyond this distance are eligible for freezing | | Auto Freeze Speed | 0.3 m/s | Maximum speed for a consist to be considered stopped | | Show Overlay | On | Show/hide the profiler HUD on game load | | Overlay Opacity | 100% | Profiler window background opacity | | Blacklist/Whitelist | Off | Filter specific road numbers in or out of LOD treatment | ## Building from Source ``` dotnet build PhysicsOverhaulMod.csproj ``` The build output goes directly to `\Mods\RailroaderPhysicsOverhaul\`. The project references game DLLs relative to the source folder, so it must be placed inside the Railroader game directory. **Prerequisites:** .NET SDK 6 or later, Railroader installed in the same directory tree.