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.
  • ContributeToTotalDuration : whether to count this feedback in the parent MMFeedbacks(Player) total duration or not
  • 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.

How to play a feedback in reverse?

  • select your MMF_Player (or MMFeedbacks), and in its Settings foldout, set Direction to BottomToTop
  • you can also force that via code :
    MyPlayer.Direction = MMFeedbacks.Directions.BottomToTop;
    MyPlayer.PlayFeedbacks();
    
  • you can learn more about direction options in the dedicated section of the documentation

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.

Alternative ways to play a feedback

While most of the time you’ll probably want to play feedbacks via code, there are other ways. The following recipes cover some of them :

How to play a MMFeedbacks from an animation using Animation Events?

  • in Unity 2019.4.30f1, create a new project using the standard RP
  • from the Package Manager, import Feel v3.0
  • create a new scene
  • create a new cube, position it at 0,0,0
  • add a MMFeedbacks component to the cube, add a Sound feedback to it, set any audio clip in its Sfx slot, like FeelDuckQuack for example
  • add a new Animator component to the cube
  • create a new animator controller, call it CubeAnimatorController, drag it on the Animator component in its Controller slot
  • open the Animation panel, create a new animation, call it BouncyCube
  • add a new property to the animation, Transform:Position, press Record
  • add a key at frame:0, with position to 0,0,0
  • add a key at frame 20, with position 0,1,0
  • copy frame 0, paste it at frame 60, you now have a cube that goes up and down
  • put the cursor at frame 25, and press the AddEvent icon (right below the frame counter), in the inspector, in the Function dropdown, select PlayFeedbacks()
  • press Play in the editor, your cube will bounce, and play a sound every time its animation passes frame 25. From there you can of course add and customize more feedbacks

How to trigger MMFeedbacks via the Timeline

  • in Unity 2019.4.26f1, create a new project
  • from the Package Manager, import Feel v3.0
  • for this example we’ll open the FeelDuck demo scene
  • create a new empty object, call it Director
  • open the Timeline panel (Window > Sequencing > Timeline)
  • in the Timeline panel, press the Create (a director) button, call the new asset TimelineDirector, save it in your project
  • drag the FeelDuckBackground into the Timeline, and pick “Add activation track”
  • copy the Active block, move the new one to the right, you should now have two Active blocks, maybe with a bit of a gap between them, that’s our Timeline animation (it’ll activate/disable/activate the background, of course you may want to do more fancy stuff in your own Timeline, that just gives us a track to play)
  • press the little pin icon under the frame count to reveal our markers track
  • around frame 120, right click in the marker track and select “Add signal emitter”
  • in the inspector, click on Create Signal, name the new asset “TestMMFeedbacks.signal”, save it in your project
  • in the inspector, click on “Add signal receiver”
  • select your Director object, and in its inspector, drag the FeelDuckJumpStartFeedback into the SignalReceiver’s event reaction slot, select its MMFeedbacks > PlayFeedbacks() method as its reaction
  • press play in the editor, then press play in your timeline, your feedback will play everytime the play head passes the marker
  • don’t hesitate to check https://blog.unity.com/technology/how-to-use-timeline-signals to learn more about how Timeline and Signals work

How to trigger a MMFeedbacks with Playmaker?

