Skip to main content

๐Ÿ•น๏ธ Lesson 10.4: 2D Physics โ€” A Platformer Base (Mini-Project)

You have sprites, sorting, and a tilemap. Time to make something move. 2D physics is its own world โ€” a separate engine with its own components. In this mini-project you'll give a sprite a body, drop it onto your tilemap, and build a tight jump with a reliable ground check, driven by the new Input System.

๐ŸŽฏ Learning Objectives

By the end of this lesson, you will be able to:

  • Use Rigidbody2D and its body types (Dynamic, Kinematic, Static)
  • Choose the right Collider2D and tune gravity for platforming feel
  • Move and jump a character with the new Input System
  • Detect the ground reliably with Physics2D.OverlapCircle
  • Assemble a playable platformer base on your tilemap level

Estimated Time: 50 minutes  ยท  Prerequisite: Lesson 10.3 (Tilemaps) & Module 3 (Input System)

In This Lesson

2D Physics Is a Separate Engine

Unity ships two physics engines: 3D (Rigidbody, Collider, Physics) and 2D (Rigidbody2D, Collider2D, Physics2D). They don't interact โ€” a 3D collider won't stop a 2D body. Everything in the 2D track uses the 2D variants, and mixing them up is the most common beginner slip here.

โš ๏ธ Everything ends in "2D"

Rigidbody2D, Box/Circle/Capsule Collider 2D, Physics2D.OverlapCircle, OnCollisionEnter2D, OnTriggerEnter2D. If you accidentally add a 3D Collider or subscribe to OnCollisionEnter (no 2D), nothing collides and no callbacks fire.

Rigidbody2D & Body Types

A Rigidbody2D hands an object to the physics simulation. Its Body Type decides how:

  • Dynamic โ€” fully simulated: gravity, forces, collisions. Your player is Dynamic.
  • Kinematic โ€” moved only by script (MovePosition), not by gravity/forces, but still detects collisions. Good for moving platforms and some controllers.
  • Static โ€” never moves; the cheap default for level geometry (a Tilemap Collider is effectively static).

๐Ÿ“– Definition

Gravity Scale: a per-body multiplier on 2D gravity. Platformers almost never feel right at 1 โ€” a heavier value like 3โ€“4 gives a snappy, arcade fall instead of a floaty drift.

โœ… Pro Tip

On the player's Rigidbody2D, set Collision Detection to Continuous and freeze Z rotation (Constraints) so a capsule doesn't tip over when it bumps a wall.

Colliders & Feel

Add a Collider2D so the body actually touches things. A Capsule Collider 2D (vertical) is the platformer favourite โ€” its rounded bottom slides over tile seams that a Box collider would snag on. Your tilemap already has a Tilemap Collider 2D + Composite Collider 2D from Lesson 10.3, so the ground is ready.

๐Ÿ’ก Physics Materials 2D: assign one with friction = 0 to the player to stop it "sticking" to walls, and tune bounciness if you want springy surfaces.

Moving & Jumping

Read input with the new Input System (Module 3): a Move Vector2 and a Jump button. Set horizontal velocity directly for crisp control, and jump by setting upward velocity. Do physics writes in FixedUpdate:

using UnityEngine;
using UnityEngine.InputSystem;

[RequireComponent(typeof(Rigidbody2D))]
public class PlatformerController2D : MonoBehaviour
{
    [SerializeField] float moveSpeed = 8f;
    [SerializeField] float jumpForce = 14f;

    [Header("Ground Check")]
    [SerializeField] Transform groundCheck;      // an empty at the feet
    [SerializeField] float groundRadius = 0.15f;
    [SerializeField] LayerMask groundLayer;      // set to your "Ground" layer

    Rigidbody2D rb;
    float moveInput;
    bool jumpQueued;
    bool isGrounded;

    void Awake() => rb = GetComponent<Rigidbody2D>();

    // Hooked up via a PlayerInput component (Invoke Unity Events)
    public void OnMove(InputAction.CallbackContext ctx) => moveInput = ctx.ReadValue<Vector2>().x;
    public void OnJump(InputAction.CallbackContext ctx) { if (ctx.performed) jumpQueued = true; }

    void FixedUpdate()
    {
        isGrounded = Physics2D.OverlapCircle(groundCheck.position, groundRadius, groundLayer);

        // horizontal: set velocity directly for responsive control
        rb.linearVelocity = new Vector2(moveInput * moveSpeed, rb.linearVelocity.y);

        if (jumpQueued && isGrounded)
            rb.linearVelocity = new Vector2(rb.linearVelocity.x, jumpForce);

        jumpQueued = false;
    }
}

