This section covers a few core concepts you'll encounter in most feedbacks, if not all.

Shakers

While some feedbacks will directly interact with an object, some others broadcast an event to listeners. The Bloom feedback, the AudioSource volume, or the Cinemachine Transition feedbacks for example all work like that. That listener is called a Shaker, as it will “shake” the target value on the receiver’s end. On each feedback’s inspector, you’ll usually get a mention that you need one. It’s also explained in details in the API documentation, the code comments, or the list of feedbacks.

To get more familiar with shakers, let’s see a few examples, from simple ones to more complex ones. Note that most shaker based feedbacks work exactly like that, what will differ is how much settings you’ll have at your disposal on both ends, shaker and feedback.

If you want to try these, you’ll need a scene with the following :

  • a camera, and a sphere in front of it
  • an empty object, to which you add a PostProcess Volume (or its URP equivalent if you prefer), set as IsGlobal, and add a new profile with Vignette on
  • on the camera, add a PostProcess Layer, and in Layer select Everything
  • in the scene, an empty object, with a MMFeedbacks script added to it

Adding a Post Processing feedback

  • on the PostProcess Volume you have, add a MMVignetteShaker
  • on the MMFeedbacks, click Add > Post Process > Vignette
  • press play (in Unity), play the MMFeedbacks

Adding a Camera Shake feedback

  • select the camera, add a MMCameraShaker
  • in its MMWiggle inspector, check Position (optionnally you could tweak rotation and scale), unfold Position, uncheck WigglePermitted, and in the WiggleType dropdown, pick Noise
  • create an empty object at the root of your scene, add an MMFeedbacks to it, add a Camera > Camera Shake feedback to it
  • press play (in Unity), then press play on your feedback (at the bottom of the MMFeedbacks inspector, under All Feedbacks Debug)
  • the camera shakes, you can then play with the camera shake values to get different effects

Pauses

By default all feedbacks in a MMFeedbacks are executed at the same time. Sure, each of them can have different internal timing settings, an initial delay, etc, but they’ll all get started at the same time. They may end up being visible or active at different times, but they’ll all start together.

If you introduce one or more pause though, feedbacks now get played sequentially, from top to bottom.

There are two types of pauses, and you’ll be able to compare them in the demo :

Pause

Jekyll
The Pause feedback

This MMFeedbacks contains 5 feedbacks, then a Pause, then two more feedbacks. When playing this MMFeedbacks, all feedbacks from the top up to (and including) the pause will play at the same time. The two remaining ones will play after the duration of the Pause (0.5s in this case). This effectively means that the two last feedbacks, Sound and Scale, will start 0.5s after the play of this MMFeedbacks.

Holding Pause

Jekyll
The Holding Pause feedback

This MMFeedbacks has the same setup as the one above, but this time the Pause has been replaced with a Holding Pause. A Holding Pause also waits for its entire duration, but will wait until all the feedbacks above it in the sequence have finished. In this case, all the ones above it start together, which means that this holding pause will wait for the duration of the slowest feedback above it, so 2s in this case. The holding pause itself has a duration of 0.25, so the last two feedbacks will run 2.25s after the play of this MMFeedbacks.

These two types of pause can be really useful, and once you’ve understood the difference between both, it’ll become super easy to create complex patterns and sequences.

Script Driven Pause

You can also drive pauses via script. In any MMFeedbacks containing pauses and/or loops, you can call the MMFeedbacks Pause() method, and execution will be paused until you call the MMFeedbacks Resume() method. You can also experiment with these via the Pause button available on any MMFeedbacks inspector. If a pause is script driven, you have an option to set it to auto resume after a certain duration (in seocnds). This lets you create interesting setups, where something will happen, either automatically after X seconds, or sooner if you call Resume() on your MMFeedbacks.

Loops

Aside from the repeat options in the Timing section of each feedback, the system also lets you define “loops” at the MMFeedbacks level. You do so by adding “Loopers” feedbacks in your sequence. When met, they’ll move the play head back up, either to the last encountered pause, or the last encountered loop start.

Jekyll
The Loop feedback

In this case, the AudioSource, Scale and Pause feedbacks will play, then the Sound, Position and Scale will play. Then we have a loop, so it’ll go back to the Pause, then Sound, Position and Scale again. It’ll do that twice, as that looper specifies that 2 loops should be played, and then it’ll end.

Jekyll
The Loop Start feedback

This one is a relatively similar setup, but now there’s a Loop Start before the pause. The feedback will loop between the Looper and the Loop Start, replaying the Sound and Scale feedbacks for the specified amount of loops.

Channel

On pretty much every feedback with a Shaker of some sort, you’ll find a Channel property. This lets you specify a unique Channel to broadcast this feedback on. Shakers also have a Channel property, and just like walkie-talkies, only the Shakers with a matching channel will be controlled by that specific feedback. This is useful to, for example, only shake the Bloom of a specific screen in a split screen setup, or lower the volume of multiple audiosources at once while keeping other unaffected.

Chance

Every feedback has a Chance slider, letting you define its chance to happen, from 0 (will never happen) to 100 (will always happen). That value is always 100 by default. A feedback with a 25% Chance value will only run once every 4 plays (statistically).

Timing

