Animating engines
This page explains how to set up an engine model in Blender so it can gimbal in the game, and optionally extend and retract its nozzle like the Descant does.
If you want to look at a finished setup, the Descant (sm+_extending_bell_rigging.blend) has a gimbal and an extending nozzle, and the Bobcat has two gimbaling nozzles with yokes and flexible feed pipes. Both are in the asset sources SVN under Parts/Vessel/Engines/Methalox.
How it works
The game moves engines by rotating and moving objects, not by deforming meshes. So everything that moves needs to sit under an empty that the game can turn or slide:
One empty at the gimbal pivot. The game rotates it to steer the engine.
If the nozzle extends, a second empty under the first one. Its animation slides the nozzle out and back in.
Everything else stays where it is.
Here's how the parts are parented on a typical engine. Each box sits inside the one it's parented to. The dashed box is something you add in Unity, not in Blender.
flowchart TB
subgraph fixed["Fixed parts"]
direction TB
mount["Mount and struts"]
pumps["Turbopumps"]
cylinders["Piston cylinders"]
yoke["Yoke<br/>(follows one tilt axis)"]
fixedbone["Fixed bone"]
pipe["Flexible pipe<br/>(skinned)"]
subgraph gimbal["Gimbal empty: tilts to steer"]
direction TB
chamber["Chamber and bell"]
rods["Piston rods"]
pivotbone["Pivot bone"]
subgraph sliding["Sliding empty: moves the nozzle out and in"]
direction TB
nozzle["Extending nozzle"]
thrust["Thrust point and plume<br/>(set up in Unity)"]
end
end
end
fixedbone -. weights .- pipe
pipe -. weights .- pivotbone
cylinders <-. point at each other .-> rods
classDef unity stroke-dasharray: 5 5
class thrust unitySetting up the gimbal
Add an empty at the point the combustion chamber swings around. This is usually somewhere around the injector or the throat. Don't rotate the empty. Its Z axis should point along the engine, so that X and Y are the two directions it can tilt in.
Parent everything that should move with the nozzle to this empty: the chamber, the bell, and the moving ends of any pistons. Anything that's bolted to the rest of the rocket, like the mount, struts and turbopumps, should stay outside of it.
The empty's name has to match what the part's Module_Gimbal is set to look for. On the Descant it's bone_gimbal. If your engine has more than one nozzle, Blender won't let you give them the same name, so call them something like gimbal_l and gimbal_r. They get renamed when the prefab is built.
If the engine has a yoke (a frame around the chamber that only tilts around one axis), line its hinge up with the empty's X axis. The game tilts around X first and then around Y, so X is the outer axis.
When you're done, rotate the empty by the gimbal range (4 to 5 degrees on our engines) in each direction and check that nothing clips or comes apart.
Setting up an extending nozzle
Add a second empty at the same spot as the gimbal empty, and parent it to the gimbal empty. On the Descant it's called bone_hood. Parent the part of the nozzle that slides out to this second empty. Because it's under the gimbal empty, the extended nozzle still tilts along with the rest of the engine.
Model the sliding part in its fully extended position. That way the empty sits at 0 when the nozzle is extended, and the animation just moves it up to retract it.
Animate only the empty's Z location, all in one action:
Frame 1: retracted
Frame 60: extended
Frame 121: retracted again (the same motion as frames 1 to 60, played backwards)
Set the scene's frame range to 1–121, and leave the timeline on frame 1 when you save and export, so the model comes into Unity retracted. Unity splits that animation into four clips: closed (1–3), engage (1–60), opened (60–62) and disengage (62–121).
Pistons, yokes and pipes
Anything that connects a fixed part to a moving part needs a bit of extra setup, otherwise it pulls apart when the engine gimbals.
Pistons need to be split into two objects, the cylinder and the rod. Put each one's origin at the point it pivots around. The cylinder stays with the fixed parts, and the rod goes under the gimbal empty. In Unity, each half gets a constraint that keeps it pointed at the other one.
Yokes go outside the gimbal empty, because they only follow one of the two tilt directions. Unity handles that with a constraint too.
Flexible pipes and hoses have to be skinned, since one end stays put while the other moves with the chamber. Make the pipe its own mesh and weight it to two bones: one that stays fixed, and one at the gimbal pivot. The fixed end should be fully weighted to the fixed bone and the chamber end fully weighted to the pivot bone, with a blend in between. Make sure the pipe has some edge loops where it needs to bend. A long straight segment with no loops in it will just stretch.
Be careful about what you count as fixed. On the Bobcat, the turbopumps and the tops of the pipes going into the mount have to stay fixed, and only the pipes going into the combustion chambers bend. If something that's attached to the mount ends up under the gimbal empty, it comes off the mount as soon as the engine moves.
You can add constraints in Blender (Damped Track, Copy Rotation) to check that the rig moves properly, but they won't be exported. If you do this for pistons, don't make the two halves track each other directly. Blender can't evaluate that properly, and one of them ends up pointing the wrong way. Have the cylinder track a separate helper empty or bone at the rod's pivot instead.
Exporting
Set up the FBX export as a collection exporter on the engine's collection (Collection Properties → Exporters → FBX). That way the export settings are saved in the blend file, and anyone can re-export it with one click and get the same result.
These are the settings to use:
Setting | Value |
|---|---|
File path |
|
Object Types | Empty, Mesh, Armature |
Apply Scalings | All Local |
Apply Unit | On |
Forward / Up | -Z / Y |
Smoothing | Normals Only |
Apply Modifiers | Off, unless a modifier like Mirror is part of the shape |
Tangent Space | Off |
Primary / Secondary Bone Axis | Y / X |
Add Leaf Bones | Off |
Bake Animation | On if the nozzle extends, off otherwise |
Key All Bones | On |
NLA Strips, All Actions, Force Start/End Keying | Off |
Sampling Rate / Simplify | 1 / 0 |
Path Mode | Auto |
Embed Textures | Off |
A few things to watch out for:
Unity finds objects by name, so don't rename objects or meshes after the prefab has been set up. It's easiest if each mesh has the same name as its object.
If you turn on Apply Modifiers, viewport-only modifiers get exported too. The UniV checker modifier, for example, adds its checker material to the mesh.
Save the file in Blender 5.2, which is what the rest of the team uses. Opening and saving a 5.2 file in an older version can lose data.
What happens in Unity
This part is for whoever builds the prefab. Most of it is stuff an FBX can't store.
Set the model's Rig to Generic, and set up the four animation clips from the frame ranges above, with looping off.
Add an Animator to the model root. The easiest way is to copy the Descant's animator controller, which works the same way as the stock Cornet and Tuba ones.
On Module_Engine, turn on RunAnimationOnActivateDeactivate and set ActivateEngineWaitTime to about the length of the extend animation (2.3 seconds on the Descant).
On Module_Gimbal, set gimbalTransformName to the gimbal empty's name and turn on forceProceduralGimbaling.
Put the thrustTransform at the middle of the nozzle exit, with its Z axis pointing in the direction of the exhaust. If the nozzle extends, it goes under the sliding empty, otherwise under the gimbal. The plume and other engine effects go in the same place.
Add the aim constraints for the pistons and yokes, and parent the pipe's pivot bone under the gimbal.
Split the colliders the same way as the model: the mount on the part itself, the chamber under the gimbal, and the extending part under the sliding empty. The bottom attach node goes at the bottom of the retracted nozzle.
Click Re-export JSON in the part's inspector, then Bake Icon. Don't edit the part JSON by hand. If something in it looks wrong, fix it on the prefab and export again.
Before you hand it over
Gimbal the engine all the way in every direction, with the nozzle both retracted and extended. Nothing should clip or pull away from the mount.
Frame 1 shows the nozzle retracted, and the frame range is 1–121.
With everything at rest, the model looks exactly like it did before you rigged it.
Every moving part has its origin where it pivots, and you're happy with the object and mesh names.
You exported the FBX with the collection exporter, and you're committing the blend and the FBX together.