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 Digital Aficionado