Note : there are plenty of other ways to do it, and this will also (most of the time) be the same general logic for Bolt and other visual scripting tools.

  • create a new project in Unity 2019.4.28f1
  • import Feel v3.0
  • install Playmaker
  • in a new scene, create an empty object, call it MyFeedbacks, add a MMFeedbacks component to it
  • add a Debug Log feedback to it, set its Debug Message to “Hello!”
  • open the Playmaker editor, right click in it to add a FSM
  • rename that first state to “GettingInput”, and using the Action Browser, add a GetKeyDown action, set its key to F2
  • in the Send Event dropdown, select New Event, name it “F2Pressed”, and press the CreateEvent button, then click on the red label to add transition to state
  • right click in the FSM to add a new state, call it “PlayingFeedbacks”
  • right click on the first state’s F2Pressed event, set its transition target to PlayingFeedbacks
  • right click on the PlayingFeedbacks state, add transition > Finished, then right click on Finished and set its transition target to GettingInput
  • in the PlayingFeedbacks state, using the Action Browser, add a CallMethod action, drag MyFeedbacks’ MMFeedbacks monobehaviour into its Behaviour slot, and in the Method dropdown, select “void PlayFeedbacks()”
  • press play in your Unity editor, now every time you’ll press F2, the feedback will play, and “Hello!” will appear in the console. Any other feedbacks you’d add to your MyFeedbacks MMFeedbacks will of course also play
Jekyll
The PlayingFeedbacks state

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);
    }
}

How to change a feedback's property via script at runtime?

For plenty of reasons, you may want to change a property or more on a feedback at runtime. It’s of course possible, and in this example we’ll see how we can change the intensity of a Chromatic Aberration feedback in the Duck demo. It’d be the exact same logic for any other property, on any other feedback.

  • in Unity 2019.4.28f1, create a new project
  • from the Package Manager, import Feel v3.0
  • open the FeelDuck demo scene
  • create a new, empty gameobject, call it RuntimeTest
  • outside of the Feel folder, create a new C# script, call it RuntimeTest, and paste the following in it :
using MoreMountains.Feedbacks;
using MoreMountains.FeedbacksForThirdParty;
using MoreMountains.Tools;
using UnityEngine;

public class RuntimeTest : MonoBehaviour
{
    public MMFeedbacks TargetFeedback;
    [Range(-100f, 100f)]
    public float Intensity = 0f;
    [MMInspectorButton("TestFeedback")]
    public bool PlayFeedbackBtn;

    private MMFeedbackLensDistortion _lensDistortion;
    // IMPORTANT : if you're using a MMF Player, replace the line above with this:
    // private MMF_LensDistortion _lensDistortion;

    private void Start()
    {
        // on start we store our lens distortion feedback component, so that we can target it later on
        _lensDistortion = TargetFeedback.GetComponent<MMFeedbackLensDistortion>();

        // IMPORTANT : if you're using a MMF Player, replace the line above with this:
        // _lensDistortion = TargetFeedback.GetFeedbackOfType<MMF_LensDistortion>();
    }

    public void TestFeedback()
    {
        // we modify our RemapIntensityOne value before playing our feedback.
        // This is done like for any other public attribute on any component in Unity
        if (_lensDistortion != null)
        {
            _lensDistortion.RemapIntensityOne = Intensity;
        }
        // we then play our MMFeedbacks
        TargetFeedback?.PlayFeedbacks();
    }
}
  • then add that script to your RuntimeTest game object
  • select your RuntimeTest object, and drag the FeelDuckLandingFeedback object from your Hierarchy panel into the RuntimeTest’s TargetFeedback slot in its inspector
  • press play in the editor, then select your RuntimeTest object, change the intensity value in your inspector, and press the TestFeedback button, every time you’ll play, the value you’ve changed in the inspector will be sent to the feedback before it plays

One important thing to keep in mind when changing values at runtime is that some of them will get cached for performance reason. So always double check that the value you’re interacting with doesn’t get cached at some point, and that you’re targeting the correct property, or resetting the cache if needed.

Preventing a type of feedback from playing

Sometimes you may want to make sure a certain type of feedback can’t play. Maybe you’d like to disable all screen shakes at once, or make sure no lens distortion feedback will play, maybe that’s tied to a setting in your options menu, etc. To do so is super simple, and can be done like this :


// this will prevent all scale feedbacks, anywhere, from playing
MMFeedbackScale.FeedbackTypeAuthorized = false;

Of course you can do that for all types of feedbacks, not just Scale.

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”.