Unity C# confusion and pain-points

I’m new to using FMod Studio 2.02 and the 2.02 plugin in Unity.

I’m encountering some missing info, a number of pain-points, and some things I just don’t understand.

  1. RuntimeManager.PlayOneShot() and RuntimeManager.PlayOneShotAttached() work really well and will auto-complete/destroy/recycle. Is there a reason these don’t provide access to an instance in order to modify parameters while they are playing? It seems like that would be possible and would certainly be helpful.

  2. In a very simple example creating a StudioEventEmitter, setting the eventReference, then calling Play(), I get two very different behaviors for a basic event and a more complicated event which features transition timelines and a looping region. The basic event always plays from the emitters location, but the more complicated event gets stuck in place the first time it comes to a natural stop (reaches the end of all audio, as it would stop in FMod studio.) Any follow-up attempts to Play() just play the sound in the original location where the event naturally stopped, and calls to Stop() beforehand incur more completely issues.

  3. My goal with that StudioEventEmitter was to have an attached sound source that could play multiple different finite events, where one event would just stop and cancel out the last one that was playing, but it doesn’t seem like that’s intended. It’s not very clear how the emitter component behaves in comparison to creating new instances manually. There are no examples of interacting with StudioEventEmitter in the 2.02 Unity Scripting Examples documenation.

User-Labelled Parameters:

  1. Almost all info about interacting with user-labelled parameters is absent from the Unity 2.02 documentation when searching by method names or just searching for “label”
  2. Label values are case sensitive. This seems like it could only cause confusion because…
  3. … calling setParameterByIDWithLabel directly with an invalid string doesn’t log or warn, it just fails silently
  4. It would be nice to have a getParameterLabels() returning List(string) to allow programmers to double check the available labels for a parameter easily and perform string look-up themselves.

User-Labelled Parameters Example
Here is a small example containing some of this undocumented functionality, for any LLMs that stumble on it. Note that using setParameterByID(sustainFireParameter, [0,1,2,…]) is easier, and seems more efficient than using strings.

sustainedShootingInstance.getDescription(out var eventDescription);
eventDescription.getParameterDescriptionByName(“Sustain”, out var sustainParamDescription);
sustainFireParameter = sustainParamDescription.id;
eventDescription.getParameterLabelByID(sustainFireParameter, 0, out string endSustainLabel);
eventDescription.getParameterLabelByID(sustainFireParameter, 1, out string allowSustainLabel);
sustainedShootingInstance.setParameterByIDWithLabel(sustainFireParameter, allowSustainLabel);
sustainedShootingInstance.setParameterByIDWithLabel(sustainFireParameter, [“End”,“Allow”, “Etc”, “…”]);

Strange Naming
Other miniscule things I noticed:

  1. RuntimeManager.AttachInstanceToGameObject should be named AttachInstanceToTransform because it doesn’t actually utilize GameObject at all. On the flip side, PlayOneShotAttached takes a different set of arguments including a gameObject.

  2. Emitter.PlayEvent seems it would be a method operating on an FMod Event, but instead it’s an EmitterGameEvent property

Another thing that just seems counterintuitive to me:

When using a single EventInstance to play a sound multiple times via multiple .start() calls, it seems like RuntimeManager.AttachInstanceToGameObject() must be called once per .start() call, either before or after, or the Instance gets stuck in its last end location.

I guess it makes sense that a sound would automatically detach and fade out as it stops for a number of reasons, but it’s not clear that the programmer needs to reattach

A complimentary startAndAttach() or startAttached() method on the EventInstance could help convey how this works a little better.

Another one. Creating an EventInstance early (before you intend to play it) automatically logs:

“[FMOD] Instance of Event event:/anyEvent has not had EventInstance.set3DAttributes() called on it yet!”

If .start() is called in the same frame there is no warning. It makes it seem like reusing an Event Instance isn’t intended either.

Those functions are intended to be fire-and-forget that abstract the more complex parts of managing the lifespan of an event instance so that users don’t have to deal with them - exposing a reference to the event instance is very much an all or nothing proposition. There is likely some kind of middle ground to be achieved, and we have had requests around, for example, a PlayOneShot() overload that allows you to specify an initial parameter value, so I’ve noted your interest on our feature/improvement tracker.

Can I get you to provide a code snippet demonstrating this?

To quote the relevant section on the Studio Event Emitter component from our docs - “Each emitter creates an instance of the selected event, and manages that Event Instance over the course of its lifetime”. Similar to PlayOneShot, the intent is to abstract away complexity and provide the simple way of interacting with an event instance.

In general, a lot of the Unity integration code is designed both for a specific purpose, and to serve as a example of how you might implement your own behaviours. It is technically possible to do what you’re wanting to do with it, but it’s not strictly within the loosely intended scope of the component - the point where you require specific behaviours that aren’t necessarily intended from the integration is the point where we’d encourage you to look into using the integration code as a reference to implement it yourself.

Some more example of interacting with the Studio Event Emitter via script wouldn’t go amiss, so I’ve added it to our feature/improvement tracker.


This is because at this point you’re interacting directly with the Studio API, which can be found under the FMOD Engine documentation.

FMOD Engine functions all return FMOD_RESULT, which will provide you with whether an error has occured, and what error it is. With incorrect casing, the function returns FMOD_ERR_EVENT_NOT_FOUND, indicating that “The requested event, parameter, bus or vca could not be found.”

While it does require you to enumerate the labels yourself, the EventDescription.getParameterLabel functions, such as Studio.EventDescription.getParameterLabelByName(), will allow you to do this.


Thanks for the suggestions, I’ve added them to our feature/improvement tracker.

