decentraland / decentraland/godot-explorer
Locomotion Iteration #1: Jump Arc, Coyote Time & Gravity v Unity
- Dominant language
- Rust
- Stars
- 18
- Forks
- 19
- Avg merge
- 4d 2h
- Merged PRs (30d)
- 43
Description
## **⚠️ Problem:**
Coyote time is unreachable in Godot — the ground-jump branch sits after `elif not on_floor`, so a jump pressed within 0.15 s of walking off a ledge is silently dropped, and off higher ledges the same press is instead consumed by the glide-open gate. Muscle memory for every platformer player, broken on mobile only. Drives **Playtime**.
## **🏁 Scope:**
Coyote reachability, gravity model, long-jump hold and jump height in `player.gd`. Air jump delay and jump cooldown already match Unity exactly — regression-guard, do not retune. Jump and landing animation is Iteration 3.
## **📝 Deliverables:**
- [ ] Coyote branch reachable — restructure the `elif` chain so a ground jump fires within 0.15 s of leaving the floor (B1)
- [ ] Glide-open gate no longer consumes a jump press inside the coyote window
- [ ] Asymmetric gravity — ascent ×4 = 40, descent 10
- [ ] Long jump: 0.5 s hold window applying ×0.5 gravity
- [ ] Jump height 1.8 fixed → 1.0 jog / 1.5 run, lerped by horizontal speed
- [ ] Air jump impulse `max(8, current_speed)` replaces the fixed 8.0
- [ ] Coyote window expressed in seconds, never tick counts
- [ ] Regression guard: jump buffer 0.15 s, jump cooldown 0.3 s, air jump delay 0.2 s unchanged
- [ ] Tap-vs-hold jump arc captured side-by-side vs desktop, test scene `-98,103`
## Metrics
Playtime. No instrumented baseline for dropped jump inputs — measure via the #905 reproduction checklist (B1: walk off a low ledge, jump within 0.15 s; Unity jumps, Godot drops the input).
## Findings
| | Godot `main` | Unity |
|---|---|---|
| Coyote time | **unreachable** — grace (`player.gd:391-395`) only drives the land animation; ground-jump branch (`:455-470`) sits after `elif not on_floor` (`:390`) | 0.15 s (`ApplyJump.cs:54`) |
| Jump buffer | 0.15 s (`player.gd:9`) | 0.15 s (`JumpTrigger.cs:8-9`) ✓ |
| Gravity | single 10 m/s² | asymmetric — ascent ×4 = 40, descent 10 (`ApplyGravity.cs:41-42`) |
| Long jump (hold) | none | 0.5 s window, ×0.5 gravity (`ApplyGravity.cs:37-38`) |
| Jump height | 1.8 fixed (`player.gd:59-60`) | 1.0 jog / 1.5 run, lerped by horizontal speed |
| Air jump | 1 · 2.0 m · 0.2 s delay · fixed impulse 8.0 (`player.gd:8,11-13`) | same values, impulse `max(8, current speed)` (`ApplyJump.cs:76-186`), behind `FeatureId.DoubleJump` |
| Jump cooldown | 0.3 s (`player.gd:10`) | 0.3 s (`ApplyJump.cs:80-83`) ✓ |
Symmetric single gravity cannot reproduce Unity's arc no matter how the height constant is tuned. Snappy going up + forgiving coming down comes from the ×4 ascent multiplier plus the hold window, not from the height value.
Tick reasoning does not transfer. Unity steps physics by frame delta — `Physics.Simulate(dt)`, `UpdatePhysicsSimulationSystem.cs:33` — not a fixed 50 Hz. "Coyote = 8 ticks" is nominal; port 0.15 **seconds**.
D2 resolved: 1.0 / 1.5 speed-lerped, matching Unity. Reachable-geometry set changes with it — 1.5 m @ 10 m/s replaces today's 1.8 m @ 11 m/s.
## **🔗 References:**
- [`ApplyJump.cs`](https://github.com/decentraland/unity-explorer/blob/dev/Explorer/Assets/DCL/Character/CharacterMotion/Velocity/ApplyJump.cs) — coyote window, cooldown, speed-lerped height, air-jump impulse
- [`ApplyGravity.cs`](https://github.com/decentraland/unity-explorer/blob/dev/Explorer/Assets/DCL/Character/CharacterMotion/Velocity/ApplyGravity.cs) — `JumpGravityFactor` ascent multiplier + `LongJumpTime` / `LongJumpGravityScale` hold window
- [#905](https://github.com/decentraland/godot-explorer/issues/905) — parent audit, §3.2
- [#1417](https://github.com/decentraland/godot-explorer/pull/1417) — closed unmerged; carried a coyote fix bundled with Jolt. Salvage the GDScript half.
- [#1553](https://github.com/decentraland/godot-explorer/issues/1553) — jump animation; changing the arc re-times the `rise` / `fall` / `land` thresholds
Contributor guide
Assessment
This issue has not been assessed yet.