decentraland / decentraland/godot-explorer
Locomotion Iteration #1: Avatar Capsule, Step Offset & Ground Contact v Unity
- Dominant language
- Rust
- Stars
- 18
- Forks
- 19
- Avg merge
- 4d 2h
- Merged PRs (30d)
- 43
Description
## **⚠️ Problem:**
The player capsule does not reach the ground — it spans y 0.45 → 1.95 while the avatar's feet are at 0, so the volume that collides with scene geometry sits half a metre above the character and is narrower and shorter than the Unity reference. Wrong hitbox on every collision the player has. Drives **Playtime**.
## **🏁 Scope:**
Player collider shape and dimensions, plus the ground-contact set that has to land with it: step offset, floor snap / downslope stick, slope limit (audit §2 + §3.1). Jump and gravity are #1557, speed and acceleration are #2850, scene-collider generation is Iteration 2.
## **📝 Deliverables:**
- [ ] Player capsule height 1.6, radius 0.3, margin 0.08, center Y 0.8 — span 0.0 → 1.6
- [ ] `SeparationRayShape3D` re-evaluated against the new capsule position
- [ ] Duplicate capsules in `stuck_detector` and `camera_mode_area_detector` kept consistent
- [ ] Custom step offset 0.35 implemented — `CharacterBody3D` has no built-in (M1)
- [ ] Floor snap replaced by downslope raycast 0.45 jog / 0.55 run
- [ ] Slope limit set explicitly to 46°, not left at the engine default 45°
- [ ] Before/after climb + clearance comparison recorded in the test scene
- [ ] 0.35–0.5 m ledges walked up, not jumped — verified vs desktop
## Metrics
Playtime. No baseline for mobile climb-failure rate exists — needs one before/after. Measure via the #905 reproduction checklist (M1: walk into 0.35–0.5 m ledges; Unity steps up, Godot blocks). Measurement approach TBD with Data.
## Findings
| | Godot `main` | Unity |
|---|---|---|
| Height | 1.5 | 1.6 |
| Radius | 0.25 | 0.3 |
| Center Y | 1.2 | 0.8 |
| Vertical span | 0.45 → 1.95 | 0.0 → 1.6 |
| Margin vs skin width | 0.05 | 0.08 |
| Step offset | none — no built-in, no custom logic | 0.35, force-written every physics tick (`ApplySlopeModifier.cs:24`); prefab serializes 0.3 |
| Floor snap / downslope stick | `floor_snap_length = 0.2` (`player.gd:218`) | downslope raycast 0.45 jog / 0.55 run (`ApplySlopeModifier.cs:40,42`) |
| Slope limit | unset → engine default 45° | 46° (`CharacterObject.prefab:103`) |
Godot: `CharacterBody3D`, `collision_layer` 4 (`CL_PLAYER`), `collision_mask` 2 (`CL_PHYSICS`).
Unity: `CharacterController`, layer 16, collides with Default, Floor, InvisibleColliders, CharacterOnly, SDKAvatarTriggerArea, SDKEntityTriggerArea.
Constraints the fix has to account for:
- Godot carries a second shape Unity has no equivalent for — `SeparationRayShape3D`, length 1.0779, at y=1.1, basis rotates local +Z to world −Y, so it points down and ends at y≈0.022. It exists to bridge the 0.45m gap under the capsule. Its geometry is tuned to center Y = 1.2 and does not survive a change to that value untouched.
- That same 0.45m gap is currently the only thing letting the player clear low geometry: `CharacterBody3D` has no built-in step offset and the repo adds none, so sub-0.45m obstacles never touch the capsule. **Godot's de-facto "step clearance" is not step logic — it is the floating capsule rolling over low geometry.** Moving the capsule down removes that clearance, so step offset 0.35 has to ship in the same PR or climbing gets worse, not better.
- Two more capsules share the current dimensions at offset y=1.153: `stuck_detector.tscn` (Area3D, mask `4294967295`) and `camera_mode_area_detector.tscn`.
- PR #943 resized the capsule to 1.6 / 0.3 / margin 0.08 but left center Y at 1.2; PR #1417 did the same. Both closed unmerged 2026-04-19.
Prefab values are not the source of truth: `CharacterObject.prefab` serializes `StepOffset` 0.3, but `ApplySlopeModifier.cs:24` overwrites it with 0.35 every physics tick. Port 0.35. D3 (snap 0.2 vs raycast 0.45/0.55) and D4 (45° vs 46°) resolved above in favour of Unity.
## **🔗 References:**
- [decentraland/unity-explorer — `CharacterObject.prefab`](https://github.com/decentraland/unity-explorer/blob/dev/Explorer/Assets/DCL/Character/CharacterObject/CharacterObject.prefab) — source of the Unity column: `m_Height` 1.6, `m_Radius` 0.3, `m_Center` (0, 0.8, 0), `m_SkinWidth` 0.08, slope limit 46°
- [`ApplySlopeModifier.cs`](https://github.com/decentraland/unity-explorer/blob/dev/Explorer/Assets/DCL/Character/CharacterMotion/Velocity/ApplySlopeModifier.cs) — the live step offset 0.35 and the downslope raycast lengths
- [`godot/src/logic/player/player.tscn`](https://github.com/decentraland/godot-explorer/blob/main/godot/src/logic/player/player.tscn) — the two collision shapes to change
- [#905](https://github.com/decentraland/godot-explorer/issues/905) — parent audit, §2 + §3.1
- [#943](https://github.com/decentraland/godot-explorer/pull/943), [#1417](https://github.com/decentraland/godot-explorer/pull/1417) — closed unmerged; both attempted this resize, neither moved center Y
Contributor guide
Assessment
This issue has not been assessed yet.