The character leaves the ground half a frame before the jump button is pressed — and the player feels it as 'mushy' controls. This is where our work begins: we don't pick Rigidbody.mass randomly but formalize the feel through specific numbers and architectural decisions. With over 10 years in game dev, we've implemented movement systems in more than 50 projects — from mobile platformers to console shooters. Contact us — we'll set up responsive controls in 2–5 days.
Why CharacterController Fails in Platformers
Unity offers two paths: the physics-based Rigidbody + collider and the kinematic CharacterController. In platformers, Rigidbody is often the better choice: it provides accurate physics on moving platforms, while CharacterController requires manual handling. With Rigidbody, you get twice fewer bugs with moving objects.
CharacterController.Move() does not interact with the physics engine directly — the character clips through moving platforms unless you implement a custom riding mechanism. The standard isGrounded flag returns false on one frame when descending a slope — causing the character to 'bounce' endlessly due to accumulated gravity in the vertical velocity buffer.
A Rigidbody character is more robust to physics interactions but requires manual friction control: without a PhysicMaterial with zero dynamicFriction on the capsule, the character gets stuck on geometry corners. Additionally, Rigidbody.AddForce() in ForceMode.VelocityChange behaves predictably only with fixedDeltaTime 0.02. If you change the physics step, all tuned feel 'drifts'. We have a ready set of configurations for different genres that guarantees stable behavior at any fixed step.
How We Build Movement System Architecture
A well-designed movement system separates three areas of responsibility: input, state, and physics/movement. Input is read in Update() and written to a MovementInput struct. States (Idle, Running, Jumping, Falling, Crouching, WallRunning) are managed by a state machine — usually a custom class on top of MonoBehaviour, not an Animator State Machine, because transition logic is often nonlinear and tied to game conditions. The actual position displacement happens in FixedUpdate() via Rigidbody.MovePosition() or directly through velocity.
For platformers with air control, an airControlCurve is crucial: horizontal acceleration in the air should be less than on the ground, but not zero. Implemented via an AnimationCurve in a ScriptableObject character settings — designers tweak the curve in the inspector without touching code. We guarantee that your designers can adjust parameters without our involvement.
How to Implement Air Control?
Coyote time and jump buffering are mandatory components for responsive controls. Coyote time allows jumping within coyoteTimeDuration (typically 0.1–0.15 s) after leaving a platform. Jump buffering stores the jump press for jumpBufferDuration (0.1–0.2 s) and executes it at the first opportunity. Without these two mechanics, players consistently 'miss' jumps at platform edges. Variable jump height is another point: if the jump button is released mid-ascent, the vertical velocity is cut to minJumpVelocity. This is implemented via a simple check in Update().
| Parameter | Recommended Value | Description |
|---|---|---|
| Coyote time | 0.1–0.15 s | Buffer for jumping after leaving a platform |
| Jump buffer | 0.1–0.2 s | Buffer for jump input before execution |
| Air control ratio | 0.3–0.5 | Ratio of horizontal acceleration in air to ground |
| Min jump velocity | 2–4 units/s | Vertical velocity when releasing jump |
What's Included in Movement System Work
- Analysis of GDD and references — we extract concrete numbers (run speed in units/s, jump height, dash time).
- Design of MovementSettings ScriptableObject — all parameters in one place, accessible to designers.
- Prototype on primitives — only colliders and logic for quick feel tuning (1–2 days).
- Integration of Animator Controller — connecting Blend Tree for locomotion and final colliders.
- Edge case testing — jump into a 1-unit gap, moving platforms with rotation, transition between NavMesh surfaces of different scenes.
- Documentation and training — handover with comments and settings, demo for the team.
Indicative Timeline Table
| Scale | Description | Timeline |
|---|---|---|
| Basic | 2D/3D character, ground, jump, coyote time | 2–5 days |
| Medium | + double jump, dash, wall jump, crouch | 1–2 weeks |
| Advanced | + swimming, climbing, ragdoll transition, moving platforms | 2–4 weeks |
| Full system | + procedural footstep IK, lean, network replication | 4–8 weeks |
Project Work Process
We start by analyzing your GDD and reference games — we write out concrete numbers from mechanics. Then we design a MovementSettings ScriptableObject with all parameters and a PlayerMovement MonoBehaviour with documented areas of responsibility.
The prototype is built on primitives without animations — only colliders and logic. This allows nailing down the feel in a day or two without spending time on Animator integration. After the feel is approved, we connect the Animator Controller with a Blend Tree for locomotion and final colliders based on mesh geometry.
Testing includes edge cases: jump into a 1-unit gap, moving platforms with rotation, transition between NavMesh surfaces of different scenes under additive loading. These situations most often reveal issues with ground detection and velocity accumulation.
Checklist of common mistakes in movement design
- Missing coyote time — player cannot jump immediately after leaving a platform.
- No jump buffering — inputs are 'eaten' if the character is not on the ground.
- Using isGrounded without SphereCast — false on slopes, infinite bouncing.
- Manual rotation with NavMeshAgent.updateRotation — rotation jitter.
- Fixed fixedDeltaTime for all platforms — on Android at 30 fps physics 'drifts'.
- No LayerMask on ground detector — SphereCast hits the character's collider.
Our solution saves up to 40% of debugging time, reducing project budget. Order a movement system development — we'll select the architecture for your project and budget. We'll estimate timelines based on your GDD within 1 business day.





