Main Menu Audio Transition (FMOD)

First things first, using Unity 2021.3.20f1 with FMOD 2.02.13 as my audio manager.

I am working on setting up my game’s main menu, and I want to have a music transition after the player has selected to either play or quit the game.

In FMOD, I established a continuous parameter named CloseMenu with a range of 0 to 1 (0 being the normal state of the menu, and 1 being the transition). I setup the tracks with a loop for the normal state, and a one-shot clip for the transition. Added a destination marker named “CloseMenu” at the start of the transition clip, and a transition region (quantized to the quarter note) pointed to this marker, and set the transition conditions to the CloseMenu parameter range 1-1. (Screenshot 1 attached shows all of this). Tested it out within FMOD, and the transition works as it should.

Over to Unity, I have a Game Object called “containsScript” which is what I have my manageMenu script as well as the FMOD Studio Event Emitter attached to (Screenshot 2).

In the ManageMenu script, I set a public integer “endMusicTrigger” which is intended to be the trigger for the CloseMenu parameter in FMOD. I have the code set so that when the player clicks either the Start Game [loadMainGame()] or Exit Game [button_exit()] buttons, the code sets the endMusicTrigger integer from 0 to 1. And then I use code “menuMusic.setParameterByName(“CloseMenu”, endMusicTrigger);” to change the parameter within FMOD and trigger the transition. (Here’s the full code for the menu):

[code=CSharp]using System.Collections;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
using UnityEngine.SceneManagement;

public class manageMenu : MonoBehaviour
{
public FMOD.Studio.EventInstance menuMusic;
public Animator FadeAni;
private float timer;
private bool timerStarted;
public float delayFadeTime;
private bool startNewGame;
private bool exitGame;
public int endMusicTrigger;

void Start()
{
    timer = 0.0f;
    timerStarted = false;
    delayFadeTime = 4.0f;
    startNewGame = false;
    exitGame = false;
    endMusicTrigger = 0;

    menuMusic = FMODUnity.RuntimeManager.CreateInstance("event:/Music/Menus/MainMenu");
    menuMusic.setParameterByName("CloseMenu", endMusicTrigger);
}

void Update()
{
    if (timerStarted && startNewGame)
    {
        timer += Time.deltaTime;
        if (timer >= delayFadeTime)
        {
            timerStarted = false;
            SceneManager.LoadScene("Chapter1");
        }
    }

    if (timerStarted && exitGame)
    {
        timer += Time.deltaTime;
        if (timer >= delayFadeTime)
        {
            timerStarted = false;
            Application.Quit();
        }
    }
}

public void loadMainGame()
{
    FadeAni.SetBool("fadeOut", true);

    startNewGame = true;

    timerStarted = true;

    endMusicTrigger = 1;

    menuMusic.setParameterByName("CloseMenu", endMusicTrigger);

    print("Load Main Game");

}

public void button_exit()
{
    FadeAni.SetBool("fadeOut", true);

    exitGame = true;

    timerStarted = true;

    endMusicTrigger = 1;

    menuMusic.setParameterByName("CloseMenu", endMusicTrigger);
    print("Exit Game");
}

}
[/code]

In theory, I believe I’ve set everything up correctly, but for some reason when I click on the the Play Game button, the endMusicTrigger integer successfully changes to 1, but the CloseMenu parameter remains at 0 (Screenshot 3).

Not getting any console errors to cue me into what I’m missing, and everything I’ve read up on says that this should be effective, so hoping someone may have a clue as to what I’ve done wrong (I’m new to Unity, FMOD, and scripting, and self-taught, so I’m sure it’s probably something stupid I just overlooked, lol).

Screenshot 2:

Screenshot 3:

Hi,

The issue appears to be that you’re creating two event instances:

  • One event instance is created by you in your manageMenu class when you call menuMusic = FMODUnity.RuntimeManager.CreateInstance("event:/Music/Menus/MainMenu");, and is never played

  • One event instance is created by the Studio Event Emitter component, and is played on “Object Start”

In this case, the event instance belonging to the Studio Event Emitter is the one that is playing, but you’re setting the CloseMenu parameter on the event instance that is created (and never played) in your script.

If you want to keep using the Studio Event Emitter component, instead of creating your own event instance you’ll want to access the Emitter’s event instance by getting a reference to the StudioEventEmitter, and then accessing the EventInstance member with menuMusic = this.GetComponent<StudioEventEmitter>().EventInstance;

