Elevators and Trains in Hammer: func_door to func_tracktrain
Elevators and trains in Source and GMod maps: which entity fits, setup with keyvalues, call buttons, path_track routes, causes of crushing and stutter.
You build a two-stop elevator from a func_door or func_movelinear, and multiple floors or trains from a func_tracktrain that follows a chain of path_track entities. Players get crushed when a block damage value is set and the entity keeps pushing while it is blocked. The stutter while riding is described by the Valve wiki as a general trait of vertically moving platforms in multiplayer Source games. It names no fix, so test the lift early. An elevator usually gets stuck because something is in the way or the route is chained incorrectly.
Which entity for which job
| Entity | Movement | Fits | Limits |
|---|---|---|---|
func_door | between two positions, distance = brush size minus Lip | simple lift with two stops | cannot stop in between |
func_movelinear | along one direction over Move Distance, any position in between via SetPosition | lift with two stops, platforms, gates | buggy according to the Valve wiki when parented to another func_movelinear |
func_tracktrain | along a chain of path_track | multiple floors, trains, curves | more work, stops slightly short of its target at higher speed |
func_tanktrain is a train that shoots at the player and can be killed, so it does not fit here. func_train and func_plat are not in GMod's FGD.
A two-stop elevator
A func_movelinear is the least work:
- Build the cabin floor as a brush, press
Ctrl+Tand choosefunc_movelinear. Name:lift. - Set
Move Direction (Pitch Yaw Roll)toUp. The field then reads-90 0 0. - Enter the distance between the two floors in units under
Move Distance, and the pace underSpeed. - Build the walls and ceiling of the cabin as a
func_brushand enter the nameliftunderParent. - Place a
func_buttonon each floor as a call button and connect it to the lift.
Entity My Output Target Entity Target Input
button_up OnPressed lift Open
button_down OnPressed lift Close
lift OnFullyOpen door_top Open
lift OnFullyClosed door_bottom Open
Open travels to the end position, Close back to the start position. OnFullyOpen and OnFullyClosed report the arrival, which is what you use to open the floor doors. How outputs, delays and names work in detail is explained in the article on inputs and outputs in Hammer.
With a func_door the maths is different: a door always travels by its own brush size, minus Lip. A floor 16 units thick that should rise 256 units therefore needs a Lip of -240. Add Delay Before Reset (-1 stay) set to -1, the Toggle flag and an unticked Touch Opens.
Doors and buttons on the cabin
Everything that should ride along gets the name of the moving entity in its Parent field. A child keeps its offset to the parent and follows every movement.
- Cabin doors: two
func_doorentities withParent=lift. Do not use a secondfunc_movelinearhere, because of the parenting bug from the table. - Buttons in the cabin:
func_buttonwithParent=lift. With theDon't moveflag, the button does not travel into the wall when pressed. - Locking buttons: On
OnPressed, send theLockinput to all buttons (lift_button*), andUnlockagain onOnFullyOpenandOnFullyClosed. - Sandbox tools: GMod's FGD offers
Allow Physics GunandSandbox Tool Whitelistfor these entities. Set the former toNoso that nobody in Sandbox grabs the lift with the Physics Gun.
Trains and multiple floors with func_tracktrain
- Place the first
path_trackand give it a name such astrack_1. If you clone it while holding Shift, Hammer counts the name up and fills inNext Stop Targetby itself. - Build the train so that its front points along the X axis, to the right in the top view. When it spawns, it turns towards the next
path_track. - Turn it into a
func_tracktrainand enter the firstpath_trackunderFirst Stop Target. The train spawns there. Max Speed (units / second)caps every speed.Initial Speed (units / second)0 means the train stands still at first.- Set the
No User Controlflag. For an elevator, addFixed Orientationso the cabin does not rotate. - Start it with the
StartForwardinput. At a stop, thepath_tracksendsStopto the train throughOnPass.
For a loop, enter the first path_track as the Next Stop Target of the last one. New Train Speed on a path_track changes the speed from there on, but only takes effect with No User Control.
In GMod, func_tracktrain also has the MoveToPathNode input. It travels to the named path_track on the same path and stops there. That makes an elevator with many floors simple:
Entity My Output Target Entity Target Input Parameter Delay
button_floor3 OnPressed lift_door Close 0.00
button_floor3 OnPressed lift MoveToPathNode floor_3 1.00
lift OnArrivedAtDestinationNode lift_door Open 0.00
The Valve wiki lists two more quirks: a func_tracktrain only updates its direction ten times per second, so fast trains overshoot the track in tight curves. And Reverse or StartBackward can fail if New Train Speed is 0 on the path_track entities involved.
Why elevators crush, stutter or get stuck
Crushing. Each of the three entities has its own damage value: Blocking Damage on func_door, Block Damage on func_movelinear, Damage on Crush on func_tracktrain. The default is 0 everywhere. For the door and func_movelinear the FGD states the value per frame, so small numbers kill quickly. How the entity behaves when blocked can be seen in the public Source SDK code: a func_door reverses, unless Delay Before Reset is negative or Force Closed is set. A func_movelinear never reverses and keeps pushing. With 0 the lift deals no block damage, but a player in the shaft can hold it up. So either keep 0 and close the shaft with floor doors that only open once the lift has arrived, or set a damage value and let the lift win. On a func_door you can react yourself with OnBlockedClosing and OnBlockedOpening.
Stutter. The Valve wiki states that any vertically moving platform seems jerky to its riders in multiplayer games, and gives the network physics of players as the reason. A tip from the discussion page of the Valve wiki's elevator tutorial, not documented and not something every map confirms: do not let players stand directly on the func_tracktrain, but on a func_brush parented to it. For func_brush, the FGD describes the Solid BSP keyvalue for exactly this case, a brush in the hierarchy of a moving object that players stand on. Check the result on a real server with ping, as described in playtesting a GMod map with players.
Props on board. A prop_physics is simulated by physics and, according to the Valve wiki, behaves oddly when you parent it. Decoration in the cabin therefore belongs on the lift as a parented prop_dynamic. Loose physics props on a moving platform can slide around and get wedged. That is experience from practice, not documented behaviour. The prop types are compared in prop_static, prop_dynamic and prop_physics.
Getting stuck. Is something in the way? Does a path_track have the Disabled or Disable train flag set, or an empty Next Stop Target? Were buttons locked but never unlocked, because the lift was blocked and OnFullyOpen never came? On a func_tracktrain, the Is unblockable by player flag helps against players blocking it.
FAQ
Why does my func_tracktrain not stop exactly at the path_track?
The train stops just before its target, more noticeably at higher speed. Lower the speed before the stop, or move the path_track by the missing distance until the cabin sits flush.
Why is the cabin lit wrongly during the ride?
Brush entities carry precomputed lightmaps from their starting position, which no longer match once the cabin travels into a differently lit area. The remedy: build the visible cabin as a prop_dynamic and parent it to an invisible brush. More on this in the Source engine lighting guide.
Can players drive a train themselves?
Yes, as long as the No User Control flag on the train is off. According to the Valve wiki, a player standing on a func_tracktrain can then control it. A func_traincontrols whose Train Name and Parent both point at the train limits this to its brush: the player has to stand inside it and on the train.