Vehicles (modeling)

From Valve Developer Community
Jump to: navigation, search
日本語

This page describes how to set up a model for use as a functional vehicle, with basic animations, and optionally, gauge animations.

SMD prerequisites

Reference model

Your reference model at minimum needs the following bones in order to work:

  • suspension front left -> steering front left -> wheel front left
  • suspension front right -> steering front right -> wheel front right
  • suspension rear left -> wheel rear left
  • suspension rear right -> wheel rear right
  • view, located where the driver's view should be

Optionally:

  • steering wheel
  • gauge needle(s)

All of which should (probably) be children of a static root bone, and should follow the above hierarchy. You can name them anything, as long they're the uniquely named, and you edit the following code examples accordingly.

Your respective meshes need to be rigged to their respective bones.

Collision model

All vehicles need physical models, so you need to set up a collision model, with a weight value close to what it would be in real life. See $collisionmodel.

To get started, you can simply use a copy of the reference model of your vehicle, but make sure to remove the wheels from the model, as wheel collisions aren't defined in the collision model itself, but at runtime, based on the entire compiled model's wheel attachments. Note that doing this will give your model a convex collision model.

If you have the time for it, modelling a low-poly version of the vehicle specifically for use as a collision model can be useful, both for optimization, and because it can be concave. See Collision mesh.

Basic animations

Vehicles require a few basic animations in order to work properly.

  • An animation SMD with only the suspension bones animated. This can be 3 frames (fully compressed, neutral, fully extended). This sequence determines the suspension travel each wheel has.
  • An animation SMD with only the wheel bones individually spinning one full revolution. This can be 6 frames, and needs a linear animation curve, defined before the animation is exported in the model editor you use.
  • An animation SMD with the steering of the wheels, with only the steering bones (and the steering wheel bone if it exists) and not the wheel bones animated. This can be just 3 frames (left, centered, right). Be sure to note the exact angle that the wheels turn to and make it consistent. You can put that angle into the vehicle’s script file so that the physics match the animation more closely.

You might want to make all of these animations have linear curves.

Note that with just 3 frames, if you animate a steering wheel, it can't turn further than 90 degrees one way without breaking the centered position, which might be unsuitable for some models. See Making the steering wheel turn further than 180 degrees.

QC setup

Attachments

Wheels

You need to set up attachments for all four wheels, to define where they are located in the model.

$attachment "wheel_fl" "<wheel front left bone>" 0 0 8
$attachment "wheel_fr" "<wheel front right bone>" 0 0 -8
$attachment "wheel_rl" "<wheel rear left bone>" 0 0 8
$attachment "wheel_rr" "<wheel rear right bone>" 0 0 –8

You must name the attachments as above. You can however name their respective bones anything, and you can place the $attachment lines anywhere in your .QC.

Only four wheels can be simulated in one vehicle. Their attachments are named with two letter codes:

  • fl "front left"
  • fr "front right"
  • rl "rear left"
  • rr "rear right"

One important detail is that the wheels are simulated as spheres. For this reason, you might want to pull the attachments slightly inwards. The origin of the wheel bones is actually the center of the wheel graphically, but in order to avoid having the spherical wheel collisions poke out from the sides of the vehicle (thus brushing against things next to it), you might want to pull the attachments in by a bit.

For example, this would place the wheel sphere exactly over its graphical representation:

$attachment "wheel_fl" "<wheel front left bone>" 0 0 0

While this adjusts it inwards by 8 inches:

$attachment "wheel_fl" "<wheel front left bone>" 0 0 8

The amount to pull them in depends on the size of the wheels and the collision model of the vehicle. In this case, the wheels have a radius of about 22.5 inches and I’ve pulled them in by 8 inches. Using this ratio is a good starting point.

Driver

This line defines where your view is going to be when driving.

$attachment "vehicle_driver_eyes" "<view bone>" 0 0 0

