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:
- Modular design enable only the features you need
- Responsive controls with coyote time and jump buffering
- Support for both platformer (horizontal) and top-down (vertical + horizontal or only vertical) movement
- Dash ability with customizable duration and speed
- Fast fall mechanic for gravity control
- Automatic Rigidbody2D setup and configuration
Quick Start:
- Assign a
Collider2Dcomponent to player GameObject (required you won't be able to put the script on a GameObject without one). - Attach
PlayerMovement2Dscript to your player GameObject. - Set the
Ground Layerin the Inspector to define what counts as ground. - Configure movement speed, jump force, and other parameters to taste.
- Enable/disable features (jump, dash, etc.) via Inspector checkboxes.
2. Requirements
Before using the PlayerMovement2D script, ensure your project meets these requirements:
- Unity Engine: Version 6000+ (should work on old ones too but just to be sure.)
- Collider2D: Any type (Box2D, Circle, Polygon, etc.). Required for collision detection.
- Rigidbody2D: Automatically added by the script if missing. Set to
Dynamicbody type for best results. - Ground Layer: Create a layer named "Ground" and assign all ground/platform objects to it. This is critical for jump detection.
- Basic Unity Knowledge: Familiarity with the Inspector, layers, and basic scripting concepts.
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.
- Platformer Mode: Enable horizontal movement, disable vertical.
- Top-Down Mode: Enable both horizontal and vertical movement. enabling vertical movement will automatically disable gravity.
- Input Normalization: Diagonal input is normalized so diagonal speed equals single axis speed.
3.2 - Jumping & Double Jump
Comprehensive jump system with quality-of-life features.
- Basic Jump: Applies upward velocity when the player presses the jump key and is grounded.
- Double Jump: Optional second jump while airborne. Resets upon landing or wall contact.
- Coyote Time: Grace period after leaving the ground where the player can still jump. Typical value: 0.1-0.2 seconds. Makes jumping feel forgiving and responsive.
- Jump Buffer: Queue a jump input just before landing. When you land within the buffer window, the queued jump executes. Typical value: 0.1 seconds.
- Fast Fall: Hold the down arrow or S key while airborne to double gravity and fall faster.
3.3 - Dashing
A dash ability that temporarily increases movement speed for a short duration.
- Triggered by pressing the dash key (default: Left Shift).
- Dash speed and duration are fully configurable.
- Useful for evasion, repositioning, or adding dynamic gameplay moments.
3.4 - Ground Detection
A raycast-based system to detect if the player is on the ground. This is critical for jump mechanics.
- Raycast fires downward from the player's position each frame (in FixedUpdate).
- Can be visualized in the editor with the "Draw Ground Ray" toggle.
- Length is adjustable to account for different sprite sizes and terrain.
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:
- Reads input from arrow keys or WASD using
Input.GetAxis() - Blocks horizontal movement if the player touches a wall
- Normalizes diagonal input so diagonal movement isn't faster
- Applies velocity to the Rigidbody2D based on enabled axes
- Preserves vertical velocity when moving horizontally (so jumping isn't affected)
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:
- Fires a raycast downward from the player to detect ground contact
- Sets
isGrounded = trueif a ground layer object is hit - Manages coyote time by starting/stopping a coroutine that delays the
isGroundedflag - Visualizes the raycast in red in the editor for debugging
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:
- Tracks jump input in a buffer window for more forgiving controls
- Allows jumping when grounded or touching a wall, even if the jump key was pressed slightly before landing (jump buffer)
- Sets
canDoubleJumpflag on successful jump - Enables double jump if airborne and
canDoubleJumpis true - Doubles gravity when holding the down arrow/S key for faster falling
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:
HandleDashing()checks if the dash key is pressed and no dash is currently active- If conditions are met, it starts the
Dash()coroutine - The coroutine temporarily sets
moveSpeedtodashSpeed - After
dashDurationseconds, it restores the original speed and setsisDashing = false
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:
- Called automatically by Unity when the player collides with a ground object
- Checks the collision normal. If it's mostly horizontal (
abs(x) > 0.5), it's a wall - Sets
isTouchingWall = true, which blocks horizontal movement inHandleMovement() - Resets the flag when leaving the wall
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
jumpForceandgravityScaleuntil 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:
- Use, reproduce, and modify the Asset as part of an End Product.
- Sell, distribute, or monetize the End Product containing the Asset.
3. Restrictions
Licensee may not:
- Distribute, sell, license, or make the Asset available in standalone form (source files) outside of an End Product.
- Claim the Asset as the Licensee's own original work.
- Remove any required attribution or credits where specified by the Licensor.
- Redistribute the Asset as part of another asset pack, template, or library for resale or re-distribution.
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.
