Answered
0
0

Hi,
I have doppler turned on (and set to 500% for testing purposes) in the relevant event macros in my FMOD Studio project but the effect is missing when I run my unity game. How do you enable the effect? I have found zero documentation on the topic and no answers in these forums.
Thanks – Brian

  • You must to post comments
Best Answer
-1
0

To use the doppler effect, you need to have a rigidbody on your event emitter. It uses the velocity of the rigidbody.

You are right, there doesn’t seem to be much documentation on it at the moment, we will look to rectify this. The only real info we have on it at the moment is here: http://www.fmod.org/docs/content/generated/FMOD_System_Set3DSettings.html

  • Cameron Baron

    I’m not having any issues getting it to work.
    These are the steps I took:

    – Create new Unity project and add FMOD integration.
    – Add cube to scene, remove 3D collider and add Rigidbody2D.
    – Move the cube high above the Main Camera (add Studio listener to camera).
    – Add emitter to cube and chose a sound.
    – In Studio make sure the event has doppler enabled, rebuild banks.
    – Hit play in Unity.
    – As the cube falls past the player you can hear the doppler.

    If that is working but not when you move your player(listener) it may be because the doppler relies on velocity from the rigidbody.

  • Brian Cullen

    Hi Cameron,
    Yep that works but unfortunately if you move an object via transform.Translate or you add an animation say a looping up or down animation to the exact same object the doppler effect completely fails to work. Unless you are using free falling objects with gravity, doppler is not working. I even have a cube in the project that drops from + 100 to -100 on the y axis that has a strong noticeable doppler effect when the animation is disable and allowed to free fall and no doppler effect whatsoever (or a very broken version) when animation is enabled (the animation is of equivalent speed and direction as the free falling scenario). In my game the cars use transform.Translate and doppler does not work there either. What next? Again they all have rigid bodies and correct studio settings etc.
    – Brian

  • Brian Cullen

    I will add that you can set gravity to 1 in the rigidbody while the up/down animation is playing and you get a glitchy/clicky broken versions of the doppler shift effect. I try all this with a standard non2D rigid body and all the same issues exist.

  • Cameron Baron

    By moving an object via the Transform, you are basically just telling the object it is in a new location now, not that it has traveled from one to another. Velocity is only used when applying forces to a rigidbody (eg. rigidbody2D.AddForce()). You can check this by printing the objects rigidbody2D.velocity to the debug console.

    As for animations, Unity have physics based animations on their roadmap (https://unity3d.com/unity/roadmap) but it is still under research.

  • Brian Cullen

    That makes sense – but I didn’t have this issue with fmod designer for the same game 3 years ago now. It might have been an idea to calculate the velocity based on transform/s positions over time irrespective of rigidbody calculations so that all movement types would work with Doppler as before – or I might be missing some logic here. I will look into a work around but would love to see a more inclusive solution in a future update so that fast moving objects using transform.translate or standard animations would work. Thanks for looking into all this for me – b

0
0

Hi All,

I know this is a very old post now, but I found myself asking the exact same question yesterday, so thought I’d leave my solution here for others to try.
In order for your events’ doppler effects to work without rigidbodies, all that FMOD really needs is an FMOD.ATTRIBUTES_3D struct describing world-space velocity of the moving object, and the same for the listener.
To achieve this, we can overload some functions in the FMOD C# scripts.

First, create a class that has the same members as a Vector3. In C#, classes are reference types by default. Using a class instead of a struct will simplify our method overloading, and slightly reduce the amount of code we need to re-write. I declared my class in RuntimeUtils.cs:

public class CVector3
{
public float x = 0;
public float y = 0;
public float z = 0;
}

Find RuntimeUtils.To3DAttributes, and overload it so it can take an instance of our class as a parameter. Add the following to RuntimeUtils.cs:

    public static FMOD.ATTRIBUTES_3D To3DAttributes(Transform transform, CVector3 kinematicVelocity)
    {
        FMOD.ATTRIBUTES_3D attributes = transform.To3DAttributes();
        FMOD.VECTOR vel;
        vel.x = kinematicVelocity.x;
        vel.y = kinematicVelocity.y;
        vel.z = kinematicVelocity.z;

        attributes.velocity = vel;

        return attributes;
    }

Next, we are going to expand the AttachedInstance class (found in RuntimeManager.cs) so that it can accommodate our CVector3 reference:

class AttachedInstance
{
public FMOD.Studio.EventInstance instance;
public Transform transform;
public Rigidbody rigidBody;
public Rigidbody2D rigidBody2D;
public CVector3 kinematicVelocity; //<———-Add this line
}

We can now add the following overload of RuntimeManager.AttachInstanceToGameObject:

    public static void AttachInstanceToGameObject(FMOD.Studio.EventInstance instance, Transform transform, CVector3 kinematicVelocity)
    {
        var attachedInstance = new AttachedInstance();
        attachedInstance.transform = transform;
        attachedInstance.instance = instance;
        attachedInstance.rigidBody2D = null;
        attachedInstance.rigidBody = null;
        attachedInstance.kinematicVelocity = kinematicVelocity;
        Instance.attachedInstances.Add(attachedInstance);
    }

In RuntimeManager.cs, locate the Update() method. About half way down you will see this loop:

             for (int i = 0; i < attachedInstances.Count; i++)
             //...etc

At the bottom of the for loop, change this……

                if (attachedInstances[i].rigidBody)
                {
                    attachedInstances[i].instance.set3DAttributes(RuntimeUtils.To3DAttributes(attachedInstances[i].transform, attachedInstances[i].rigidBody));
                }
                else
                {
                    attachedInstances[i].instance.set3DAttributes(RuntimeUtils.To3DAttributes(attachedInstances[i].transform, attachedInstances[i].rigidBody2D));
                }

To this……

                if (attachedInstances[i].rigidBody)
                {
                    attachedInstances[i].instance.set3DAttributes(RuntimeUtils.To3DAttributes(attachedInstances[i].transform, attachedInstances[i].rigidBody));
                }
                else if (attachedInstances[i].rigidBody2D)
                {
                    attachedInstances[i].instance.set3DAttributes(RuntimeUtils.To3DAttributes(attachedInstances[i].transform, attachedInstances[i].rigidBody2D));
                }
                else if (attachedInstances[i].kinematicVelocity != null)
                {
                    attachedInstances[i].instance.set3DAttributes(RuntimeUtils.To3DAttributes(attachedInstances[i].transform,attachedInstances[i].kinematicVelocity));
                }

Here we are saying that our Doppler effect can fall back to kinematic velocity if there are no Rigidbodies associated with that instance.

We have sorted out everything we need to convert our CVector3 data into an FMOD.ATTRIBUTES_3D struct. However, we still need to fill in the kinematic velocity each frame.
To do this we need to make some changes to StudioEventEmitter.cs.

First, add and initialise the following class members to StudioEventEmitter:

CVector3 kinematicVelocity = null;
Vector3 positionLastFrame = Vector3.zero;

Next, locate the Play() method and find this piece of code:

            if (is3D)
            {
                var rigidBody = GetComponent<Rigidbody>();
                var transform = GetComponent<Transform>();
                instance.set3DAttributes(RuntimeUtils.To3DAttributes(gameObject, rigidBody));
                RuntimeManager.AttachInstanceToGameObject(instance, transform, rigidBody);
            }

Change it to this:

            if (is3D)
            {
                var rigidBody = GetComponent<Rigidbody>();
                var transform = GetComponent<Transform>();

                if (!rigidBody)
                {
                    kinematicVelocity = new CVector3();
                    instance.set3DAttributes(RuntimeUtils.To3DAttributes(this.transform, kinematicVelocity));
                    RuntimeManager.AttachInstanceToGameObject(instance, transform, kinematicVelocity);
                }
                else
                {
                    instance.set3DAttributes(RuntimeUtils.To3DAttributes(gameObject, rigidBody));
                    RuntimeManager.AttachInstanceToGameObject(instance, transform, rigidBody);
                }
            }

…StudioEventEmitter will now send kinematic data if a Rigidbody is not found.

Next, add the following method to StudioEventEmitter.cs:

    void setKinematicVelocity()
    {
        //Get current velocity
        Vector3 currentVel;
        currentVel.x = kinematicVelocity.x;
        currentVel.y = kinematicVelocity.y;
        currentVel.z = kinematicVelocity.z;

        //Update to new velocity
        currentVel = Vector3.Lerp(currentVel, (this.transform.position - positionLastFrame) / Time.deltaTime, Time.deltaTime * 15); //A very short lerp prevents jitter

        //Reassign to CVector3 object
        kinematicVelocity.x = currentVel.x;
        kinematicVelocity.y = currentVel.y;
        kinematicVelocity.z = currentVel.z;

        //Store world position for next frame
        positionLastFrame= this.transform.position;
    }

Call the above method it in Update(), but ONLY if kinematicVelocity is non-null. If it is null, this indicates that a rigidbody was found, and kinematicVelocity should be ignored.

void Update()
{
if (kinematicVelocity != null)
{
setKinematicVelocity();
}
}

We’re almost done. However, Doppler effect should be calculated from the point of the listener, who may also be moving. We therefore need to apply similar changes to the StudioListener.cs script.

First, add the two members, and setKinematicVelocity() method, in the exact same manner as with StudioEventEmitter.cs:

public CVector3 kinematicVelocity = null;
Vector3 positionLastFrame;

    void setKinematicVelocity()
    {
        //Get current velocity
        Vector3 currentVel;
        currentVel.x = kinematicVelocity.x;
        currentVel.y = kinematicVelocity.y;
        currentVel.z = kinematicVelocity.z;

        //Update to new velocity
        currentVel = Vector3.Lerp(currentVel, (this.transform.position - positionLastFrame) / Time.deltaTime, Time.deltaTime * 15);

        //Reassign to CVector3 object
        kinematicVelocity.x = currentVel.x;
        kinematicVelocity.y = currentVel.y;
        kinematicVelocity.z = currentVel.z;

        //Store world position for next frame
        positionLastFrame = this.transform.position;
    }

…Call setKinematicVelocity() in the Update method for StudioListener, again checking for nulls:

void Update()
{
if (kinematicVelocity != null)
{
setKinematicVelocity();
}
//..etc
}

In the OnEnable() method, we are going to create a CVector3 IF there are no rigidbodies on the StudioListener game object:

    void OnEnable()
    {
        RuntimeUtils.EnforceLibraryOrder();
        rigidBody = gameObject.GetComponent<Rigidbody>();
        rigidBody2D = gameObject.GetComponent<Rigidbody2D>();

     //<-----Add this part here--------->
        if (!rigidBody && !rigidBody2D)
        {
            kinematicVelocity = new CVector3();
        }
     //<---------------------------------->

         //...etc
    }

In RuntimeManager.cs, we need to add one last overload. That is to let SetListenerLocation() work with CVector3 objects. Add this overload to RuntimeManager.cs:

    public static void SetListenerLocation(int listenerIndex, GameObject gameObject, CVector3 kinematicVelocity)
    {
        Instance.studioSystem.setListenerAttributes(listenerIndex, RuntimeUtils.To3DAttributes(gameObject.transform, kinematicVelocity));
    }

Finally, StudioListener.cs will also have a method called SetListenerLocation(). Remove it, and replace it with this version:

    void SetListenerLocation()
    {
        if (rigidBody)
        {              
            RuntimeManager.SetListenerLocation(ListenerNumber, gameObject, rigidBody);
        }
        else if (rigidBody2D)
        {
            RuntimeManager.SetListenerLocation(ListenerNumber, gameObject, rigidBody2D);
        }
        else
        {
            RuntimeManager.SetListenerLocation(ListenerNumber, gameObject, kinematicVelocity);
        }
    }

You should now have made all the adjustments to the FMOD Unity wrapper to allow you to supply your events with kinematic velocity.

If the above changes don’t work, I’ve likely missed something out… I’ve checked it over as much as I can right now, but my FMOD Unity install is heavily modified and I was trying to leave out other stuff that made no sense in this context, while still trying to remember all the steps required to do this.

Leave a reply if it helps you. Also leave a reply if it doesn’t, along with any errors, and I’ll fix it asap!

In any case, hopefully you understand what I’m doing here, and if so, you can make your own modifications depending on your needs.

Happy programming
Geoff

  • You must to post comments
Showing 2 results
Your Answer

Please first to submit.