As with the wheel attachments, this attachment must be named vehicle_driver_eyes.

This line defines where

Miscellaneous attachments

These are some miscellaneous attachments that might be required, with approximate positions that you might have to tweak.

$attachment "vehicle_feet_passenger0" "<view bone>" 0 0.00 -27.00
$attachment "vehicle_engine" "<root bone>" 40.00 0.00 17.00

The vehicle_feet_passenger0 attachment determines where the driver's third person player model is located.

Both attachments can use their own unique bones.

Pose parameters

You need the following $poseparameter lines to allow the physics to interact with your model.

This line is for the steering. –1 is left, +1 is right.

$poseparameter "vehicle_steer" -1 1

These lines are for the suspension states of each wheel. 0 is compressed, 1 is extended.

$poseparameter "vehicle_wheel_fl_height" 0 1
$poseparameter "vehicle_wheel_fr_height" 0 1
$poseparameter "vehicle_wheel_rl_height" 0 1
$poseparameter "vehicle_wheel_rr_height" 0 1

These lines are for the rotation of each wheel. +/- 180 degrees from base rotation.

$poseparameter "vehicle_wheel_fl_spin" -180 180 wrap
$poseparameter "vehicle_wheel_fr_spin" -180 180 wrap
$poseparameter "vehicle_wheel_rl_spin" -180 180 wrap
$poseparameter "vehicle_wheel_rr_spin" -180 180 wrap

All of the above should not be renamed.

Animations

Here’s an example .QC snippet that sets up animations for the pose parameters, including a steering wheel in the steering animation.

$sequence "idle" "<reference model SMD>"  activity ACT_IDLE -1 loop fps 30
$animation neutral "<reference model SMD>" frames 0 0

// steering
$weightlist front_wheels { "<steering front left bone>" 1.0 "<steering front right bone>" 1.0 "<steering wheel bone>" 1.0 }
$animation turn_left "<steering animation SMD>" frame 0 0 subtract neutral 0 weightlist front_wheels
$animation turn_right "<steering animation SMD>" frame 2 2 subtract neutral 0 weightlist front_wheels
$sequence turning { turn_left turn_right blend vehicle_steer -1 1 } weightlist front_wheels delta autoplay


// front left
$weightlist wheel_fl { "<steering front left bone>" 1.0  "<suspension front left bone>" 1.0 "<wheel front left bone>" 0.0 }
$animation wheel_fl_low "<suspension animation SMD>" frame 0 0 subtract neutral 0 weightlist wheel_fl 
$animation wheel_fl_high "<suspension animation SMD>" frame 2 2 subtract neutral 0 weightlist wheel_fl 
$sequence wheel_fl_suspension { wheel_fl_low wheel_fl_high blend "vehicle_wheel_fl_height" 0 1.0 } weightlist wheel_fl delta autoplay

$weightlist wheel_fl_spin { "<wheel front left bone>" 1.0 } 
$animation sp_fl_1 "<wheelspin animation SMD>" frame 0 0 subtract neutral 0 weightlist wheel_fl_spin 
$animation sp_fl_2 "<wheelspin animation SMD>" frame 1 1 subtract neutral 0 weightlist wheel_fl_spin 
$animation sp_fl_3 "<wheelspin animation SMD>" frame 2 2 subtract neutral 0 weightlist wheel_fl_spin 
$animation sp_fl_4 "<wheelspin animation SMD>" frame 3 3 subtract neutral 0 weightlist wheel_fl_spin 
$animation sp_fl_5 "<wheelspin animation SMD>" frame 4 4 subtract neutral 0 weightlist wheel_fl_spin 
$animation sp_fl_6 "<wheelspin animation SMD>" frame 5 6 subtract neutral 0 weightlist wheel_fl_spin 
$sequence wheel_fl_spin { sp_fl_1 sp_fl_2 sp_fl_3 sp_fl_4 sp_fl_5 sp_fl_6 blendwidth 6 blend "vehicle_wheel_fl_spin" -180 180 } weightlist wheel_fl_spin delta autoplay



