Knockback
Push an entity away when hit. The physical consequence of damage. Creates space, shows impact, and can create environmental hazards.
The Feel
Knockback creates physical consequence. Damage isn't just a number going down - it's being pushed back, losing ground, potentially falling into danger. It makes combat spatial.
Good knockback also creates opportunities: knock an enemy off a ledge, into spikes, or into another enemy.
Exposed Variables
| Variable | Type | Default | What it does |
|---|---|---|---|
knockbackForce |
float | 8.0 | Horizontal push force |
knockbackUp |
float | 4.0 | Vertical lift during knockback |
hitstunDuration |
float | 0.2 | Time target can't act after hit |
knockbackDecay |
float | 10.0 | How fast knockback velocity decays |
Tuning Guide
For light hit reactions:
knockbackForce= 3 - 5knockbackUp= 0 - 2hitstunDuration= 0.1- Quick recovery, fast combat
Reference: Hack-and-slash, character action games
For heavy, consequential hits:
knockbackForce= 10 - 15knockbackUp= 5 - 8hitstunDuration= 0.3 - 0.5- Major displacement, time to reposition
Reference: Smash Bros., platform fighters
For environmental kill potential:
- High
knockbackForce - Significant
knockbackUp(launches into air) - Design levels with hazards at edges
Pseudocode
Pseudocode
On Hit:
- Calculate direction from attacker to target
- Apply velocity: (direction * knockbackForce) + (up * knockbackUp)
- Start hitstun timer
- Disable target's movement/actions
Every Frame during hitstun:
- Decay knockback velocity
- Prevent target actions
On hitstun end:
- Restore target control
Unity Setup (2D)
- Setup the Target (entity that receives knockback):
- Add
Rigidbody2D(Gravity Scale: 3, Freeze Rotation Z: checked) - Add
Collider2D(BoxCollider2D or CapsuleCollider2D) - Add the
KnockbackReceiver2D.csscript below
- Add
- From Attack Scripts:
- Get target's KnockbackReceiver2D component
- Call
ApplyKnockback(attackerPosition, force, upForce) - Or use the preset methods for light/medium/heavy hits
- Optional - Movement Integration:
- Your movement script should check
IsInHitstun - Disable player input while in hitstun
- Your movement script should check
- Optional - Weight System:
- Set
weightin Inspector (1 = normal, 2 = heavy, 0.5 = light) - Heavier entities receive less knockback
- Set
The Script (2D)
KnockbackReceiver2D.cs
// ============================================================
// KnockbackReceiver2D.cs
// Attach to any entity that can be knocked back when hit.
// Includes hitstun (action disable) and weight system.
// Unity 6.x
// ============================================================
using UnityEngine;
using System;
[RequireComponent(typeof(Rigidbody2D))]
public class KnockbackReceiver2D : MonoBehaviour
{
// ========================================================
// KNOCKBACK PARAMETERS - Tune these in the Inspector
// ========================================================
[Header("Weight")]
[Tooltip("How resistant to knockback (1 = normal, 2 = half knockback, 0.5 = double)")]
[SerializeField] [Range(0.1f, 5f)] private float weight = 1f;
[Header("Knockback Decay")]
[Tooltip("How fast horizontal knockback velocity decays")]
[SerializeField] [Range(0f, 20f)] private float horizontalDecay = 8f;
[Tooltip("Should we apply decay or let physics handle it?")]
[SerializeField] private bool useManualDecay = true;
[Header("Preset Forces (for ApplyLightHit, etc.)")]
[Tooltip("Light hit: horizontal force")]
[SerializeField] private float lightForce = 4f;
[Tooltip("Light hit: upward force")]
[SerializeField] private float lightUpForce = 2f;
[Tooltip("Light hit: hitstun duration")]
[SerializeField] private float lightHitstun = 0.1f;
[Tooltip("Medium hit: horizontal force")]
[SerializeField] private float mediumForce = 8f;
[Tooltip("Medium hit: upward force")]
[SerializeField] private float mediumUpForce = 4f;
[Tooltip("Medium hit: hitstun duration")]
[SerializeField] private float mediumHitstun = 0.2f;
[Tooltip("Heavy hit: horizontal force")]
[SerializeField] private float heavyForce = 15f;
[Tooltip("Heavy hit: upward force")]
[SerializeField] private float heavyUpForce = 8f;
[Tooltip("Heavy hit: hitstun duration")]
[SerializeField] private float heavyHitstun = 0.4f;
[Header("Debug")]
[Tooltip("Show debug info in console")]
[SerializeField] private bool debugMode = false;
// ========================================================
// INTERNAL STATE
// ========================================================
private Rigidbody2D rb;
private float hitstunTimer = 0f;
private bool inHitstun = false;
private Vector2 knockbackVelocity = Vector2.zero;
// ========================================================
// EVENTS - Subscribe to react to knockback
// ========================================================
/// <summary>
/// Fired when knockback starts (direction, force)
/// </summary>
public event Action<Vector2, float> OnKnockbackStart;
/// <summary>
/// Fired when hitstun ends
/// </summary>
public event Action OnHitstunEnd;
// ========================================================
// PUBLIC PROPERTIES
// ========================================================
/// <summary>
/// True while in hitstun (cannot act)
/// </summary>
public bool IsInHitstun => inHitstun;
/// <summary>
/// Remaining hitstun time
/// </summary>
public float HitstunRemaining => hitstunTimer;
/// <summary>
/// This entity's weight (affects knockback received)
/// </summary>
public float Weight => weight;
// ========================================================
// UNITY LIFECYCLE
// ========================================================
void Awake()
{
rb = GetComponent<Rigidbody2D>();
}
void Update()
{
// Process hitstun timer
if (inHitstun)
{
hitstunTimer -= Time.deltaTime;
if (hitstunTimer <= 0f)
{
EndHitstun();
}
}
}
void FixedUpdate()
{
// Apply manual knockback decay
if (useManualDecay && inHitstun)
{
// Decay horizontal velocity toward zero
float decayAmount = horizontalDecay * Time.fixedDeltaTime;
float currentX = rb.linearVelocity.x;
if (Mathf.Abs(currentX) > decayAmount)
{
float newX = currentX - Mathf.Sign(currentX) * decayAmount;
rb.linearVelocity = new Vector2(newX, rb.linearVelocity.y);
}
else
{
rb.linearVelocity = new Vector2(0f, rb.linearVelocity.y);
}
}
}
// ========================================================
// PUBLIC METHODS - Call these to apply knockback
// ========================================================
/// <summary>
/// Apply knockback with full control over parameters
/// </summary>
/// <param name="attackerPosition">Position of the attacker (knockback away from this)</param>
/// <param name="horizontalForce">Force pushing away horizontally</param>
/// <param name="upwardForce">Force pushing upward</param>
/// <param name="hitstunDuration">How long target can't act</param>
public void ApplyKnockback(Vector2 attackerPosition, float horizontalForce,
float upwardForce, float hitstunDuration)
{
// Calculate direction FROM attacker TO this entity
Vector2 direction = ((Vector2)transform.position - attackerPosition).normalized;
// If directly above/below, default to facing away from attacker's right
if (Mathf.Abs(direction.x) < 0.1f)
{
direction.x = transform.position.x > attackerPosition.x ? 1f : -1f;
}
// Apply weight modifier (heavier = less knockback)
float weightModifier = 1f / weight;
float finalHorizontal = horizontalForce * weightModifier;
float finalUpward = upwardForce * weightModifier;
// Build knockback velocity
knockbackVelocity = new Vector2(
Mathf.Sign(direction.x) * finalHorizontal,
finalUpward
);
// Apply velocity directly
rb.linearVelocity = knockbackVelocity;
// Start hitstun
inHitstun = true;
hitstunTimer = hitstunDuration;
// Fire event
OnKnockbackStart?.Invoke(direction, horizontalForce);
if (debugMode)
{
Debug.Log($"Knockback applied! Direction: {direction}, " +
$"Force: ({finalHorizontal}, {finalUpward}), " +
$"Hitstun: {hitstunDuration}s");
}
}
/// <summary>
/// Apply knockback in a specific direction (not away from attacker)
/// </summary>
public void ApplyKnockbackDirection(Vector2 direction, float horizontalForce,
float upwardForce, float hitstunDuration)
{
float weightModifier = 1f / weight;
knockbackVelocity = new Vector2(
direction.x * horizontalForce * weightModifier,
upwardForce * weightModifier
);
rb.linearVelocity = knockbackVelocity;
inHitstun = true;
hitstunTimer = hitstunDuration;
OnKnockbackStart?.Invoke(direction, horizontalForce);
}
/// <summary>
/// Apply a light hit knockback
/// </summary>
public void ApplyLightHit(Vector2 attackerPosition)
{
ApplyKnockback(attackerPosition, lightForce, lightUpForce, lightHitstun);
}
/// <summary>
/// Apply a medium hit knockback
/// </summary>
public void ApplyMediumHit(Vector2 attackerPosition)
{
ApplyKnockback(attackerPosition, mediumForce, mediumUpForce, mediumHitstun);
}
/// <summary>
/// Apply a heavy hit knockback
/// </summary>
public void ApplyHeavyHit(Vector2 attackerPosition)
{
ApplyKnockback(attackerPosition, heavyForce, heavyUpForce, heavyHitstun);
}
/// <summary>
/// Immediately end hitstun (for special recovery moves)
/// </summary>
public void CancelHitstun()
{
if (inHitstun)
{
EndHitstun();
}
}
// ========================================================
// INTERNAL METHODS
// ========================================================
/// <summary>
/// End hitstun and restore control
/// </summary>
private void EndHitstun()
{
inHitstun = false;
hitstunTimer = 0f;
OnHitstunEnd?.Invoke();
if (debugMode)
{
Debug.Log("Hitstun ended - control restored");
}
}
}
// ============================================================
// USAGE EXAMPLES:
//
// 1. From an attack script when dealing damage:
// void DealDamage(GameObject target, int damage)
// {
// var knockback = target.GetComponent<KnockbackReceiver2D>();
// if (knockback != null)
// {
// knockback.ApplyMediumHit(transform.position);
// }
// // Also apply damage to health, play effects, etc.
// }
//
// 2. Custom knockback for special attacks:
// void SuperPunch(GameObject target)
// {
// var knockback = target.GetComponent<KnockbackReceiver2D>();
// // Massive knockback with long hitstun
// knockback.ApplyKnockback(transform.position, 20f, 12f, 0.6f);
// }
//
// 3. In your movement script, disable input during hitstun:
// void Update()
// {
// if (knockbackReceiver.IsInHitstun)
// {
// // Don't process movement input
// return;
// }
// // Normal movement code...
// }
//
// 4. Subscribe to knockback events:
// void Start()
// {
// GetComponent<KnockbackReceiver2D>().OnKnockbackStart += OnHit;
// GetComponent<KnockbackReceiver2D>().OnHitstunEnd += OnRecover;
// }
// void OnHit(Vector2 dir, float force) { PlayHitAnimation(); }
// void OnRecover() { PlayRecoverAnimation(); }
//
// 5. Combine with HitstopManager for full impact:
// void OnAttackHit(GameObject target)
// {
// HitstopManager.Instance.TriggerMediumHit();
// target.GetComponent<KnockbackReceiver2D>().ApplyMediumHit(transform.position);
// ScreenShake.Instance.TriggerShake(0.2f, 0.3f);
// }
//
// 6. For 3D: Change Rigidbody2D to Rigidbody, Vector2 to Vector3
// ============================================================
Common Issues
"Knockback goes the wrong direction"
Make sure you're calculating direction FROM attacker TO target, not the reverse. Also check if target's facing direction affects the calculation.
"Target gets stuck in walls"
Add collision checks or reduce knockback when near walls. Some videogames "slide" along walls; others cancel knockback.
"Combat feels floaty"
knockbackUp might be too high relative to gravity. Also check knockbackDecay - slow decay = floaty.
"Player gets knocked into pits unfairly"
Consider invincibility frames or reduced knockback near hazards. Or lean into it - environmental kills can be a feature, not a bug.
Combine With
- Hitstop - pause before knockback begins
- Screen Shake - emphasize the impact
- Squash & Stretch - deform on impact
- Input Buffer - buffer recovery actions during hitstun
Knockback Variations
| Type | Description |
|---|---|
| Fixed knockback | Same force regardless of damage dealt |
| Damage-scaled | More damage = more knockback (Smash Bros.) |
| Weight-modified | Heavier targets fly less far |
| Directional | Different attacks knock in different directions |
| Wall bounce | Hitting a wall bounces you back into play |
Why Teach This
Knockback teaches combat as space control. It connects damage to positioning, making combat about territory, not just health bars. Students learn that good combat systems have physical consequences.
Teaching Sequence
- Implement basic knockback (force away from attacker)
- Add hitstun to disable target temporarily
- Tune for different attack types
- Add environmental hazards to make knockback consequential
The Smash Bros. Lesson
Super Smash Bros. is the ultimate knockback-focused design. Damage increases knockback instead of reducing health. The only way to lose is to be knocked off the stage. This design makes positioning central to everything.
Even if students aren't making platform fighters, they can learn from this: what if knockback was the primary threat?
Assessment Questions
- How does knockback change where combat happens in a level?
- Why pair hitstun with knockback? What goes wrong without it?
- How would you balance knockback near instant-death hazards?
Heritage Notes
Knockback existed in early videogames (Castlevania's infamous knockback into pits) but became a design focus with fighting games. Street Fighter II's corner pressure, where knockback traps opponents at screen edge, showed that positioning could be a resource.
Platform fighters like Smash Bros. made knockback the entire victory condition, replacing health bars entirely.
Knockback as Permissions
From a Permissions perspective, knockback temporarily revokes permission to act (hitstun) and forcibly repositions the target. It's one of the most aggressive permission changes in action videogames.
This can feel fair (you got hit, you pay the price) or unfair (chain knockback that never lets you act). Tuning knockback is tuning fairness.
The Castlevania Problem
Classic Castlevania is notorious for knockback into pits - one hit sends you flying backwards into instant death. This was partly technical limitation, partly intentional difficulty.
Modern videogames usually provide either:
- Invincibility frames to escape after one hit
- Reduced knockback near hazards
- Ledge grabbing or recovery mechanics
The Castlevania approach is a heritage choice: using it quotes that tradition, with all its implications.
Knockback and Atmosphere
Knockback force communicates power. A weak slap might push you slightly; a massive hit launches you across the screen. The Atmosphere of a weapon is partially defined by its knockback. A weighty hammer should send enemies flying; a quick dagger shouldn't.
Related
- Hitstop - pause before the knockback
- Screen Shake - visual impact feedback
- Permissions - knockback as permission revocation