How to use a rigged GLB character in Godot 4
Import a rigged, animated .glb into Godot 4, loop its clips, play them from code and walk it around with a CharacterBody3D.
You have a rigged character as one .glb file, and you want it walking around your Godot scene. This guide takes you from the file to a character you steer with the arrow keys. We tested every step in Godot 4.7.2 with a character from Hatchery3D, but they work for any glTF character with a skeleton and named clips.
What's in the file
A Hatchery3D character is a single glTF 2.0 binary file. Godot reads it natively, with no plugin. Inside it you get:
- A textured mesh, already at its real height in metres, with its feet on the floor at the origin.
- A humanoid skeleton of 77 bones, with
Hipsat the root. - Nine animation clips:
idle,walk,run,jump,punch,slash,hit,deathandwave. - Two empty sockets,
HandSocket_LandHandSocket_R, in the palms, for the things your character holds.
Import it
Drag the .glb into the FileSystem dock. Godot imports it as a scene. Open it, and you see this tree:
character (Node3D)
├── Rig (Node3D)
│ └── Skeleton3D
│ ├── LeftHand (BoneAttachment3D)
│ │ └── HandSocket_L
│ ├── RightHand (BoneAttachment3D)
│ │ └── HandSocket_R
│ └── RETOPO (MeshInstance3D)
└── AnimationPlayer
Every clip is an animation in the AnimationPlayer, under its own name. The hand sockets become BoneAttachment3D nodes, so anything you put under them follows the hand.
Loop idle, walk and run
Play walk now and it stops after one step. The file marks idle, walk and run as loops, but Godot's importer doesn't read that mark, so every clip comes in set to play once. Fix it once, in the import settings:
- Double-click the
.glbin the FileSystem dock. The Advanced Import Settings window opens. - In the tree on the left, open the
AnimationPlayerand selectidle. - On the right, set Loop Mode to Linear.
- Do the same for
walkandrun. - Click Reimport.
The setting lives in the .import file next to the .glb, so it survives when you replace the file with a new take of the same name. Leave jump, punch, slash, hit, death and wave on None: they play once and stop.
Play a clip
Make a new 3D scene and drag the .glb from the FileSystem dock into it. Rename the new node to character, so the paths below match. Then play a clip from any script:
$character/AnimationPlayer.play("wave")
To switch between clips without a pop, pass a blend time in seconds as the second argument: play("walk", 0.2) fades from the current clip into walk over a fifth of a second.
Walk it around
The walk and run clips play on the spot, like a treadmill. That's what you want in a game: your code moves the body, and the clip moves the legs. Here's a minimal player.
- Make a new scene with a
CharacterBody3Das its root, and call itPlayer. - Drag the character's
.glbunder it, and rename the node tocharacter. - Add a
CollisionShape3DunderPlayer. Give it aCapsuleShape3Dwith the character's height (1.8 for a 1.8 m character) and a radius of about 0.3. Move it up by half the height, so it stands on the floor like the mesh. - Attach this script to
Player:
extends CharacterBody3D
# The speeds at which the feet don't slide, in metres per second.
const WALK_SPEED := 1.3
const RUN_SPEED := 2.6
@onready var model: Node3D = $character
@onready var animation: AnimationPlayer = $character/AnimationPlayer
func _physics_process(delta: float) -> void:
if not is_on_floor():
velocity += get_gravity() * delta
var input := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
var direction := Vector3(input.x, 0.0, input.y)
var running := Input.is_key_pressed(KEY_SHIFT)
var speed := RUN_SPEED if running else WALK_SPEED
velocity.x = direction.x * speed
velocity.z = direction.z * speed
move_and_slide()
if direction.length() > 0.1:
# The model faces +Z, so turn it toward the direction of travel.
model.rotation.y = atan2(direction.x, direction.z)
animation.play("run" if running else "walk", 0.2)
else:
animation.play("idle", 0.2)
Put the Player in a level with a floor, press F6, and steer with the arrow keys. Hold Shift to run.
Match the speed to the feet
If the feet slide, the body moves faster or slower than the legs. Each Hatchery3D character comes with its own measured speeds: the file stores them on the walk and run clips as speed_mps, in the glTF extras. Godot doesn't import extras on animations, so copy the numbers into the two constants. For a 1.8 m character, they are about 1.3 m/s for the walk and 2.6 m/s for the run. A shorter character takes shorter steps, so its numbers are lower.
Put a sword in its hand
Characters come with empty hands, because a weapon rigged into the hand swings through the body. Make the sword as a separate prop instead, and parent it to a socket:
- Open the character's scene with New Inherited Scene (right-click the
.glb), or work in the scene where you placed it. In a placed instance, right-click it and turn on Editable Children first. - Drag the sword's
.glbunderSkeleton3D/RightHand/HandSocket_R. - Rotate and move the sword until the grip sits in the palm.
A Hatchery3D prop comes with a collision box. In a hand, that box pushes against the character's own body, so delete the prop's prop_collision node when you use it as a held item.
Props: colliders and LODs
Props import the same way. Their pivot sits at the base, so a prop stands on the floor where you drop it. The file carries a box collider that Godot turns into a StaticBody3D with a CollisionShape3D, so walls, crates and barrels block the player right away.
The file also holds two lower levels of detail. Godot skips those and builds its own LODs for every imported mesh, so you don't need to do anything: distant props draw fewer triangles automatically.
When something looks wrong
| You see | The cause | The fix |
|---|---|---|
| The walk stops after one step | The loop mode is None | Set Loop Mode to Linear in the import settings, then reimport |
| The character walks backwards | It faces +Z and your code expects −Z | Turn the model with atan2(direction.x, direction.z), as in the script |
| The feet slide | The body speed doesn't match the clip | Use the clip's speed_mps for the constants |
| A held prop shoves the character | The prop's collider touches the body | Delete the prop's prop_collision node |
For bigger setups, put the clips in an AnimationTree with a blend space for idle, walk and run. The clips and their names stay the same.