// front right
$weightlist wheel_fr { "<steering front right bone>" 1.0  "<suspension front right bone>" 1.0 "<wheel front right bone>" 0.0 }
$animation wheel_fr_low "<suspension animation SMD>" frame 0 0 subtract neutral 0 weightlist wheel_fr 
$animation wheel_fr_high "<suspension animation SMD>" frame 2 2 subtract neutral 0 weightlist wheel_fr 
$sequence wheel_fr_suspension { wheel_fr_low wheel_fr_high blend "vehicle_wheel_fr_height" 0 1.0 } weightlist wheel_fr delta autoplay

$weightlist wheel_fr_spin { "<wheel front right bone>" 1.0 } 
$animation sp_fr_1 "<wheelspin animation SMD>" frame 0 0 subtract neutral 0 weightlist wheel_fr_spin 
$animation sp_fr_2 "<wheelspin animation SMD>" frame 1 1 subtract neutral 0 weightlist wheel_fr_spin 
$animation sp_fr_3 "<wheelspin animation SMD>" frame 2 2 subtract neutral 0 weightlist wheel_fr_spin 
$animation sp_fr_4 "<wheelspin animation SMD>" frame 3 3 subtract neutral 0 weightlist wheel_fr_spin 
$animation sp_fr_5 "<wheelspin animation SMD>" frame 4 4 subtract neutral 0 weightlist wheel_fr_spin 
$animation sp_fr_6 "<wheelspin animation SMD>" frame 5 6 subtract neutral 0 weightlist wheel_fr_spin 
$sequence wheel_fr_spin { sp_fr_1 sp_fr_2 sp_fr_3 sp_fr_4 sp_fr_5 sp_fr_6 blendwidth 6 blend "vehicle_wheel_fr_spin" -180 180 } weightlist wheel_fr_spin delta autoplay


// rear left
$weightlist wheel_rl { "<suspension rear left bone>" 1.0 "<wheel rear left bone>" 0.0 }
$animation wheel_rl_low "<suspension animation SMD>" frame 0 0 subtract neutral 0 weightlist wheel_rl 
$animation wheel_rl_high "<suspension animation SMD>" frame 2 2 subtract neutral 0 weightlist wheel_rl 
$sequence wheel_rl_suspension { wheel_rl_low wheel_rl_high blend "vehicle_wheel_rl_height" 0 1.0 } weightlist wheel_rl delta autoplay

$weightlist wheel_rl_spin { "<wheel rear left bone>" 1.0 } 
$animation sp_rl_1 "<wheelspin animation SMD>" frame 0 0 subtract neutral 0 weightlist wheel_rl_spin 
$animation sp_rl_2 "<wheelspin animation SMD>" frame 1 1 subtract neutral 0 weightlist wheel_rl_spin 
$animation sp_rl_3 "<wheelspin animation SMD>" frame 2 2 subtract neutral 0 weightlist wheel_rl_spin 
$animation sp_rl_4 "<wheelspin animation SMD>" frame 3 3 subtract neutral 0 weightlist wheel_rl_spin 
$animation sp_rl_5 "<wheelspin animation SMD>" frame 4 4 subtract neutral 0 weightlist wheel_rl_spin 
$animation sp_rl_6 "<wheelspin animation SMD>" frame 5 6 subtract neutral 0 weightlist wheel_rl_spin 
$sequence wheel_rl_spin { sp_rl_1 sp_rl_2 sp_rl_3 sp_rl_4 sp_rl_5 sp_rl_6 blendwidth 6 blend "vehicle_wheel_rl_spin" -180 180 } weightlist wheel_rl_spin delta autoplay