Alternatively, you can get rid of the Studio Event Emitter and handle the event instance in your own code, in which case all you should have to do to is start the event instance with menuMusic.start(); after creating it.

The second option you mentioned [using menuMusic.start(); without an Event Emitter] was actually the first thing I tried, but when I did that it A) still didn’t trigger the transition, and B) the menu music kept playing after the scene had loaded, so I added the Event Emitter as at least with that it stopped playing after the main scene had loaded.

I just now added “menuMusic.stop(STOP_MODE.ALLOWFADEOUT);” into the codes for the two timers in the Update Function [(timerStarted && startNewGame) and (timerStarted && exitGame)], and that fixed the second problem, but I’m still not getting the transition to trigger. I confirmed that the End Music Trigger integer is changing in the inspector, but for some reason it’s still not translating that to the FMOD event.

I guess ultimately what I could do is put the menuMusic.stop code into the loadMainGame and button_exit functions instead of the timers, and then have a separate one-shot event start, but I’m sure that the way I was trying to do it would be more efficient if I can get it to work!

In trying to test out your first suggestion (keeping the Studio Event Emitter), I’m not entirely sure how to go about “getting a reference to the StudioEventEmitter.” (Sorry, I know that’s probably a REALLY basic thing, but like I said, I’m self taught and very new to everything here).

Just to clarify, if you play your event in FMOD Studio, and change the parameter there, does the event behave as expected?

If it does, can you please rebuild your banks (just case the current banks don’t have your event behavior in them), and then do the following and see whether anything stands out as unexpected:

  • Wrap your call to menuMusic.setParameterByName("CloseMenu", endMusicTrigger); with Debug.Log() - most FMOD API calls will return a result which will indicate what the issue is if there is one
  • Add the following to Update() to ensure that the parameter is in fact getting set correctly:
menuMusic.getParameterByName("CloseMenu", out float paramValue);
Debug.Log(paramValue);

It’s not a problem. By getting a reference to the StudioEventEmitter, I mean calling getComponent<StudioEventEmitter>(); from your own script, which will return a reference to the first StudioEventEmitter component on the GameObject. You can also have a public StudioEventEmitter member in your class, and manually assign the Emitter component to it in the Unity inspector.

As an aside, you’ll want to make sure to call .release() on your event instance (i.e. menuMusic.release()) when you no longer expected to be doing anything with it, as this allows the FMOD System to clean it up when it has stopped.

Thank you for the clarification. Yes, when I change the parameter manually from within FMOD it plays as expected. Rebuilt the banks and added the Debug.Log to the update method, all that comes up is the log: “UnityEngine.Debug:Log (object)” which happens once at launch of the menu, and once when I click on the start game button.

