Correct way of using RuntimeManager in editor

Hey there,
I’m trying to get the MinDistance and MaxDistance attributes of events in a OnDrawGizmos, but for that I need to get the EventDescription which is only possible through RuntimeManager. In the latest updates, it can’t be accessed anymore in the Editor, and the error message encourages you to “create your own System objects”, which is essentially initializing a FMOD Studio System.

I have tried making my own FmodSystem but it doesn’t seem to be loading the events (everytime I try to get an event through ID or name, it says it doesn’t exist), and I believe it’s generating a conflict since more than one is being created in the editor.


 public static FMOD.Studio.System FmodSystem
        {
            get
            {
                if (_fmodSystem.isValid())
                    return _fmodSystem;
                var result = FMOD.Studio.System.create(out var studioSystem);
                if (result != FMOD.RESULT.OK)
                {
                    if (studioSystem.isValid())
                    {
                        studioSystem.release();
                        studioSystem.clearHandle();
                    }
                    Debug.LogError("[FMOD/STMOD] FMOD Studio System couldn't be initialized: "+result);
                }
                else
                {
                    INITFLAGS studioInitFlags = INITFLAGS.NORMAL | INITFLAGS.DEFERRED_CALLBACKS;
                    studioSystem.initialize(0, studioInitFlags, FMOD.INITFLAGS.NORMAL, IntPtr.Zero);
                    studioSystem.update();
                    _fmodSystem = studioSystem;
                }
                return _fmodSystem;
            }
        }

Worst thing is, if I comment out the RuntimeManager’s conditional to use it only if the game is playing, everything works out fine, but I imagine it’s not intended.

Is there any documentation or way to use FMOD’s System correctly? Thanks!

Hi,

The only thing you seem to be missing is bank loading. To retrieve an EventDescription, you will need to load the bank it belongs to. If you’re planning to retrieve it by path, you will also need to load the Master strings bank.

While it is technically possible to call into RuntimeManager to create a Studio System, it is only designed to be used at runtime. This is because RuntimeManager destroys the created System in OnDestroy(), which only happens when a scene or game ends. so you’ll leak the Studio System in the current editor instance when calling from outside of runtime. FMOD can support up to eight Systems at once - once you call into RuntimeManager eight times from the editor, FMOD will no longer be able to create a new system until you restart the editor.

Thanks! That worked. It does require a fair amount of code to make a System object, which I believe it’s not documented since I couldn’t find it anywhere.
For anyone wondering, this is the final code:

public static FMOD.Studio.System FmodSystem
        {
            get
            {
                if (FMODStudioSettings == null)
                {
                    return new FMOD.Studio.System();
                }

                if (_fmodSystem.isValid())
                    return _fmodSystem;

                var result = FMOD.Studio.System.create(out var studioSystem);
                if (result != RESULT.OK)
                {
                    if (studioSystem.isValid())
                    {
                        studioSystem.release();
                        studioSystem.clearHandle();
                    }
                    Debug.LogError("FMOD Studio System couldn't be initialized: "+result);
                }
                else
                {
                    INITFLAGS studioInitFlags = INITFLAGS.NORMAL | INITFLAGS.DEFERRED_CALLBACKS;
                    studioSystem.initialize(0, studioInitFlags, FMOD.INITFLAGS.NORMAL, IntPtr.Zero);
                    studioSystem.update();
                    foreach (string bankName in GetBankPaths())
                    {
                        const string bankExtension = ".bank";
                        string bankPath;
                        string bankFolder = Application.streamingAssetsPath;
                        if (System.IO.Path.GetExtension(bankName) != bankExtension)
                        {
                            bankPath = $"{bankFolder}/{bankName}{bankExtension}";
                        }
                        else
                        {
                            bankPath = $"{bankFolder}/{bankName}";
                        }
                        studioSystem.loadBankFile(bankPath, LOAD_BANK_FLAGS.NORMAL, out _);
                    }
                    studioSystem.flushSampleLoading();
                    _fmodSystem = studioSystem;
                }
                return _fmodSystem;
            }
        }

Aside from that, you will need to cache the Event Descriptions to avoid lag spikes whenever you get them and then invalidate the cache at some point when FMOD’s banks are updated.

1 Like

If anyone comes across this thread and looks for an updated answer, I highly recommend not to use my previous code. Do not create your own StudioSystem.

Instead, in latest FMOD versions, you can use EditorUtils.System and then do getEventById to get the event description and any other information you might need. This works fine in the editor.

The reason I’m brining this up is because I have recently found out that plugins/spatializers affecting the event won’t be loaded in the editor with my code, and EditorUtils does it better anyway. No need to reinvent the wheel.

1 Like

Thanks for the update, this guided me going through the same questionning. For anyone interested, this code allows pressing a button to play a EventReference in Editor :slight_smile:

using FMODUnity;
using UnityEditor;
using UnityEngine;

namespace Wikwemot_AR.Modules.Definitions.Editor.Scripts.CustomEditors
{
    [CustomPropertyDrawer(typeof(EventReference))]
    public class CustomEventReferenceDrawer : EventReferenceDrawer
    {
        private static readonly Texture IconPlay = WikwemotEditorImages.LoadImage("Play.png");

        private bool _isPlaying;
        private FMOD.Studio.EventInstance _eventInstance;

        public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
        {
            base.OnGUI(position, property, label);

            // Draw a button that plays the sound
            var buttonRect = new Rect(position.x, position.y + 60, 20, 20);

            if (GUI.Button(buttonRect, IconPlay))
            {
                ToggleIsPlaying(property);
            }
        }

        private void ToggleIsPlaying(SerializedProperty property)
        {
            _isPlaying = !_isPlaying;

            if (_isPlaying)
            {
                StopIfAlreadyPlaying();
                PlayNewSound();
            }
            else
            {
                _eventInstance.stop(FMOD.Studio.STOP_MODE.IMMEDIATE);
                _eventInstance.release();
            }

            void StopIfAlreadyPlaying()
            {
                if (!_eventInstance.isValid()) return;
                _eventInstance.stop(FMOD.Studio.STOP_MODE.IMMEDIATE);
                _eventInstance.release();
            }

            void PlayNewSound()
            {
                var eventReference = GetEventReference(property);
                if (!eventReference.HasValue) return;

                EditorUtils.LoadPreviewBanks();
                EditorUtils.System.getEventByID(eventReference.Value, out var eventDescription);
                eventDescription.createInstance(out _eventInstance);
                
                _eventInstance.start();
            }
        }

        private FMOD.GUID? GetEventReference(SerializedProperty property)
        {
            var pathProperty = property.FindPropertyRelative("Path");
            if (pathProperty != null)
            {
                var path = pathProperty.stringValue;
                return EventReference.Find(path).Guid;
            }

            return null;
        }
    }
}

The interesting part is the ‘PlayNewSound’ method.

1 Like