Viewport based spatial attenuation

I’m working on a top-down game, similar view as an RTS. I currently have the listener attenuation object centered on the camera’s focal point, locked at ground height. This mostly works, however the player expectation is that as you zoom the camera in and out, it should change the scope of what you can hear based on your viewport (ie, if I can see it, I can hear it). This obviously is counter to how distance based attenuation works. Effectively would need the min/max range to be dynamically configurable based on the listener rather than the sources.

Any guidance on how to approach this problem? There have been plenty of top-down games made, I imagine there’s already a common solution for this?

Hi,

A solution may be creating a Preset Spatializer Effect (FMOD Studio | Glossary - Preset Effect) which uses a Global parameter connected to the height of the camera which automates (FMOD Studio | Authoring Events - Automation and Modulation) the min max override of the spatializer. The values I have used are just an example. Then using the preset spatializer on all the events you want to be affected by the height of the camera.

You can change the parameter value in code using FMODUnity.RuntimeManager.StudioSystem.setParameterByName()

Hope this helps!

Hi @Connor_FMOD , thank you for the direction and apologies for the late response, priorities got shifted away from audio for a while.

I’ve implemented as suggested, I do think this approach will give us the desired outcome. However, the automation on the max distance doesn’t seem to be working. I’ve confirmed the global parameter value is being updated correctly via the profiler. The max distance seems to stay at the default 30.0 regardless of the CameraZoom parameter value.

Here’s the preset configuration:

Is there something I’ve configured incorrectly? Also, is there a way I can inspect what automation values are being applied to an event instance in the profiler to more accurately determine what behavior is happening with max distance?

Hi,

Could I see the code you are using to set/get the parameter value? If you are using FMOD API | Studio API Reference - EventInstance::getParameterByName, you will need to check the finalvalue to get the parameters current value.

In the profiler if you click on the lifespan of the event instance you can see the parameters attached to it and their values:
image
Is this what you were looking for?

I’ve already confirmed that the global property is being set correctly. It shows the updated value in the profiler as well as in our log output.

Sample code of our usage:

public void SetGlobalProperty(string propertyName, float value)
{
    var result = RuntimeManager.StudioSystem.setParameterByName(propertyName, value);
            
    if(result != RESULT.OK)
        MLog.Warn($"{nameof(SetGlobalProperty)} '{propertyName}' has an invalid result: {result}");

    RuntimeManager.StudioSystem.getParameterByName(propertyName, out var updatedValue);
    MLog.Info($"Global parameter '{propertyName}' is now {updatedValue}");
}

Thank you for showing me where the event inspection is on the profiler, I wasn’t aware of needing to click on the dots on the map to see the details. Unfortunately, this doesn’t expose the information we were hoping to see, which is how the automation is affecting the max distance on the spatial attenuation effect.

I isolated testing driving the max distance via a global parameter through just a slider. In FMOD Studio outside of running the game, it appears to work when manually changing the global parameter default value (can see the max value update accordingly on an event), but in practice this does not work. The global parameter value changes, but the max distance stays at a fixed distance.

Hi,

Thank you for the code. The issue is you are checking the value rather than the finalvalue. " finalvalue is the final value of the parameter after applying adjustments due to automation, modulation, seek speed, and parameter velocity to value . This is calculated asynchronously when the Studio system updates." This is the value you want to be checking.

public void SetGlobalProperty(string propertyName, float value)
{
    var result = RuntimeManager.StudioSystem.setParameterByName(propertyName, value);
            
    if(result != RESULT.OK)
        MLog.Warn($"{nameof(SetGlobalProperty)} '{propertyName}' has an invalid result: {result}");

// Added var out finalValue to the getParameterByName function   
RuntimeManager.StudioSystem.getParameterByName(propertyName, out var updatedValue, out var finalValue);
    MLog.Info($"Global parameter '{propertyName}' is now {finalValue}");
}

Let me know if that helps.

Unfortunately, you won’t be able to see values changing outside of the Profiler. However, you could get the value directly from the spatialized via code. This would involve:

  1. Getting the channel group from the event instance (FMOD Engine | Studio API reference - EventInstance::getChannelGroup) that has the spatializer.
  2. Getting the spatializer from the group (FMOD Engine | Core API Reference - ChannelControl::GetDSP). There will be more than 1 DSP so you can check the FMOD_DSP_TYPE for FMOD_DSP_TYPE_PAN (FMOD Engine | Core API Reference - FMOD_DSP_TYPE)
  3. Then finally we can get the FMOD_DSP_PAN_3D_MAX_DISTANCE from the spatializer to check that the value is correct (FMOD Engine | Core API Reference - FMOD_DSP_PAN_3D_MAX_DISTANCE).

Here is an example for retrieving the spatializer from an event emitter that is playing an event with the spatializer on the master track:

Code Example
if (emitter.EventInstance.getChannelGroup(out FMOD.ChannelGroup channelGroup) == FMOD.RESULT.OK)
{
	if (channelGroup.getNumDSPs(out int num) == FMOD.RESULT.OK && num > 0)
	{
		for (int i = 0; i < num; i++)
		{
			if (channelGroup.getDSP(i, out spatializer) == FMOD.RESULT.OK)
			{
				if (spatializer.getType(out FMOD.DSP_TYPE type) == FMOD.RESULT.OK)
				{
					if (type == FMOD.DSP_TYPE.PAN)
					{
						Debug.Log("Found the spatializer");
						return;
					}
				}
			}
		}
	}
}
if (spatializer.hasHandle())
{
	spatializer.getParameterFloat((int)FMOD.DSP_PAN._3D_MAX_DISTANCE, out float maxVal);
	Debug.Log("Current max distance val: " + maxVal);
}

Let me know if this is the detail you are looking for.