Currently, the script stands as follows (//commented out the lines for starting the music from within the script, but I’ve tried it both ways with basically the same result):

using System.Collections;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
using UnityEngine.SceneManagement;
using FMOD.Studio;
using FMODUnity;

public class manageMenu : MonoBehaviour
{
public FMOD.Studio.EventInstance menuMusic;
public Animator FadeAni;
private float timer;
private bool timerStarted;
public float delayFadeTime;
private bool startNewGame;
private bool exitGame;
public int endMusicTrigger;

void Start()
{
    GetComponent<StudioEventEmitter>();
    menuMusic = this.GetComponent<StudioEventEmitter>().EventInstance;
    timer = 0.0f;
    timerStarted = false;
    delayFadeTime = 4.0f;
    startNewGame = false;
    exitGame = false;
    endMusicTrigger = 0;

    //menuMusic = FMODUnity.RuntimeManager.CreateInstance("event:/Music/Menus/MainMenu");
    //menuMusic.start();
    menuMusic.setParameterByName("CloseMenu", endMusicTrigger);        
}

void Update()
{
    GetComponent<StudioEventEmitter>();
    menuMusic = this.GetComponent<StudioEventEmitter>().EventInstance;
    menuMusic.getParameterByName("CloseMenu", out float paramValue);
    Debug.Log(paramValue);

    if (timerStarted && startNewGame)
    {
        timer += Time.deltaTime;
        if (timer >= delayFadeTime)
        {
            timerStarted = false;
            SceneManager.LoadScene("Chapter1");
            menuMusic.release();
        }
    }

    if (timerStarted && exitGame)
    {
        timer += Time.deltaTime;
        if (timer >= delayFadeTime)
        {
            timerStarted = false;
            Application.Quit();
            menuMusic.release();
        }
    }
}

public void loadMainGame()
{
    FadeAni.SetBool("fadeOut", true);

    startNewGame = true;

    timerStarted = true;

    endMusicTrigger = 1;

    //menuMusic.stop(STOP_MODE.ALLOWFADEOUT);

    print("Load Main Game");

}

public void button_exit()
{
    FadeAni.SetBool("fadeOut", true);

    exitGame = true;

    timerStarted = true;

    endMusicTrigger = 1;

    //menuMusic.stop(STOP_MODE.ALLOWFADEOUT);

    print("Exit Game");
}

}

Your script looks fine, so it’s difficult to diagnose exactly what the issue is. Instead, here’s a simplified version of your script that creates an event instance (you don’t need StudioEventEmitter) and just changes the parameter value when you press enter:

using FMODUnity;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
using UnityEngine.SceneManagement;

public class BasicSetParamTest : MonoBehaviour
{
    public FMOD.Studio.EventInstance menuMusic;
    public int endMusicTrigger;

    void Start()
    {
        endMusicTrigger = 0;
        menuMusic = FMODUnity.RuntimeManager.CreateInstance("event:/Music/Menus/MainMenu");
        Debug.Log("Result of setParameter in Start() = " + menuMusic.setParameterByName("CloseMenu", endMusicTrigger));
        Debug.Log("Result of staring event instance = " + menuMusic.start());
    }

    void Update()
    {

        Debug.Log("Result of getParameter = " + menuMusic.getParameterByName("CloseMenu", out float paramValue));
        Debug.Log("Current parameter value = " + paramValue);

        if (Input.GetKeyDown(KeyCode.Return))
        {
            loadMainGame();
        }
    }

    public void loadMainGame()
    {
        Debug.Log("loadMainGame() called");
        endMusicTrigger = 1;
        Debug.Log("Result of setParameter = " + menuMusic.setParameterByName("CloseMenu", endMusicTrigger));
    }

    private void OnDestroy()
    {
        menuMusic.release();
    }

}

I would recommend setting up a simple test scene using this script, and seeing whether your event behaves properly - my own timelime-based event that uses the parameter as a transition condition works as expected using this. If this doesn’t shed any light on the issue or resolve it for you, I would recommend uploading your Unity project (or a stripped down version of it) and your FMOD Studio project to the “Uploads” tab of your FMOD user profile so that I can take a closer look.

Also, you don’t need to call:

GetComponent<StudioEventEmitter>();
menuMusic = this.GetComponent<StudioEventEmitter>().EventInstance;

in Update().

The first line is returning a reference to the StudioEventEmitter, but you’re not storing it in a variable or calling a function with it, so it’s not doing anything. You don’t need this line at all.

The second line stores a reference to the EventInstance in the class member menuMusic, which means if you want to act on the EventInstance later, you can just use menuMusic - you only need to call the second line once in Start() to get the reference.

Mmkay, I tried the simplified script in a test scene, and hitting enter increased the integer in Unity, but still didn’t change the parameter in FMOD. Just submitted my project for approval so that I can post the uploads - once I get approved, I’ll do the uploads and let you know. Thank you again for all your input (and tolerance of my noobness, lol).

1 Like

While I’m waiting on the approval - would it be better to upload the whole project, or just an edited down version for just the parts pertaining to the issue I’m currently having?

Whatever is best for you is fine, as long as you’re able to consistently and easily repro the issue with whatever version of the project you upload.

1 Like

Thanks for uploading your FMOD Studio project.

I’ve tested the event with both my own script and your script (with the scene loading part removed), and the only way I’m able to reproduce the issue you describe is when I set the CloseMenu parameter before the “MainMenu” event’s timeline reaches its transition region - is it possible that you were setting CloseMenu without waiting for the requisite minute to pass so that the timeline’s playhead is within the loop/transition region?

1 Like

OMG, I knew it had to be something absolutely simple that I had overlooked, and yep, that was it. I extended the transition region within FMOD to cover the intro section, and that did the trick. Thank you so much for your patience and help with getting me through this! You have been amazing!

1 Like