Contents

Lix 2D Movement - Full Documentation

Author: SillyLix
Version: 1.1.1
Release Date: March 2025
Last Updated: December 2025
License: License
Unity Version: 6000+ (Tested with 6000.0.161f1) Should also work on older Versions too but hasn't been tested.
Download: itch.io


1. Overview

The Lix 2D Movement system is a flexible, production-ready movement controller for 2D games in Unity. It provides a solid foundation for platformers, top-down games, and other 2D gameplay scenarios. The script is highly configurable through the Unity Inspector, allowing you to enable/disable features as needed without modifying code.


Key Strengths:


Quick Start:

  1. Assign a Collider2D component to player GameObject (required you won't be able to put the script on a GameObject without one).
  2. Attach PlayerMovement2D script to your player GameObject.
  3. Set the Ground Layer in the Inspector to define what counts as ground.
  4. Configure movement speed, jump force, and other parameters to taste.
  5. Enable/disable features (jump, dash, etc.) via Inspector checkboxes.

2. Requirements

Before using the PlayerMovement2D script, ensure your project meets these requirements:


3. Core Features


3.1 – Horizontal & Vertical Movement

The system supports both traditional platformer movement (horizontal only) and top-down movement (horizontal + vertical). Input is automatically normalized to prevent diagonal movement from being faster. Also supports vertical-only movement for specific top-down scenarios.


3.2 - Jumping & Double Jump

Comprehensive jump system with quality-of-life features.


3.3 - Dashing

A dash ability that temporarily increases movement speed for a short duration.


3.4 - Ground Detection

A raycast-based system to detect if the player is on the ground. This is critical for jump mechanics.


4. Configuration & Inspector Settings

All settings are exposed in the Unity Inspector. No code changes required to customize behavior.

4.1 - Movement Settings

Parameter Type Default Description
enableHorizontalMovement bool true Allow left/right movement via arrow keys or WASD.
enableVerticalMovement bool false Allow up/down movement (enables top-down mode). Disables gravity automatically.
moveSpeed float 5f Movement speed in units per second. Higher = faster movement.
freezeRotation bool true Prevent the Rigidbody2D from rotating due to physics. Recommended: on.

4.2 - Jump Settings

Parameter Type Default Description
enableJump bool false Enable the jump mechanic.
enableDoubleJump bool false Allow a second jump while airborne.
jumpForce float 8f Upward velocity applied when jumping. Higher = higher jump.
gravityScale float 1f Gravity multiplier. 0 = no gravity, 1 = normal, 2+ = stronger gravity.
coyoteTimeEnabled bool true Allow jumping a short time after leaving the ground.
coyoteTimeSeconds float 0.2f Duration of coyote time (0.0-0.3 recommended).
jumpBufferSeconds float 0.1f Duration to buffer jump input before landing (0.0-0.3 recommended).
groundRayLength float 1f Distance of downward raycast for ground detection. Adjust based on sprite size.
drawGroundRay bool true Visualize the ground detection raycast in the Scene view (editor only).
groundLayer LayerMask Default Layer mask defining which objects count as ground for jump detection.
jumpKey KeyCode Space Key to trigger a jump.

4.3 - Dash Settings

Parameter Type Default Description
enableDash bool false Enable the dash ability.
dashSpeed float 20f Speed during dash. Higher = faster dash.
dashDuration float 0.2f How long the dash lasts in seconds.
dashKey KeyCode LeftShift Key to trigger a dash.

4.4 - Runtime State (Read-Only in Inspector)

These fields update in real-time and help with debugging. They are visible in the Inspector but should not be modified directly.

Variable Type Description
horizontalInput float Current horizontal input value (-1 to 1).
verticalInput float Current vertical input value (-1 to 1).
isGrounded bool Is the player touching the ground?
canDoubleJump bool Is a double jump available?
isDashing bool Is the player currently dashing?
isTouchingWall bool Is the player in contact with a wall side?

5. Code Explanation

The PlayerMovement2D script is organized into clear sections with modular methods. Here's how the code works:


5.1 - Initialization (Start & Initialize)

When the game starts, the script automatically sets up the Rigidbody2D component:

void Start()
{
    Initialize();
}

void Initialize()
{
    // Get or create Rigidbody2D
    if (rb2d == null)
    {
        if (!TryGetComponent<Rigidbody2D>(out rb2d))
        {
            gameObject.AddComponent<Rigidbody2D>();
            rb2d = GetComponent<Rigidbody2D>();
        }
    }
    
    // Configure physics settings
    rb2d.gravityScale = gravityScale;
    rb2d.freezeRotation = freezeRotation;
    rb2d.collisionDetectionMode = CollisionDetectionMode2D.Continuous;
}

What it does: Finds or creates a Rigidbody2D, applies gravity and rotation settings, and enables continuous collision detection for smooth physics.


5.2 - Main Update Loop (Update & FixedUpdate)

void Update()
{
    HandleMovement();
    HandleJumping();
    HandleDashing();
}

private void FixedUpdate()
{
    CheckGrounded();
}

What it does: Update() handles input every frame. FixedUpdate() runs at a fixed physics timestep and checks ground collision. This separation ensures responsive input while maintaining stable physics.


5.3 - Movement (HandleMovement)

void HandleMovement()
{
    Vector2 inputDirection = Vector2.zero;

    if (enableHorizontalMovement && !isTouchingWall)
    {
        horizontalInput = Input.GetAxis("Horizontal");
        inputDirection.x = horizontalInput;
    }

    if (enableVerticalMovement)
    {
        rb2d.gravityScale = 0; // Top-down mode
        verticalInput = Input.GetAxis("Vertical");
        inputDirection.y = verticalInput;
    }

    // Normalize to prevent diagonal speed boost
    if (inputDirection.magnitude > 0f)
    {
        inputDirection.Normalize();
    }

    // Apply movement
    if (enableHorizontalMovement && enableVerticalMovement)
    {
        rb2d.linearVelocity = new Vector2(inputDirection.x * moveSpeed, inputDirection.y * moveSpeed);
    }
    else if (enableHorizontalMovement)
    {
        rb2d.linearVelocity = new Vector2(inputDirection.x * moveSpeed, rb2d.linearVelocity.y);
    }
    else if (enableVerticalMovement)
    {
        rb2d.linearVelocity = new Vector2(rb2d.linearVelocity.x, inputDirection.y * moveSpeed);
    }
}

What it does:


5.4 - Ground Detection (CheckGrounded)

private void CheckGrounded()
{
    // Cast a ray downward from the player
    Vector2 rayOrigin = transform.position + Vector3.down * 0.1f;
    RaycastHit2D hit = Physics2D.Raycast(rayOrigin, Vector2.down, groundRayLength, groundLayer);

    if (drawGroundRay)
    {
        Debug.DrawRay(rayOrigin, Vector2.down * groundRayLength, Color.red);
    }

    if (hit.collider != null)
    {
        // Player is on ground
        isGrounded = true;
        if (coyoteTimeEnabled)
        {
            StopCoroutine(waitBeforeTurningOffJump());
        }
    }
    else
    {
        // Player is airborne
        if (coyoteTimeEnabled && isGrounded)
        {
            StartCoroutine(waitBeforeTurningOffJump());
        }
        else
        {
            isGrounded = false;
        }
    }
}

What it does:


5.5 - Jumping (HandleJumping)

void HandleJumping()
{
    if (enableJump)
    {
        // Update jump buffer timer
        if (Input.GetKeyDown(jumpKey))
        {
            jumpBufferTimer = jumpBufferSeconds;
        }
        else if (jumpBufferTimer > 0f)
        {
            jumpBufferTimer -= Time.deltaTime;
        }

        bool wantJumpNow = Input.GetKeyDown(jumpKey);

        // Ground or wall jump
        if ((isGrounded || isTouchingWall) && (wantJumpNow || jumpBufferTimer > 0f))
        {
            rb2d.linearVelocity = new Vector2(rb2d.linearVelocity.x, jumpForce);
            canDoubleJump = enableDoubleJump;
            jumpBufferTimer = 0f;
        }
        // Double jump
        else if (canDoubleJump && wantJumpNow)
        {
            rb2d.linearVelocity = new Vector2(rb2d.linearVelocity.x, jumpForce);
            canDoubleJump = false;
        }

        // Fast fall
        if ((Input.GetKey(KeyCode.DownArrow) || Input.GetKey(KeyCode.S)) && !isGrounded)
        {
            rb2d.gravityScale = gravityScale * 2;
        }
        else
        {
            rb2d.gravityScale = gravityScale;
        }
    }
}

What it does:


5.6 - Dashing (HandleDashing & Dash Coroutine)

void HandleDashing()
{
    if (enableDash && Input.GetKeyDown(dashKey) && !isDashing)
    {
        StartCoroutine(Dash());
    }
}

private IEnumerator Dash()
{
    isDashing = true;
    float originalSpeed = moveSpeed;
    moveSpeed = dashSpeed;
    yield return new WaitForSeconds(dashDuration);
    moveSpeed = originalSpeed;
    isDashing = false;
}

What it does:


5.7 - Wall Collision Detection (OnCollisionEnter2D & OnCollisionExit2D)

void OnCollisionEnter2D(Collision2D collision)
{
    if (collision.gameObject.layer == LayerMask.NameToLayer("Ground"))
    {
        Vector2 normal = collision.GetContact(0).normal;
        if (Mathf.Abs(normal.x) > 0.5f) // Horizontal normal = wall
        {
            isTouchingWall = true;
        }
    }
}

void OnCollisionExit2D(Collision2D collision)
{
    if (collision.gameObject.layer == LayerMask.NameToLayer("Ground"))
    {
        isTouchingWall = false;
    }
}

What it does:


5.8 - Coyote Time Coroutine (waitBeforeTurningOffJump)

IEnumerator waitBeforeTurningOffJump()
{
    yield return new WaitForSeconds(coyoteTimeSeconds);
    isGrounded = false;
}

What it does: Delays setting isGrounded = false by coyoteTimeSeconds, allowing the player to jump for a brief moment after leaving the ground. This makes jumping feel forgiving and responsive.


6. Example Usage


6.1 - Setup in the Editor

// Steps:
// 1. Create a new GameObject (e.g., "Player")
// 2. Add a Collider2D component (Box2D or Circle)
// 3. Add the PlayerMovement2D script to the GameObject
// 4. In the Inspector:
//    - Set "Move Speed" to 5 (or adjust to preference)
//    - Enable "Jump" and set "Jump Force" to 8
//    - Enable "Dash" and set "Dash Speed" to 20
//    - Set your ground layer into "Ground Layer"
// 5. Create platforms and assign them to the "Ground" layer
// 6. Press Play and use arrow keys to move, Space to jump, Shift to dash

6.2 - Platformer Setup

Goal: Create a classic 2D platformer with jumping and dashing.

Inspector Settings:
- Enable Horizontal Movement: ✓
- Enable Vertical Movement: ✗
- Enable Jump: ✓
- Enable Double Jump: ✓
- Enable Dash: ✓
- Move Speed: 6
- Jump Force: 10
- Dash Speed: 25
- Dash Duration: 0.15
- Coyote Time: 0.15 seconds
- Jump Buffer: 0.1 seconds

Controls:
- Arrow Keys / WASD: Move
- Space: Jump (twice for double jump)
- Shift: Dash
- Down Arrow: Fast fall while airborne

6.3 - Top-Down Setup

Goal: Create a top-down game (like Zelda) with 4-directional movement.

Inspector Settings:
- Enable Horizontal Movement: ✓
- Enable Vertical Movement: ✓
- Enable Jump: ✗
- Enable Dash: ✓
- Move Speed: 7
- Dash Speed: 18
- Dash Duration: 0.2

Controls:
- Arrow Keys / WASD: Move in any direction (normalized for equal diagonal speed)
- Shift: Dash

Notes:
- Gravity is automatically disabled
- Jump mechanics are unnecessary for top-down games
- Dash is useful for quick evasion or dodging

Pro Tips:

  • Fine-Tuning Feel: Adjust jumpForce and gravityScale until jumping feels responsive. Lower gravity = floatier feel, higher gravity = snappier feel.
  • Coyote Time: Start with 0.15-0.2 seconds. Too high and it feels sluggish; too low and it feels punishing.
  • Ground Ray Length: Visualize with "Draw Ground Ray" enabled. The ray should barely extend beyond your player sprite.
  • Dash Cooldown: The script prevents overlapping dashes. Consider adding a cooldown timer in a custom extension if desired.

7. Use Cases

Classic Platformer

Enable horizontal movement, jumping with double jump, coyote time, and dash. Perfect for games like Super Mario or Celeste-style platformers.

Top-Down Adventure

Enable both horizontal and vertical movement, disable jumping, enable dash for dodging. Ideal for Zelda-like games.

Wall Slide Puzzle

Use wall detection (automatic) and wall jumping to create challenging wall-based puzzles. Enable coyote time for forgiveness.

Speedrun-Friendly Platformer

Maximize coyote time and jump buffer, enable dash with minimal cooldown. Players can chain jumps and dashes for high mobility.


8. License

Overview: This license governs use of the provided assets and the rights granted to the Licensee.

1. Definitions

Asset(s): The files, code, and other materials provided by the Licensor.

End Product: A game, application, or other finished product in which the Asset is incorporated and distributed to end users.

2. Grant of License

The Licensor grants the Licensee a worldwide, non-exclusive, non-transferable license to:

3. Restrictions

Licensee may not:

4. Attribution

Attribution is NOT required for this asset, but it is very much so appreciated.

5. Ownership

Ownership: The Licensor retains all copyright and moral rights in the Asset. The Licensee receives only the rights expressly granted in this license.

6. Termination

Violation of any term of this license automatically terminates the rights granted. Upon termination the Licensee must cease using the Asset and remove it from any End Product distribution.

7. Disclaimer

Assets are provided "as is" without warranty of any kind. The Licensor is not responsible for any damages arising from the use of the Asset.