Setting up a Recoil Effect in Unity

Published

To make gameplay dynamic, a lot of little effects are necessary. One such effect is a recoil on hit: remember when you played a Zelda game? A monster hit by an attack would get pushed back, sometimes falling off into a hole, never to be seen again. Or it could be you being pushed off a platform by a tricky boss.

Moldorm ALTTP
Yes, I'm looking at you Moldorm

We’ll be working to implement this effect in Unity. A first word: we’ll be implementing it for scratch, but you might prefer using rigidbodies.

We’ll start by setting up for our demonstration scene. We’ll create a controller which triggers the OnHit method on a GameObject when we press Space. It will send 2 pieces of information: the direction and the force of the hit.

Here’s our little scene controller:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MonsterHitSceneController : MonoBehaviour
{
    public MonsterHitMonsterController mTarget;
    public float mHitStrength;
    public Vector3 mHitDirection;


    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space)) {
            mTarget.OnHit(mHitStrength, mHitDirection.normalized);
        }
    }
}

Now that this is set, we can focus on the OnHit behaviour itself on our monster / player.
For our effect, we want the unit to be pushed back, but we also want to add some decay to it: the pushing force should not be constant but should instead start strong and decay over time. This means we need to set a duration for the recoil effect, and a variable tracking the elapsed time for the recoil effect, as well as the direction of the force and its strength.

Considering all this, we can adjust the force currently pushing the unit by a factor of: 1f - elapsed_time / recoil_duration. If we then square this value, we’ll get a very nice decay: a strong push at first which becomes weaker and weaker over time.
We’ll also use a variable, being_hit, to signify that the character is currently being pushed. Once elapsed_time > recoil_duration, we can set being_hit to false and go back to the default behaviour.

Here’s what the code looks like:

using UnityEngine;

public class MonsterHitMonsterController : MonoBehaviour
{
    public SpriteRenderer mSpriteRenderer;

    public float mRecoilDuration;    
    public Color mHitColor;

    private bool _bBeingHit;
    private float _mElapsed;

    private float _mHitStrength;
    private Vector3 _mHitDirection;

    void Start()
    {
        mSpriteRenderer = transform.GetComponent<SpriteRenderer>();
    }

    void Update()
    {
        if (_bBeingHit) {
            float temp = 1f - Mathf.Clamp01(_mElapsed / mRecoilDuration);
            mSpriteRenderer.color = Color.Lerp(Color.white, mHitColor, temp);

            transform.position += _mHitDirection * _mHitStrength * temp * temp * Time.deltaTime;

            _mElapsed += Time.deltaTime;

            if (_mElapsed >= mRecoilDuration) {
                _bBeingHit = false;
                _mElapsed = 0f;
                mSpriteRenderer.color = Color.white;
            }
        }

    }

    public void OnHit(float hit_strength, Vector3 hit_direction) {
        _mHitDirection = hit_direction;
        _mHitStrength = hit_strength;
        _bBeingHit = true;

    }
}

As a bonus, we are also changing the color of the Sprite when it is hit, fading the tint from red to the base white along with the pushback Pretty neat!
Now let’s see how it looks:

Showing off the recoil effect with a Sprite
And here's our little recoil effect in action!

Very nicely done, we now have a working recoil effect! You’ll just need to add a link your collision map, or check for pitfalls and you’ll have recreated an important mechanic for ARPGs, congratulations!