On every feedback you’ll find a Timing foldout, towards the top of its inspector. Unfolding it will give you access to the following options :

  • TimeScale Mode : scaled or unscaled, will let you define if the feedback should run on scaled or unscaled time. This is very situational, and doesn’t apply to all feedbacks.
  • Initial Delay : a delay, in seconds, to apply before executing this feedback
  • Cooldown Duration : the time, in seconds, after a Play during which the feedback can’t be played again (useful to prevent user controlled feedbacks from being spammed)
  • ExcludeFromHoldingPauses : this setting lets holding pauses ignore this feedback. This can be useful when dealing with a particularly long feedback, and you just want it to start but not delay the rest of the sequence.
  • Number of Repeats : the amount of times this feedback should repeat (0 by default)
  • Repeat Forever : check this checkbox to have your feedback repeat forever
  • Delay Between Repeats : the delay, in seconds, that should pass before that feedback gets played again when in repeat mode
  • Play Direction : this section lets you define how this particular feedback should react when its parent MMFeedbacks is played in certain directions (from top to bottom by default, or in reverse, from bottom to top)
  • MMFeedbacks Direction Condition : lets you define whether this feedback will always play, or only when the parent MMFeedbacks is played in reverse or forwards (the default)
  • Play Direction : lets you define whether this feedback should play always in normal mode, in reverse, in the direction of its host, or in opposite direction of its host. Most feedbacks have the possibility to “play in reverse”. For example, a Position Feedback that takes an object from point A to point B will take it from B to A when played in reverse.
  • Constant Intensity : when playing, a MMFeedbacks calls its children MMFeedback with an optional Intensity, a multiplier that lets you modulate how “strongly” feedbacks play. If you check constant intensity, this feedback will ignore that global modifier.
  • Use Intensity Interval : if checked, will let you define a min and max intensity, within which this feedback will play. Intensities outside of this range will not trigger this feedback. This is useful if you have one MMFeedbacks that should result in different outcomes based on its intensity. For example, imagine a game where your Character can get damaged, and you want it to flicker in red when getting damaged heavily, and in yellow when getting damaged a little. A “getting damaged” MMFeedbacks could have a ShaderController feedback on it, that colors the damaged character in yellow only when intensity is between 0 and 50, and another ShaderController feedback, that triggers when intensity is between 50 and 100.
  • Sequence : a Sequence to use to play this feedback
  • Track ID : the ID to use on the Sequence
  • Quantized : whether or not the Sequence should be played in quantized mode

You can also change Timing values at runtime, using the SetInitialDelay, SetDelayBetweenRepeats or SetSequence methods.

Enable / Disable feedbacks

If you wish to prevent feedbacks from playing, there are multiple ways you can do so :

  • at a global level, you can target the MMFeedbacks.GlobalMMFeedbacksActive static switch, setting it to false will prevent all and any feedback from playing
  • at the MMFeedbacks level, MMFeedbacks won’t play if they’re disabled in any way (mono, game object…)
  • at the MMFeedback (no “s”) level, you can check or uncheck the checkbox next to the MMFeedback name to enable/disable it. You can also check its Active checkbox.

Remap

On many feedbacks you may come across “remapped” values. These are used when working with animation curves. Let’s take the example of the Bloom feedback. It lets you control (among other things) the intensity of the bloom across time. You define how much that time should be via the Shake Duration field. And you’ve got an animation curve that lets you define how it should vary over that period of time. That animation curve will go from 0 to 1 on the x axis. And while technically you could set any sort of y value on it, it’s usually good practice to keep it normalized (between 0 and 1). That’s where remapped fields come in. On the Bloom feedback, you can define RemapIntensityZero and RemapIntensityOne values, which are respectively the values that should correspond to the 0 y value of the curve and the 1 value of the curve. If you pick a bell curve, and set them to 0 and 1 (the default values), your bloom will go from 0 to 1 to 0. If you remap to 1 and 5 your bloom will go from 1 to 5 and back to 1.

Relative Values

Many of the various feedbacks included in the asset make a value vary over time. Whether it’s bloom intensity, audiosource pitch, or an object’s rotation, it’s a common thing you’ll encounter a lot. Most of these let you work (optionally) with “relative values”. If you decide to do so, the feedback will add whatever values you’ve defined to the target’s initial values.

Additive Plays

On many feedbacks you’ll find a “AllowAdditivePlays” option. If this is true, calling that feedback will trigger it, even if it’s in progress. If it’s false, it’ll prevent any new Play until the current one is over.

Editing values at runtime

You can target a MMFeedback using GetComponent, then cache it (it’s usually a good idea), and modify its values at runtime, maybe based on the force of a hit, or the speed of your vehicle.

In this example, we grab a distortion filter and modify one of its values at runtime. In this case we do it on Update, but of course you could do it only once before triggering your feedback, or however you want.


using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using MoreMountains.Feedbacks;

public class RealtimeAdjustment : MonoBehaviour
{
    /// the MMFeedbacks on which to grab and modify a distortion filter
    public MMFeedbacks TargetFeedbacks;

    protected MMFeedbackAudioFilterDistortion _distortion;

    /// <summary>
    /// On Awake we store our distortion filter
    /// </summary>
    protected virtual void Awake()
    {
        _distortion = TargetFeedbacks.GetComponent<MMFeedbackAudioFilterDistortion>();
    }

    /// <summary>
    /// On Update we make our distortion's remap one value oscillate
    /// </summary>
    protected virtual void Update()
    {
        _distortion.RemapDistortionOne = Mathf.Sin(Time.time);
    }
}

Disabling Help Texts

In most feedbacks you’ll find help text boxes in the inspectors, explaining how to use the feedbacks. If you want to disable them, you can select the MMFeedbacksConfiguration asset located at MMFeedbacks/Editor/Resources, and uncheck “Show Inspector Tips”.