Hanging heads on NPC bodies with a bone attachment in Godot
Three test subjects in the Breakout Site moved off the astronaut stand-in onto a headless human frame with separate heads that animate in step.
Three people in the Breakout Site are on bodies built for this game now, and not the astronaut stand-in. I installed a headless human body and three heads as trials. Subject 29 stands and idles in his cell. Subjects 26 and 27 are dead on the floor, and they can still be searched like any body.
They’re bare and bald, and a row of them looks like shop dummies. I’ll get to what’s wrong at the end.
Why only the subjects?
I’d already said the tints on the astronaut bodies were fine for now, and that I was going to replace those aliens with real bodies eventually. The plan was to move whichever people could go through the whole model pipeline, and to stop and write down exactly what the kits lacked for the rest. I didn’t want a one-off workaround.
Before starting I checked what was installed. There was no body frame, no head and no armor piece. The armor study’s pieces had all failed the pipeline’s gate, there was no lab coat, and the game had no code to put a head on a body. So the scientists and staff stayed on the stand-in, and the subjects went through bare.
The human frame’s face table gave me two more heads besides the first. The body is 2,238 triangles and the heads are 1,258, 1,296 and 1,237. All four were built from scratch, built twice to the same bytes, checked on review sheets, and then installed through the gate.
Naming a body and a head in an NPC’s file
An NPC’s JSON file picks its body and head with one field:
"figure": "human/02"
That’s "<race>/<face>", and the face is 01 if it’s left off. A table maps a race to its body’s path and a pattern for its heads’ paths. Subject 29 has head 02, Subject 26 has head 03 and Subject 27 has head 01.
A new script, NpcFigure, builds the figure from the two files. NpcFigure.make(name, height, dead) returns null if the models aren’t installed, and then the NPC code falls back to the old model. So an NPC file can name a figure before its models exist.
How is the head attached?
NpcFigure is a Node3D, and it builds the figure in four steps:
- It instances the body’s scene and scales it by the NPC’s height divided by the body manifest’s
height_with_head. - It adds a
BoneAttachment3DnamedSocket_neckto the body’s skeleton, on the head bone the manifest names, at that bone’s global pose. - It puts the head’s scene under the attachment at the offset the manifest gives for the neck.
- It scales the head by one number, which is the neck’s size in the body’s manifest divided by the neck size the head was drawn at.
The head has its own skeleton and its own AnimationPlayer, and its clips have the same names as the body’s: AN_frame_idle, AN_frame_walk and AN_frame_death. play() and hold() on the figure pass every call to both players, so the jaw moves in step with the body.
A figure is scenery. It has no collision.
Standing and lying down
A standing figure plays idle when it enters the tree. It has to wait until then, because a player poses nothing before that.
A dead figure holds the death clip at its last frame. The figure is shifted along z by the manifest’s fallen_back value times the scale. That puts the middle of the fallen body over the node’s origin, the same as a monster’s corpse.
In the NPC script, a figure replaces the model when it’s installed, and the model’s yaw offset is zeroed because a figure faces +Z. A dead NPC with a figure is added lying down and tinted with the same tint call the stand-ins use.
The test for this has 59 checks and they all pass when it’s run alone.
What’s wrong with them?
- I can tell the three faces apart at 2 m, but not at 5 or 12 m.
- Every subject is bald and bare. Nothing can dress a person yet, and that’s also why the scientists and staff are still on the stand-in.
- The body tint multiplies the skin’s texture. The dead assayist’s violet tint was violet on the white astronaut, but on one of these bodies it comes out dark brown. It has to be laid over the texture and not multiplied into it before I move the assayist over.