Skip to main content
Game Physics – Complete Beginner to Advanced Guide
CHAPTER 07 Intermediate

Character Controllers and Platformer Physics

Updated: May 16, 2026
30 min read

# CHAPTER 7

Character Controllers and Platformer Physics

1. Introduction

If you attach a Rigidbody to a capsule and push it with AddForce, you will quickly realize that true physics feels terrible for a player character. The character will slide on ice when trying to stop, get stuck on tiny bumps, and slowly slide down gentle slopes. Players do not want realistic momentum; they want instantaneous, pixel-perfect control. In this chapter, we will bridge the gap between strict physics and arcade gameplay. We will explore Character Controllers. We will learn how to bypass friction, detect the ground accurately using Raycasts, handle sloped terrain, and implement "Coyote Time" for perfect platforming.

2. Learning Objectives

By the end of this chapter, you will be able to:
  • Explain why standard Rigidbodies make poor platformer characters.
  • Implement robust Ground Detection using SphereCasts/Raycasts.
  • Calculate and apply movement perpendicular to sloped surfaces.
  • Program "Coyote Time" and "Jump Buffering" for enhanced game feel.
  • Decide between Kinematic and Dynamic character controllers.

3. The Rigidbody Friction Problem

When a Rigidbody character pushes against a wall while in the air, the physical friction between the character's collider and the wall's collider will cause the character to get stuck and refuse to fall!
  • The Fix: You must create a Physics Material with 0 Friction and apply it to the player's collider. This makes the player perfectly slippery, allowing them to slide down walls naturally.

4. Advanced Ground Detection

Checking if velocity.y == 0 is a terrible way to check if a player is on the ground. (What if they are at the apex of a jump?). Using OnCollisionEnter is also buggy if the floor is made of multiple tiny blocks.
  • The Industry Standard: Use a mathematical laser pointing down from the feet.
csharp
1234567891011
bool isGrounded;
public Transform feetPosition;
public float checkRadius = 0.2f;
public LayerMask groundLayer;

void Update()
{
    // Draw an invisible mathematical sphere at the feet.
    // If it overlaps with anything on the "Ground" layer, we are grounded!
    isGrounded = Physics.CheckSphere(feetPosition.position, checkRadius, groundLayer);
}

5. Handling Slopes

If a player walks into a 45-degree ramp with a standard (1, 0, 0) forward vector, they will smash into it and bounce. We must calculate the angle of the slope and rotate our movement vector to slide *up* it.
  1. 1. Shoot a Raycast down to hit the slope.
  1. 2. Get the "Normal" of the slope (the mathematical arrow pointing straight out from the ramp's surface).
  1. 3. Use Vector Cross Products (or engine projection methods) to angle the forward movement vector parallel to the ramp.

6. The Magic of "Coyote Time"

In classic platformers like *Super Mario*, if you run off a ledge, you actually have about 0.1 seconds of falling through the air where you can *still* press jump. This is called Coyote Time (named after Wile E. Coyote). It prevents player frustration when they press jump a millisecond too late.
csharp
123456789101112131415161718192021
float coyoteTime = 0.1f;
float coyoteTimeCounter;

void Update()
{
    if (isGrounded)
    {
        coyoteTimeCounter = coyoteTime; // Reset timer
    }
    else
    {
        coyoteTimeCounter -= Time.deltaTime; // Tick down in the air
    }

    // You can jump even if you are slightly off the ledge!
    if (Input.GetKeyDown("space") && coyoteTimeCounter > 0f)
    {
        Jump();
        coyoteTimeCounter = 0f; // Consume the jump
    }
}

7. Visual Learning: Slope Movement

txt
123456789
[ Standard Forward Vector (Smashes into ramp) ]
     -->
        /
       / (Ramp)

[ Projected Slope Vector (Slides up ramp) ]
      /^
     / |
    /

8. Best Practices

  • Jump Buffering: Along with Coyote Time, implement a Jump Buffer. If the player is falling and presses "Jump" 0.1 seconds *before* they hit the ground, save that input. The moment they touch the ground, execute the jump automatically. This makes the controls feel incredibly responsive and fluid.

9. Common Mistakes

  • Applying Velocity Directly: Many tutorials teach rb.velocity = new Vector3(x, rb.velocity.y, z);. This manually overrides the physics engine every frame. It works for simple games, but if a moving platform or an explosion tries to push the player, this line of code will instantly delete that external force! It is safer to use rb.AddForce or dedicated Kinematic Character Controllers.

10. Mini Project: Build a Responsive Platformer Controller

Objective: Combine Ground Detection, Zero Friction, and Coyote Time. *(Assume a 2D environment using Rigidbody2D)*
csharp
12345678910111213141516171819202122232425262728
class PlatformerPlayer
{
    public Rigidbody2D rb;
    public float speed = 10f;
    public float jumpForce = 15f;
    
    private float coyoteTimer;
    private bool isGrounded;

    void Update()
    {
        isGrounded = Physics2D.OverlapCircle(transform.position, 0.2f, LayerMask.GetMask("Ground"));

        if (isGrounded) coyoteTimer = 0.1f;
        else coyoteTimer -= Time.deltaTime;

        // X Movement
        float x = Input.GetAxisRaw("Horizontal");
        rb.velocity = new Vector2(x * speed, rb.velocity.y);

        // Jump
        if (Input.GetKeyDown(KeyCode.Space) && coyoteTimer > 0f)
        {
            rb.velocity = new Vector2(rb.velocity.x, jumpForce);
            coyoteTimer = 0f;
        }
    }
}

11. Practice Exercises

  1. 1. Why must you apply a zero-friction Physics Material to a Rigidbody player character in a platformer?
  1. 2. Explain the concept of "Coyote Time" and why it improves player experience.

12. MCQs with Answers

Question 1

You are programming a 3D character. When they walk up a steep ramp, they bounce violently and launch into the air. What physics calculation are you missing?

Question 2

What is the most reliable way to detect if a player character is standing on the ground?

13. Interview Questions

  • Q: Explain the difference between a Dynamic Rigidbody controller and a Kinematic Character Controller (KCC). Why do most AAA first-person shooters use KCCs instead of standard Rigidbodies?
  • Q: Walk me through the implementation of "Jump Buffering." How does it solve the issue of a player pressing the jump button slightly too early while falling?
  • Q: A player is pushing against a vertical wall in mid-air. They let go of the keyboard, but they do not fall; they remain stuck to the wall. Identify the physics engine interaction causing this and provide the solution.

14. FAQs

Q: Unity has a built-in CharacterController component. Should I use it? A: Yes! Unity's built-in CharacterController is a Kinematic controller. It ignores gravity and momentum, allowing you to code exact pixel-perfect movement. It also handles slopes and stairs automatically. It is highly recommended for 3D games over a Rigidbody!

15. Summary

In Chapter 7, we tamed the physics engine to create snappy, responsive character controls. We solved the "sticky wall" issue by removing friction. We built robust ground detection using SphereCasts, bypassing the unreliability of standard collision events. We tackled sloped terrain using Vector projection. Finally, we learned the secrets of "Game Feel" by implementing Coyote Time and Jump Buffering, turning rigid physics simulations into fluid, fun arcade gameplay.

16. Next Chapter Recommendation

Our character moves perfectly. Now it is time to give them a weapon. Proceed to Chapter 8: Projectile and Ballistics Systems.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·