It's funny that nearly 3 years after its release Half-Life continues to surprise. The effect that this tutorial explains was, as far as I know, available in all the versions of Half-Life since the original. However, I had never seen it used in a map, nor had I even known it was possible, until the release of Half-Life: Blue Shift. Gearbox used this trick several times in Blue Shift. One example (not to spoil too much, I hope) is in the train yard section. There are a couple of huge spools of rope on a flatbed car, held in place by chocks. When the player destroys the chocks, the spools start to roll, break some crates, and finally fall off the car onto the ground and come to rest.
Note: The latest version of the Half-Life FGD and many other FGDs contain the necessary properties to set the angular velocity of the func_train.
This effect (moving and rotating entities) was totally new to me, and to the vast majority of Half-Life mappers out there. The method of this tutorial was figured out by examining the Blue Shift maps, but combing through the Half-Life SDK would have also revealed this trick.
Now you might say to yourself, "Self, I seem to remember spinning elevators in Xen in the original Half-Life. Those are moving and rotating entities" And you'd be right. But these were func_platrots - essentially spinning func_plats. As such, they have a limitation of only 2 points to move between (the "lowered" and "raised" points). As you'll soon see, rotating func_trains are a lot more powerful and controllable and can lead to some nifty sequences.
Also, don't confuse rotating func_trains with func_tracktrains. A func_tracktrain will turn to face its next path_track - excellent examples are the opening train ride in Half-Life, and the small trains you ride throughout "On A Rail." Rotating func_trains will simply rotate constantly whenever they are moving.
How It Works
Normal func_trains simply move along their path_corners while keeping their orientation with respect to the XYZ axes. Rotating func_trains introduce rotation around any or all the axes while they move along their path_corners. Rotation allows func_trains to change from 3 degree-of-freedom entities (movement along 3 axes) to 6 degree-of-freedom entities (movement and rotation along all 3 axes), if you are familiar with those terms.
Getting a func_train to rotate involves 2 things:
- Make sure the func_train includes an origin brush. Since it will be rotating, the origin brush must be used to set the point of rotation.
- Add a key called avelocity to the func_train (which will be incorporated into future released versions of the fgds). This allows you to specify the train's angular velocity (in degrees per second) around all 3 axes. The format is in typical Pitch-Yaw-Roll format - i.e. Y Z X. For example, the avelocity entry -360 100 0 would make the func_train rotate around the Y-axis once per second, the Z-axis at 100? per second, and have no rotation around the X-axis. There are several ways to add the avelocity key to the func_train entity:
- With SmartEdit mode Off, you can add a key called avelocity, with values in Y Z X format.
- You can add the following line to the fgd file, somewhere in the func_train code (which can be edited in any text editor):
avelocity(string) : "Angular velocity (Y Z X)" : "0 0 0"
The diagram below shows the setup of the XYZ axes and the corresponding rotation around those axes. I haven't done thorough testing to determine which direction of rotation is positive and which is negative, but it's likely that the rotations follow the right-hand rule, if you are familiar with that term. If not, it's a simple matter of trial and error to get your trains rotating in the proper direction - if you want them to spin the other way, simply change the sign of that avelocity entry (i.e. swap positive and negative).
How it can be Used
First, if you have not done so, I suggest you run and/or examine the example map. There are a number of rotating func_trains in the map, outlined below.
The SuperCobra attack helicopter (modified from a prefab by Kevin Quang) uses 3 func_trains total - 1 normal train for the body, 1 train rotating around the Z-axis for the main rotor, and 1 train rotating around the Y-axis for the tail rotor. The avelocity entries:
- Main rotor: Angular velocity (Y Z X) = 0 6000 0
- Tail rotor: Angular velocity (Y Z X) = 4000 0 0
The Army truck (modified from a prefab by Barry Bollinger) uses 4 total func_trains - 1 for the truck body and 3 identical rotating trains for the wheels, rotating in the Y-axis. The avelocity entries:
- All Wheels: Angular velocity (Y Z X) = -782 0 0
You may ask where the hell I pulled the -782 from. The answer is simple: geometry.
The F-16 that does barrel rolls is modified from a prefab by Adam Grebinsky, and only consists of one func_train because the entire thing rotates in the X-axis. The avelocity entry:
- F-16: Angular velocity (Y Z X) = 0 0 250
The tire rotates around the Y-axis in the opposite direction of the truck's wheels. Notice how the tire appears to wobble as well - this is because it has been rotated in the Z-axis as built. The avelocity entry:
- Tire: Angular velocity (Y Z X) = 651 0 0
Each barrel at the end is a different rotating func_train, and I just gave them all different avelocity values to make them tumble through the air.
Note that a couple of the vehicles in the example map actually combine simple func_trains and rotating func_trains. In both cases (the helicopter and the truck) all sections are named identically, to ensure they are triggered simultaneously. The trick to getting these combinations to work and look correctly is setting up the path_corners carefully, so the trains do not move relative to each other and destroy the illusion. Once I set up the helicopter as 3 func_trains, I carefully placed the 3 path_corners in the center of each segment. Then, by copying the group of 3 path_corners to new locations, I could ensure that the corners did not change position relative to one another, and hence neither would the trains. The same was done for the truck and its 3 axles.
You may have noticed that the vehicle sounds are not dynamically emitted from the func_trains as they move - I used ambient_generics instead. Unfortunately, you are limited to the func_train sounds that are hard-coded into Half-Life. The ambient_generic trick makes for a passable solution, but not ideal. Hopefully, customizable func_train sounds are things the Spirit of Half-Life mod can incorporate into its code in the future.
Note also that the rotating func_trains always rotate when they are moving, and stop rotating immediately when they reach the last path_corner. Unless the sequence is carefully planned, this can end up looking really bad. Take a close look at the rolling tire after the truck "explosion" in the example map - see how it just stops instantly when it hits the antenna? It looks kind of crappy, but my thought was that the player wouldn't notice since the barrels are hurtling at his/her face at about the same time. What I would really liked to have done there is have the tire bounce off, and slowly roll the other way. But this is not possible with the current Half-Life code (unless you use an env_render trick), since the path_corner's New Train rot. Speed property has no effect on rotating func_trains - the only way to change the rotation of a func_train is with new code (again, hopefully this too will be added in the next version of the Spirit of Half-Life).