Modifying Unity’s Camera ImageEffects values at runtime

5 July 2016

A couple of months ago I was mussing around integrating noise, vigenette, chromatic aberration, and other image effects into Astervoid 2000.

I found it wasn’t enough to just throw the image effects on the camera, I also wanted to change them at runtime depending on various events happening in the game.

Example of chromatic aberration offset & bloom intensity changing over time.

While coming up with a solution, I didn’t find this as straightforward as I had hoped it would be in Unity, and wanted to document and share the technique I devised for fellow game devs to use.

I started with the interface I wanted our team to use. Essentially, it’d be nice to call a method from anywhere in our codebase (an event handler, when a certain GameObject shows up, etc). I came up with a 1-liner solution like so:

// Kick up noise intensity to 1.5 over 0.8 seconds DynamicCameraEffects.BringTheNoise(newIntensity: 1.5f, duration: 0.8f);

`DynamicCameraEffects` is a C# class inheriting from MonoBehaviour that is added to camera. This camera also needs Unity’s ‘Noise And Grain’ ImageEffect from StandardAssets.

Once the noise has hit it’s desired effect, the callback `RemoveNoise()` pulls out the effect after a random amount of time has passed.

One gotcha, I needed to reference the Unity ImageEffects namespace via `using UnityStandardAssets.ImageEffects;`

Also, we rely heavily on DOTween to simplify changing values over time (Similar to MCTween for Flash, ‘member that?). It has a ton of helpers to clean up your code and provides a bunch of easing equations, but you could also re-write this with a typical LERPing implementation.

The whole class for affecting Noise looks something like this:

using DG.Tweening; // remove this if you're not using DOTween using System.Collections; using UnityEngine; using UnityStandardAssets.ImageEffects; ///  /// An extension class for dynamically adding noise to the camera easily at runtime ///  public class DynamicCameraEffects : MonoBehaviour { private static NoiseAndGrain _noiseScript; private void Start() { _noiseScript = GetComponent(); } public static void BringTheNoise(float newIntensity, float duration) { DOTween.To(() => _noiseScript.intensityMultiplier, x => _noiseScript.intensityMultiplier = x, newIntensity, duration) .SetEase(Ease.OutCirc) .SetUpdate(true) .OnComplete(RemoveNoise); } private static void RemoveNoise() { var duration = Random.Range(0.2f, 1f); DOTween.To(() => _noiseScript.intensityMultiplier, x => _noiseScript.intensityMultiplier = x, 0.45f, duration) .SetEase(Ease.InQuad) .SetUpdate(true); } } 

Download/Gist - Raw

You could control the effect manually by deleting the `RemoveNoise()` function, and the `OnComplete(RemoveNoise)`.

This also makes it easier to create effects loops where every X number of seconds effects are added and pulled off the camera, breathing life into your game and making every playthrough unique.

Happy game dev'ing!

Chuck Bergeron's photo
Chuck Bergeron Tech-infused Cosmonaut