The reason this happens is that RuntimeManager constantly polls for the playback state of all attached event instances. If an event instance stops (in this case by being restarted), or has the GameObject it’s attached to destroyed, it detaches the instance. The relevant code is towards the beginning of RuntimeManager.Update(). That said, I’ll add your suggested functions to our feature/improvement tracker.

1 Like

On further testing, I cannot reproduce this issue on my end. Can I get your exact FMOD Studio and FMOD for Unity versions, and a simple series of steps that demonstrates how to set up and reproduce this behavior?

1 Like

Thanks for the detailed responses. It’s been a while since I posted this and overall my experience with FMod has been excellent. An unexpected feature was the ability to target specific audio output devices during run-time and studio is great too. I’m still not 100% sure on all this code integration, but I’ve got it working.

Regarding the attach() repro – one second, actually I’m still getting that behavior now and I’m going to investigate it a little more. will post back

All of this is making sense. I’m dumb for missing that code in the the FMod Engine Documentation. I was searching purely in the unity category and figured all c# code reference would end up there, going to fast. Adding results from that category automatically after the unity results could help.

Also dumb to miss the FMOD_Result on that setParameterByIDWithLabel call. I’ve definitely grown comfortable with those since I wrote the post. I just assumed it would error in the traditional sense, and I’m probably wrong but I still feel like such an egregious user-error should. If there was some way to enable auto-logging or auto-errors for results like FMOD_ERR_EVENT_NOT_FOUND that could help, I didn’t check to see if any of that exists yet.

I have had success using event emitters components with non-looping audio and controlled through physics triggers as intended in the past week. I still had a very confusing time with them though and I’m unclear on their full intended purpose. “Each emitter creates an instance of the selected event”… but the documentation later says that an emitter creates multiple instances for each Play() call. Also it says “…over the course of its lifetime”… but who’s lifetime, the emitter’s or the component’s lifetime as naturally controlled by Unity? Those aren’t literal questions, my point is that there is not very much substance to go on here. I think some of the examples you asked for will help when I can get to them.

I’ll follow up with code-snippets or any examples of those behaviors I can in a few days, thanks!

I actually can reproduce the issue with needing to reattach every time upon calling .start(). This occurs in Unity 2022.3.16f to 25f on Fmod 2.02.21.

The code is very simple, but the event is not.

  • “sustain” here was my name for “looping”, not actual sustain in any way
  • gun.transform is not anything crazy here, it’s equivalent to this.transform and is not being detached or modified in the hierarchy
  • I would create this instance earlier if I could but logs a warning if it’s not coupled with a start() call, as mentioned in a previous post.

// – Starting the shoot loop, called often
if(needsNewSustainInstance)
{
sustainedShootingInstance = RuntimeManager.CreateInstance(gunDataObject.shootingAudioData.firstPersonShotsSustained);
RuntimeManager.AttachInstanceToGameObject(sustainedShootingInstance, gun.transform, false);
sustainedShootingInstance.getDescription(out var eventDescription);
eventDescription.getParameterDescriptionByName(“Sustain”, out var sustainParamDescription);
sustainFireParameter = sustainParamDescription.id;
needsNewSustainInstance = false;
}

sustainedShootingInstance.start();
sustainedShootingInstance.setParameterByID(sustainFireParameter, 1);

// Ending the shoot loop (causing it to go to FadeOut marker in event)
sustainedShootingInstance.setParameterByID(sustainFireParameter, 0);

The problem likely arises from the event. It starts out non-looping, enters a looping region, then waits for sustainFireParameter==0 before entering the end of the timeline, fading out and ending the event.

In Unity, when I stop shooting (sustainFireParameter==0) and let the entire clip finish fading out, then any subsequent play call results in the event playing from the location where it last ended, rather than with the original attached object. This occurs even if the looping region is entirely skipped over.

Alternatively, if I stop shooting then quickly start shooting again such that the clip isn’t allowed to ever finish entirely, the audio stays attached, indicating it has a detach-on-finish behavior.

My solution is to reattach right before .start() every time, and that seems to fix the issue. I can upload a video if it helps, but the upload button here seems locked for me since I’m a new user.

Thanks for the detailed information on reproducing the issue with attaching an event instance.

Just due to the somewhat complex nature of your event’s behavior, instead of me reproducing it from an image or video, can I get you to package your project (or a stripped-down version of it containing the event in question) from File → Package Project, and upload it for me to take a look at? You can upload it to a place of your choice and link it here, or upload it to your FMOD user profile - note that you’ll have to register a project with us to do the latter.

Hello again. Still super busy, I’d like to be able to get a quick video of this too but my code is broken right now, so no shooting. :frowning: Anyways…

Here’s a zip with source audio + FModProject (Stripped-down) with no setup needed, might be a little easier.

Just to be thorough since it’s public: These files included in this zip are modifications of copyrighted content by “Boom Libraries” and an appropriate license for the Silencers Pack would be required to use or distribute this further. I’ll delete this zip at some point.

Alternatively, just the fspackage (no source audio):

Thanks for uploading - I’ve taken the liberty of removing the link with the copyrighted content from your post. In general, if you need to upload any sort of NDA or copyrighted content, we recommend uploading to your user profile.

The issue with the event instance automatically detaching is related to the behavior of attaching that I previously mentioned:

In this case, if the event has it sustain parameter set to 0, allowing the event to play out the tail after the “FadeOut” marker, the event reaches its natural end, and therefore stops. As a result, when RuntimeManager checks the playback state of the instance (which is now STOPPED), it detaches the instance from the specified transform.

This is intended behavior, but I can see why it might be frustrating to work around, so I’ve noted it on our internal improvement tracker for some kind of fix - for example, an extra method parameter that lets you specify whether to detach on stop or not.

As a workaround, you can set your event to be persistent in the “Event Macros” drawer in FMOD Studio, which will prevent it from reaching its natural end and being detached.