This tutorial will explain the basics of making a user-controlled boat. This one will be powered by two air fans and in the tutorial I will explain how to make them work, but if you just follow certain parts this tutorial will show you the basics of making any player-driven vehicle you would like; space ships, cars, etc... This is an advanced tutorial. You must have a good grasp on how entities work to complete this.
To begin...
Make a little test map to surround your boat. Put some water where the boat will drive around in and a couple spawns. Nothing fancy, just enough to actually test the boat on. (Screen 1)
The boat we're making will be based off of a func_physbox. So for now you're going to make a hull for the boat out of brushes. This is probably the most difficult part. I recommend using the vertex editing tool. It will give good results. The boat I've made here (Screens 2 and 3) is just going to be a tiny rowboat, but will have engines stuck to it later.
IMPORTANT:
You MUST make the boat body out of a wood texture or it will sink. If you want metal boats, try editing a metal texture to act as wood with the surfaceprop variable in the .vmt file. That's how I made the boats in zm_natalyas_ship_v2a.
In this tutorial I named the boat hull func_physbox to LittleBoat00. Uncheck all the spawn flags except for Motion Disabled. But leave that flag unchecked while testing how well your boat floats and stuff. Once you get that satisfactory, put it to disabled.
Once you've gotten your boat hull's flushed out, start adding some of the essential details. You can see in screen 4 that I've added two engines, two fans, and a control panel and steering wheel to drive my boat. These should be prop_dynamic_override with their parents set to the func_physbox boat hull you already have. In this I didn't parent the fans and the steering wheel yet as I'm going to make those move when the boat moves.
For the Fan Blades:
Put two small nodraw blocks at each of their rotating axes. Name the left one Port Spinner and the right one as Starboard Spinner. Set flags to Y Axis, Not Solid, and Large Sound Radius. Set Max Rotation Speed to 720. Parent the fan blade models to their respective func_rotating that we just made here. Parent the func_rotatings to the boat hull.
The next step is to add an ignition button like mine in Screen 5. Parent it to the boat hull and check the flags Don't Move, Toggle, and Use Activates.
Now for the first output... Make a new output:
On In
Target entities named: LittleBoat00
Via this output: Enable Motion
And check the Fire Only Once box for that output.
What that does is it will let the boat move when you turn it on, but won't move beforehand. However it's not going to hold it in place when you turn it off.
The next thing to do is add a game_ui entity to the map. Put it close to the front of the boat such that the player is always looking towards it even though it won't be rendered in game. Also add two Logic_Compare entities and a phys_keepupright.
The Logic_Compare entities can be placed anywhere, but the phys_keepupright should be placed over the top of the boat.
Now to set some properties for the game_ui...
Click on the game_ui and name it something. Mine is named LittleBoat00 Interface. Then turn off smart edit and click the add button. In the first field write parentname and in the second field write the name of the func_physbox you're using as a boat hull. So for mine it's LittleBoat00. You can see what mine looks like in Screen 6.
Next you want to set the game_ui's field of view to 0.0 and the flags to Freeze Player, Hide Weapon, and Jump Deactivates. The point of parenting the game_ui to the boat was so that with FieldofView 0.0 if you look away from the console you lose control of the boat, since jumping doesn't always work. If you don't parent the game_ui, well, once you drive so you can't see it you'll lose control of the boat. This isn't good. D:
For the phys_keepupright, name it something, mine's called LittleBoat00 Keep Upright. Set Angular Limit to 10 and Target Entity to your boat hull.
For the Logic Compares, don't worry about them right now just name them Left//Right Compare and Forewards//Backwards compare.
Now we're going to do more with the boat itself again. We'll come back to the controls in a bit.
Here we're going to add some sounds.
I added six sounds to the back of the boat. They are:
Motor Start
Flags = Start Silent, Not Looped
Motor Shut Down
Flags = Start Silent, Not Looped
Motor Idle
Flags = Start Silent
Fan Idle
Flags = Start Silent
Motor Engaged
Flags = Start Silent
Fan Engaged
Flags = Start Silent
Make their source be the engines.
Go back to the start button you made earlier. We're going to add more outputs.
Add this output:
On In
Target LittleBoat00 Keep Upright
Via This Input: Turn On
Fire Only Once YES
Add this output:
On In
Target Motor Start
Via This Input: Play Sound
Add this output:
On In
Target Motor Idle
Via This Input: Play Sound
After a delay of 2 Seconds
Add this output:
On In
Target Fan Idle
Via This Input: Play Sound
After a delay of 2 Seconds
Add this output:
On Out
Target Fan Idle
Via This Input: Stop Sound
After a delay of 2 Seconds
Add this output:
On Out
Target Motor Idle
Via This Input: Stop Sound
After a delay of 2 Seconds
Add this output:
On Out
Target Motor Shut Down
Via This Input: Play Sound
Now your boat should sound like the engine's running when you turn it on.
I also added a little running light hovering over the control panel by using an env_sprite and making it show when you press in the ignition and hide it when you press it out, turning off the boat. Now is a good time to stop and see that everything runs properly so far.
Once you've made sure everything works you can move on to the fun part -- making the boat actually move.
Go to your steering wheel. On it make a nodraw button. This button will be used to make the player gain control when they press the steering wheel.
Parent it to the boat hull and set flags to Use Activates, Starts Locked, and Don't Move. Name it what you like. Under outputs add:
On Pressed
Target (Your Game UI)
Via this Input: Activate
(These will let you control the boat with your directional movement controls.) See Screen 7:
Here you also want to edit the ignition button you made earlier. Add the following outputs:
On In
Target: (Steering Wheel Button)
Unlock
On Out
Target: (Steering Wheel Button)
Lock
Now you need to add thrusters to move the boat before you can get the Logic_Compares to move it.
Create 8 phys_thrusters roughly level with the boat. 2 in front, 2 in back, and 2 for each side. (Look at Screen 8 for Positioning.)
Name the Front two as LBoat 00 Fore Thrust and the back two as LBoat 00 Aft Thrust. With the 4 middle ones on the sides, name the top left and bottom right as LBoat 00 Starboard Thrust and top right & bottom left as LBoat 00 Port Thrust. (These will turn it.)
Set the attached object for all of them to the boat hull. Set their flags to Apply Force, Apply Torque, Orient Locally, and Ignore Mass.
For the Fore and Aft thrusters set force to 160.
For the Port and Starboard thrusters set force to 30.
For the Fore Thrusters set their direction to 90 Degrees.
270 Degrees for Aft.
For Starboard, top left should be 0 degrees while bottom right is 180 degrees.
Port gets the same -- Bottom Left is 0 and Top Right is 180.
Now we're going to set steering controls.
Copy everything from Screen 9 onto your Left//Right logic_compare.
Stop here to test that the boat can be turned left and right.
Now for the final, forewards and backwards controls.
To start, copy everything you see in Screen 10.
Then copy everything from Screen 11. Note: The last output on 10 which is kinda cut off is the first output on screen 11.
With that completed your boat should now move and sound properly and the fans should work too. :)
You can use this knowledge to make other vehicles such as planes, cars, and weapon turrets too; for example planes would just have a phys_thruster beneath them pushing them up into the air. This tutorial is mostly just meant for letting people understand how to make the controls.
One side note:
If you're putting a drivable func_physbox-based vehicle into your map you will need to make sure that sv_turbophysics is set to 0 or else it won't work. Most servers default to 0 but some special ones like zombie mod have it set to 1. You can just put a point_servercommand in your map to override the server setting.