โš ๏ธ Unity 6 renamed velocity

On Rigidbody2D it's now linearVelocity (the old velocity is deprecated). Also note we queue the jump in the callback and consume it in FixedUpdate so a press is never missed between physics steps.

The Ground Check

You only want to jump when standing on something. The robust way is a tiny overlap circle at the character's feet: if it overlaps anything on the Ground layer, you're grounded. Put an empty child called GroundCheck at the feet and reference it.

A 2D ground check using an overlap circle at the character's feet A sprite character stands on a tile floor. A small green circle at its feet overlaps the ground tiles, meaning grounded. To the right, the same character mid-jump has the circle in the air overlapping nothing, meaning not grounded. Grounded In the air overlaps ground โ†’ isGrounded = true overlaps nothing โ†’ isGrounded = false
Figure 1: The ground check. A small overlap circle at the feet returns true only while it touches the Ground layer โ€” so jumping is allowed on the left, blocked on the right.

The decision each physics step is simple:

flowchart LR A["FixedUpdate"] --> B["OverlapCircle at feet
on Ground layer?"] B -- "Yes" --> C["isGrounded = true"] B -- "No" --> D["isGrounded = false"] C --> E{"Jump pressed?"} E -- "Yes" --> F["velocity.y = jumpForce"] E -- "No" --> G["keep falling"] D --> G

Figure 2: Ground check gating the jump.

๐Ÿ’ก Feel bonus: add a short coyote time (allow a jump for ~0.1s after leaving a ledge) and jump buffering (remember a press for ~0.1s before landing). These two tricks are what make a platformer feel "tight" instead of stiff.

Mini-Project: The Platformer Base

๐Ÿ—๏ธ Put it on the tilemap

Goal: a sprite character that runs and jumps around the tilemap level you painted in Lesson 10.3.

  1. Put your ground tiles on a Ground layer (the tilemap with the Composite Collider).
  2. Create the player: a Sprite Renderer + Rigidbody2D (Dynamic, freeze Z rotation, Gravity Scale ~3) + Capsule Collider 2D.
  3. Add an empty child GroundCheck at the feet.
  4. Add a PlayerInput component with a Move (Vector2) and Jump (Button) action; set Behavior to Invoke Unity Events and wire OnMove/OnJump to the controller.
  5. Attach PlatformerController2D; set its Ground Layer mask to Ground.
  6. Press Play: run left/right and jump only when grounded. Tune moveSpeed, jumpForce, and Gravity Scale until it feels good.
๐Ÿ’ก Hint: my character jumps infinitely / mid-air

The ground check isn't finding the floor. Confirm the Ground Layer mask matches the tilemap's layer, the GroundCheck transform sits just at the feet (not inside the body), and groundRadius is small but non-zero (~0.15). Gizmo it by drawing the circle in OnDrawGizmosSelected.

โœ… Success check

The character runs with responsive control, falls with a satisfying weight, and jumps a consistent height โ€” but only from the ground. No tipping over, no sticking to walls.

๐ŸŽฏ Quick Quiz

Question 1: Your 2D player falls straight through the tilemap floor. The most likely cause?

Question 2: Which is the robust way to know the character can jump?

Question 3: Why write movement in FixedUpdate and only queue the jump in the input callback?

Summary

๐ŸŽ‰ Key Takeaways

  • 2D uses a separate engine โ€” always the 2D components and callbacks.
  • Rigidbody2D body types: Dynamic (player), Kinematic (script-moved), Static (level).
  • Tune Gravity Scale and use a Capsule Collider 2D for good platformer feel; freeze Z rotation.
  • Set horizontal linearVelocity for crisp control; jump by setting upward velocity in FixedUpdate.
  • Physics2D.OverlapCircle at the feet is the reliable ground check that gates jumping.

๐Ÿš€ What's Next?

Your character moves โ€” but it's a static sprite sliding around. In Module 11: 2D Animation & Lighting we bring it to life, starting with Lesson 11.1: Frame-Based Sprite Animation โ€” and you'll see the very same Animator state machine from Module 1 driving 2D frames.

๐Ÿ•น๏ธ You've got a platformer

Run, fall, and jump on a real tilemap level โ€” the physical heart of every 2D platformer, built on the same input and physics discipline you learned in 3D.