134 lines
6.0 KiB
Markdown
134 lines
6.0 KiB
Markdown
|
# Manipulating the Stack
|
||
|
|
||
|
## Quick Volumes
|
||
|
|
||
|
While working on a game you'll often need to push effect overrides on the stack for time-based events or temporary states. You could dynamically create a global volume on the scene, create a profile, create a few overrides, put them into the profile and assign the profile to the volume but that's not very practical.
|
||
|
|
||
|
We provide a `QuickVolume` method to quickly spawn new volumes in the scene:
|
||
|
|
||
|
```csharp
|
||
|
public PostProcessVolume QuickVolume(int layer, float priority, params PostProcessEffectSettings[] settings)
|
||
|
```
|
||
|
|
||
|
First two parameters are self-explanatory. The last parameter takes an array or a list of effects you want to override in this volume.
|
||
|
|
||
|
Instancing a new effect is fairly straightforward. For instance, to create a Vignette effect and override its enabled & intensity fields:
|
||
|
|
||
|
```csharp
|
||
|
var vignette = ScriptableObject.CreateInstance<Vignette>();
|
||
|
vignette.enabled.Override(true);
|
||
|
vignette.intensity.Override(1f);
|
||
|
```
|
||
|
|
||
|
Now let's look at a slightly more complex effect. We want to create a pulsating vignette effect entirely from script:
|
||
|
|
||
|
```csharp
|
||
|
using UnityEngine;
|
||
|
using UnityEngine.Rendering.PostProcessing;
|
||
|
|
||
|
public class VignettePulse : MonoBehaviour
|
||
|
{
|
||
|
PostProcessVolume m_Volume;
|
||
|
Vignette m_Vignette;
|
||
|
|
||
|
void Start()
|
||
|
{
|
||
|
m_Vignette = ScriptableObject.CreateInstance<Vignette>();
|
||
|
m_Vignette.enabled.Override(true);
|
||
|
m_Vignette.intensity.Override(1f);
|
||
|
|
||
|
m_Volume = PostProcessManager.instance.QuickVolume(gameObject.layer, 100f, m_Vignette);
|
||
|
}
|
||
|
|
||
|
void Update()
|
||
|
{
|
||
|
m_Vignette.intensity.value = Mathf.Sin(Time.realtimeSinceStartup);
|
||
|
}
|
||
|
|
||
|
void OnDestroy()
|
||
|
{
|
||
|
RuntimeUtilities.DestroyVolume(m_Volume, true, true);
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
This code creates a new vignette and assign it to a newly spawned volume with a priority of `100`. Then, on every frame, it changes the vignette intensity using a sinus curve.
|
||
|
|
||
|
> **Important:** Don't forget to destroy the volume and the attached profile when you don't need them anymore!
|
||
|
|
||
|
## Fading Volumes
|
||
|
|
||
|
Distance-based volume blending is great for most level design use-cases, but once in a while you'll want to trigger a fade in and/or out effect based on a gameplay event. You could do it manually in an `Update` method as described in the previous section or you could use a tweening library to do all the hard work for you. A few of these are available for Unity for free, like [DOTween](http://dotween.demigiant.com/), [iTween](http://www.pixelplacement.com/itween/index.php) or [LeanTween](https://github.com/dentedpixel/LeanTween).
|
||
|
|
||
|
Let's use DOTween for this example. We won't go into details about it (it already has a good [documentation](http://dotween.demigiant.com/documentation.php)) but this should get you started:
|
||
|
|
||
|
```csharp
|
||
|
using UnityEngine;
|
||
|
using UnityEngine.Rendering.PostProcessing;
|
||
|
using DG.Tweening;
|
||
|
|
||
|
public class VignettePulse : MonoBehaviour
|
||
|
{
|
||
|
void Start()
|
||
|
{
|
||
|
var vignette = ScriptableObject.CreateInstance<Vignette>();
|
||
|
vignette.enabled.Override(true);
|
||
|
vignette.intensity.Override(1f);
|
||
|
|
||
|
var volume = PostProcessManager.instance.QuickVolume(gameObject.layer, 100f, vignette);
|
||
|
volume.weight = 0f;
|
||
|
|
||
|
DOTween.Sequence()
|
||
|
.Append(DOTween.To(() => volume.weight, x => volume.weight = x, 1f, 1f))
|
||
|
.AppendInterval(1f)
|
||
|
.Append(DOTween.To(() => volume.weight, x => volume.weight = x, 0f, 1f))
|
||
|
.OnComplete(() =>
|
||
|
{
|
||
|
RuntimeUtilities.DestroyVolume(volume, true, true);
|
||
|
Destroy(this);
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
In this example, like the previous one, we spawn a quick volume with a vignette. We set its `weight`property to `0` as we don't want it to have any contribution just yet.
|
||
|
|
||
|
Then we use the sequencing feature of DOTween to chain a set of tweening events: fade in, pause for a second, fade out and finally destroy the volume and the component itself once it's done.
|
||
|
|
||
|
And that's it. Of course you can also tween individual effect properties instead of the volume as a whole, it's up to you.
|
||
|
|
||
|
## Profile Editing
|
||
|
|
||
|
You can also manually edit an existing profile on a volume. It's very similar to how material scripting works in Unity. There are two ways of doing that: either by modifying the shared profile directly or by requesting a clone of the shared profile that will only be used for this volume.
|
||
|
|
||
|
Each method comes with a a few advantages and downsides:
|
||
|
|
||
|
- Shared profile editing:
|
||
|
- Changes will be applied to all volumes using the same profile
|
||
|
- Modifies the actual asset and won't be reset when you exit play mode
|
||
|
- Field name: `sharedProfile`
|
||
|
- Owned profile editing:
|
||
|
- Changes will only be applied to the specified volume
|
||
|
- Resets when you exit play mode
|
||
|
- It is your responsibility to destroy the profile when you don't need it anymore
|
||
|
- Field name: `profile`
|
||
|
|
||
|
The `PostProcessProfile` class has a few utility methods to help you manage assigned effects. Notable ones include:
|
||
|
|
||
|
- `T AddSettings<T>()`: creates, adds and returns a new effect or type `T` to the profile. Will throw an exception if it already exists.
|
||
|
- `PostProcessEffectSettings AddSettings(PostProcessEffectSettings effect)`: adds and returns an effect you created yourself to the profile.
|
||
|
- `void RemoveSettings<T>()`: removes an effect from the profile. Will throw an exception if it doesn't exist.
|
||
|
- `bool TryGetSettings<T>(out T outSetting)`: gets an effect from the profile, returns `true` if one was found, `false` otherwise.
|
||
|
|
||
|
You'll find more methods by browsing the `/PostProcessing/Runtime/PostProcessProfile.cs` source file.
|
||
|
|
||
|
> **Important:** Don't forget to destroy any manually created profiles or effects.
|
||
|
|
||
|
## Additional notes
|
||
|
|
||
|
If you need to instantiate `PostProcessLayer` at runtime you'll need to make sure resources are properly bound to it. After the component has been added, don't forget to call `Init()` on it with a reference to the `PostProcessResources` file as a parameter.
|
||
|
|
||
|
```csharp
|
||
|
var postProcessLayer = gameObject.AddComponent<PostProcessLayer>();
|
||
|
postProcessLayer.Init(resources);
|
||
|
```
|