// rear right
$weightlist wheel_rr { "<suspension rear right bone>" 1.0 "<wheel rear right bone>" 0.0 }
$animation wheel_rr_low "<suspension animation SMD>" frame 0 0 subtract neutral 0 weightlist wheel_rr 
$animation wheel_rr_high "<suspension animation SMD>" frame 2 2 subtract neutral 0 weightlist wheel_rr 
$sequence wheel_rr_suspension { wheel_rr_low wheel_rr_high blend "vehicle_wheel_rr_height" 0 1.0 } weightlist wheel_rr delta autoplay

$weightlist wheel_rr_spin { "<wheel rear right bone>" 1.0 }  
$animation sp_rr_1 "<wheelspin animation SMD>" frame 0 0 subtract neutral 0 weightlist wheel_rr_spin 
$animation sp_rr_2 "<wheelspin animation SMD>" frame 1 1 subtract neutral 0 weightlist wheel_rr_spin 
$animation sp_rr_3 "<wheelspin animation SMD>" frame 2 2 subtract neutral 0 weightlist wheel_rr_spin 
$animation sp_rr_4 "<wheelspin animation SMD>" frame 3 3 subtract neutral 0 weightlist wheel_rr_spin 
$animation sp_rr_5 "<wheelspin animation SMD>" frame 4 4 subtract neutral 0 weightlist wheel_rr_spin 
$animation sp_rr_6 "<wheelspin animation SMD>" frame 5 6 subtract neutral 0 weightlist wheel_rr_spin 
$sequence wheel_rr_spin { sp_rr_1 sp_rr_2 sp_rr_3 sp_rr_4 sp_rr_5 sp_rr_6 blendwidth 6 blend "vehicle_wheel_rr_spin" -180 180 } weightlist wheel_rr_spin delta autoplay

Miscellaneous

Model origin note

If your model is rotated by 90 to 180 degrees relative to the direction it actually drives, try using an $origin line like this:

$origin 0 0 0 90

Gauge animations

For either method, use this $poseparameter line:

(Note: "guage" isn't a typo, it's the actual necessary spelling)

$poseparameter "vehicle_guage" 0 1

You might need to give your animation a linear curve with either method.

Simple

For a simple animation with a single gauge needle, like in the Half-Life 2 buggy:

$weightlist speedo { "<gauge needle bone>" 1.0 }
$animation slow "<gauge animation SMD>" frame 0 0 subtract neutral 0 weightlist speedo
$animation mid "<gauge animation SMD>" frame 1 1 subtract neutral 0 weightlist speedo
$animation fast "<gauge animation SMD>" frame 2 2 subtract neutral 0 weightlist speedo
$sequence speedometer { slow mid fast blend vehicle_guage 0 1 } weightlist speedo delta autoplay

Advanced

For an animation with two gauge needles moving independently, like in a regular car's gauge cluster:

