Polishing The Game: Adding a Damage Effect and Camera Shake

Clint Wagoner
7 min readApr 2, 2023

--

Giving some ‘oomph’ to the damage system.

Damage graphic: https://www.vhv.rs/file/max/35/354430_thumbnail-effect-png.png

There are two parts to creating this effect. The first one is adding a visual to help convey damage to the player, while the second one is a simple camera jolt upon taking damage. This adds weight to when the player gets hit.

Adding the Damage Graphic

The first thing to do is to add the red streaks graphic. You can use any transparent image, but for mine, I just grabbed this image from vhv.rs:

Create an images folder in your sprite folder and drag your damage image into your project. Next, select the image and set the Texture Type to ‘Sprite (2D and UI)’ then hit ‘Apply’.

Next, right-click on your UI Canvas and select UI > Image. This will create a new image object in your canvas.

Give it a name like Damage or DamageVisual

Select the image object and open the Image component. From here you can change the color. I went with red.

Animating the Graphic

Now let’s create the animation for the Damage object. Open your Animation window (Window > Animation > Animation or Ctrl+6) and select the Damage object in your hierarchy. In the animation window select ‘Create’. Name your animation ‘DamageStreaks’.

Hit the record button in the upper left side of the properties pane. Your timeline should have a red highlight if you are recording. We are going to record the alpha channel in the color wheel. With record enabled open the color wheel and drag the alpha channel all the way down. Next, set the scale to 1.

Next, move the timeline playback to ‘3’ and set the alpha level to a visible level.

Finally, move the play bar to ‘35’ and bring the alpha channel back to zero. While we are here, let’s also change the scale to ‘2’. Hit play to see your handiwork.

I encourage you to play around with the animation until you find something that works for your game. Let’s double-check that the animation is not set to loop. Select your animation in your project folder and make sure that the ‘Loop Time’ function is set to off.

Lastly, go into your Animator window and create transitions to your new animation.

Create a new bool parameter and call it ‘TookDamage’.

I renamed my Default state to ‘Idle’, but you don’t have to. What we do need to do is create the transitions to and from the ‘DamageStreaks ’animation. Right-click on the default state and select ‘Make Transition’ and then select the DamageStreaks state. Then make another transition to go back to the default.

Now select the transition from default to DamageStreaks. In the conditions list, shown in the inspector, let’s add a condition. It will already be set to ‘TookDamage’ is equal to ‘true’. That is what we need for this condition, but let’s also remove exit time and fixed duration.

Let’s set the returning transition condition to ‘TookDamage’ is equal to ‘false’. And let’s also set it for no exit time or fixed duration.

Now all of our editor animation setup is done. Let’s script the animation to fire when the player gets hit.

Scripting the Damage Animation

In our UIManager script, let’s first add this new variable:

    [SerializeField] private Animator _damageStreakAnim;

Now hop back to the editor and assign the Damage object’s animator component to the new slot.

Back in our UIManager script, let’s now add these two methods. The first one sets the animator parameter ‘TookDamage’ to true. The second IEnumerator() method will wait for the duration of the animation and then set the ‘TookDamage ’bool back to false.

Notice the StartDamageStreaks() method is public. We will call this method from the ‘Player ’script when damage is taken.

    public void StartDamageStreaks()
{
_damageStreakAnim.SetBool("TookDamage", true);
StartCoroutine(StopDamageStreaks());
}

IEnumerator StopDamageStreaks()
{
yield return new WaitForSeconds(.35f);
_damageStreakAnim.SetBool("TookDamage", false);
}

Now, all we need to do is call the StartDamageStreaks() method from the ‘Player ’ script in the method where we take damage. This requires we find the UIManager script in the Start() method.

public class Player : MonoBehaviour
{
private UIManager _uiManager;

void Start()
{
_uiManager = FindObjectOfType<UIManager>();
if (_uiManager == null) Debug.LogError("_uiManager is NULL");

}
}
    public void Damage(int damage)
{
_health -= damage;
_uiManager.StartDamageStreaks(); // Here is where I call the method

}

And now you should see the streaks whenever you run your Damage() function. Now, let’s make the camera shake as well.

Making the Camera Shake

Now that the hard part is done, let’s make the effect even more impactful with a little camera shake. Create a new C# script in your scripts folder and call it ‘CameraShake’. Next, attach it to your Main Camera object.

This is a pretty simple script, so I am just going to add the whole thing here and break it down with comments.

using UnityEngine;

public class CameraShake : MonoBehaviour
{
//Which camera are we shaking and where is it at?
[Header("Camera Information")]
private Transform _cameraTransform;
private Vector3 _originalCameraPos;

//How long should we shake it and how hard?
[Header("Shake Parameters")]
[SerializeField] float _shakeDuration = 2f;
[SerializeField] float _shakeAmount = 0.7f;

//Is the camera allowed to shake?
private bool _canShake = false;
private float _shakeTimer;


private void Start()
{
//Find the scene camera
_cameraTransform = GetComponent<Transform>();
}

void Update()
{
//Keep tabs on the camera position, so that
//when we shake the camera we know where to set it back to
_originalCameraPos= transform.localPosition;

//If canShake is turned on, shake the camera
if (_canShake) StartCameraShakeEffect();
}

//Method we will call from the Player script
//to set the canShake bool to true
public void ShakeCamera()
{
_canShake = true;
_shakeTimer = _shakeDuration;
}

//Called from Update() if canShake is true.
void StartCameraShakeEffect()
{
if (_shakeTimer > 0)
{
//Shake the camera
_cameraTransform.localPosition = _originalCameraPos+ Random.insideUnitSphere * _shakeAmount;
//Countdown the shake timer
_shakeTimer -= Time.deltaTime;
}
else
{
// If the shake timer gets to 0, stop shaking and replace the camera
// to where it was before we started shaking
_shakeTimer = 0f;
_cameraTransform.position = _originalCameraPos;
_canShake = false;
}
}
}

For a quick overview of ‘Random.insideUnitSphere’ check out the Unity API.

Lastly, let’s call the ShakeCamera() method when we get hit. Back in the Player script, let’s add this line to our damage function, just underneath the DamageStreaks call. We also need to get a reference to the ‘CameraShake ’script.

public class Player : MonoBehaviour
{
private UIManager _uiManager;
private CameraShake _shake;

void Start()
{
_uiManager = FindObjectOfType<UIManager>();
if (_uiManager == null) Debug.LogError("_uiManager is NULL");

_shake = FindObjectOfType<CameraShake>();
if (_shake == null) Debug.LogError("_shake is NULL");

}
}
public void Damage(int damage)
{
_health -= damage;
_uiManager.StartDamageStreaks();
_shake.ShakeCamera(); // Calling shake method here
}

And there you have it! We have an awesome new damage effect, complete with a damage graphic and camera shake! Now tweak away to make it your own!

Thanks for reading!

--

--