Framerate independent event in Unity3D

762 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 answers

1
Steak Overflow 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.