$weightlist speedo { "<gauge needle speedo bone>" 1.0 "<gauge needle tacho bone>" 1.0 }
$animation speedo1 "<gauge animation SMD>" frame 0 0 subtract neutral 0 weightlist speedo
$animation speedo2 "<gauge animation SMD>" frame 1 1 subtract neutral 0 weightlist speedo
$animation speedo3 "<gauge animation SMD>" frame 2 2 subtract neutral 0 weightlist speedo
$animation speedo4 "<gauge animation SMD>" frame 3 3 subtract neutral 0 weightlist speedo
$animation speedo5 "<gauge animation SMD>" frame 4 4 subtract neutral 0 weightlist speedo
$animation speedo6 "<gauge animation SMD>" frame 5 5 subtract neutral 0 weightlist speedo
$animation speedo7 "<gauge animation SMD>" frame 6 6 subtract neutral 0 weightlist speedo
$animation speedo8 "<gauge animation SMD>" frame 7 7 subtract neutral 0 weightlist speedo
$animation speedo9 "<gauge animation SMD>" frame 8 8 subtract neutral 0 weightlist speedo
$animation speedo10 "<gauge animation SMD>" frame 9 9 subtract neutral 0 weightlist speedo
$animation speedo11 "<gauge animation SMD>" frame 10 10 subtract neutral 0 weightlist speedo
$animation speedo12 "<gauge animation SMD>" frame 11 11 subtract neutral 0 weightlist speedo
$animation speedo13 "<gauge animation SMD>" frame 12 12 subtract neutral 0 weightlist speedo
$animation speedo14 "<gauge animation SMD>" frame 13 13 subtract neutral 0 weightlist speedo
$animation speedo15 "<gauge animation SMD>" frame 14 14 subtract neutral 0 weightlist speedo
$animation speedo16 "<gauge animation SMD>" frame 15 15 subtract neutral 0 weightlist speedo
$animation speedo17 "<gauge animation SMD>" frame 16 16 subtract neutral 0 weightlist speedo
$animation speedo18 "<gauge animation SMD>" frame 17 17 subtract neutral 0 weightlist speedo
$animation speedo19 "<gauge animation SMD>" frame 18 18 subtract neutral 0 weightlist speedo
$animation speedo20 "<gauge animation SMD>" frame 19 19 subtract neutral 0 weightlist speedo
$sequence speedometer { speedo1 speedo2 speedo3 speedo4 speedo5 speedo6 speedo7 speedo8 speedo9 speedo10 speedo11 speedo12 speedo13 speedo14 speedo15 speedo16 speedo17 speedo18 speedo19 speedo20 blend vehicle_guage 0 1 } blendwidth 20 weightlist speedo delta autoplay

In this example, 20 frames were animated, but you can use as many as you want, as long as you change the code to account for differences in frame amounts.

Making the steering wheel turn further than 180 degrees

Because of how animation SMDs work, if a bone rotates over 180 degrees over one frame, its rotation direction will appear to be the opposite of what you might've intended. To avoid this, you can animate in a way so that there's no more than 180 degrees of rotation in individual frames.

Make sure your animation has a linear curve.

This setup allows for about 2.5 proper revolutions of the steering wheel, when turning one way:

$weightlist front_wheels { "<steering front left bone>" 1.0 "<steering front right bone>" 1.0 "<steering wheel bone>" 1.0 }
$animation steer1 "<steering animation SMD>" frame 0 0 subtract neutral 0 weightlist front_wheels
$animation steer2 "<steering animation SMD>" frame 1 1 subtract neutral 0 weightlist front_wheels
$animation steer3 "<steering animation SMD>" frame 2 2 subtract neutral 0 weightlist front_wheels
$animation steer4 "<steering animation SMD>" frame 3 3 subtract neutral 0 weightlist front_wheels
$animation steer5 "<steering animation SMD>" frame 4 4 subtract neutral 0 weightlist front_wheels
$animation steer6 "<steering animation SMD>" frame 5 5 subtract neutral 0 weightlist front_wheels // center frame
$animation steer7 "<steering animation SMD>" frame 6 6 subtract neutral 0 weightlist front_wheels
$animation steer8 "<steering animation SMD>" frame 7 7 subtract neutral 0 weightlist front_wheels
$animation steer9 "<steering animation SMD>" frame 8 8 subtract neutral 0 weightlist front_wheels
$animation steer10 "<steering animation SMD>" frame 9 9 subtract neutral 0 weightlist front_wheels
$animation steer11 "<steering animation SMD>" frame 10 10 subtract neutral 0 weightlist front_wheels
$sequence turning { steer1 steer2 steer3 steer4 steer5 steer6 steer7 steer8 steer9 steer10 steer11 blend vehicle_steer -1 1 } blendwidth 11 weightlist front_wheels delta autoplay

When creating your own animation for such a setup, make sure to have an even amount of frames for both turning directions, with a center frame in the middle.

As with the above advanced gauge setup, it's possible to have more or less than 10 frames.

See also