Framerate independent event in Unity3D

775 Views Asked by At

I use the libpd4unity package to communicate with Pure Data. I receive a bang from Pure Data with LibPD.Bang. On a bang event I play sound by FMOD.

Problem is, that I receive bangs frequently, for example once every 500 ms but event doesn't trigger in specific length of frame. Usually length change 1 frame less or more.

Is there a solution for this problem? For example a framerate independent event? I want to know if event (delegate) in Unity3D is framerate independent or not.

Because there is tempo for playing each sound and just 1 frame ruins rhythm.

I need to sync sounds for playing by each separate bang.

1

There are 1 best solutions below

1
On

Regarding your question on whether delegates are dependent or independent from Unity's framerate, there's no straight answer. It depends on how your delegates are called. Are they called from a thread? Are they executed in a thread? Coroutines are not framerate independent, they are executed in Unity's loop.

The following script should shine a light on the difference between handling delegates in coroutines and in threads.

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

public class DelegatesAndFramerate : MonoBehaviour {

    delegate void MyDelegate();
    MyDelegate myDelegate1; // done with coroutines
    MyDelegate myDelegate2; // done with threads

    Thread thread;
    bool threadDone = false;

    private int frameCount = 0;
    private int delegate1CallCount = 0;
    private int delegate2CallCount = 0;
    private int callerLoopsCount_coroutine = 0;
    private int callerLoopsCount_thread = 0;

    void Start () {
        myDelegate1 += Elab1;
        myDelegate2 += Elab2;

        StartCoroutine(CallerCoroutine());

        thread = new Thread(new ThreadStart(CallerThread));
        thread.Start();
    }

    void Update()
    {
        frameCount++;
    }

    void Elab1()
    {
        delegate1CallCount++;
    }

    void Elab2()
    {
        delegate2CallCount++;
    }

    IEnumerator CallerCoroutine()
    {
        while(true)
        {
            callerLoopsCount_coroutine++;
            myDelegate1();
            yield return null;
        }
    }

    void CallerThread()
    {
        while(!threadDone)
        {
            callerLoopsCount_thread++;
            myDelegate2();
        }
    }

    void OnDestroy()
    {
        Debug.Log("Frame Count: " + frameCount);
        Debug.Log("Delegate Call Count (Coroutine): " + delegate1CallCount);
        Debug.Log("Delegate Call Count (Thread): " + delegate2CallCount);
        Debug.Log("Caller Loops Count (Coroutine): " + callerLoopsCount_coroutine);
        Debug.Log("Caller Loops Count (Thread): " + callerLoopsCount_thread);

        threadDone = true;
        thread.Join();
    }
}

If you attach it to a GameObject and let Unity play for some seconds you'll see that the times the delegate was called from a coroutine is equal to the number of executed frames whilst the times the delegate was called from the thread will be way bigger.

I have experience in interfacing softwares similar to Pure Data and I think what you need is a (rather typical) thread with all your delegates there, create a queue of commands for Unity and digest it in Unity's Update. Not knowing libPD in the specific this might not be the best practice for the case but it is a widely used approach. Basically the producer-consumer pattern.

Basing on the example GUITextScript.cs, libPD only requires you to subscribe to the right delegates. You don't have control on when these are executed, the library has; so if you keep having this issue it's worth submitting a bug report to the developers I guess.