Hard-coding event paths is often not the best solution to get an overview of the existing Events in the game. For example, if we move or rename an Event inside FMOD Studio, the event path in the code must also be updated. This takes time. It is a bit easier to organize our events with ScriptableObjects.
This method was suggested to me by my friend Ryan. Check out his works!
Download the Unity & FMOD Project for this tutorial. If you get any errors after opening the project, please delete the FMODStudioCache.asset file, as it will still contain the old path to the FMOD Studio Project.
What are ScriptableObjects?
ScriptableObjects are asset files used to store information. They are very useful to store configuration data for your game and your objects.
From a technical perspective, ScriptableObjects, like MonoBehaviours, inherit from UnityEngine.Object. We can still define fields and methods, or use some of Unity’s callbacks: Awake(), OnDestroy(), OnEnable() and OnDisable(). But there is no Update() method. Unlike MonoBehaviours, ScriptableObjects are not serialized in the scene (or prefabs) and are not used as components of GameObjects. Instead, they are stored as assets in the project folders – just like textures, models and script files.
Setting up ScriptableObjects in Unity
We create a script that lists events in the form of strings for a hypothetical player. This allows us to create an asset file that we can use in other scripts to access the paths of events:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(menuName = "SO/Audio/Player", fileName = "New Player Sheet")]
public class PlayerAudioData: ScriptableObject {
[Header("Movement")]
[FMODUnity.EventRef]
public string footsteps = null;
[FMODUnity.EventRef]
public string jump = null;
[FMODUnity.EventRef]
public string dash = null;
[FMODUnity.EventRef]
public string land = null;
[Header("Attacks")]
[FMODUnity.EventRef]
public string[] swordAttacks = null;
[FMODUnity.EventRef]
public string hammerAttack = null;
[FMODUnity.EventRef]
public string shield = null;
}
As we see, the script does not inherit from MonoBehaviour, but from ScriptableObject. This means that we cannot use this ScriptableObject directly as a component. Instead, we can use Unity’s Menu to create an asset file that contains all the data we defined:
Unity will then create a new asset file called New Player Sheet (which we can of course change in the previously created script). Let’s name the file something like this: PlayerAudio. When we click on the file, we see empty FMOD Events fields that we can fill with our events:
Playing FMOD Events with ScriptableObjects
When we create a script to play events, we first declare the ScriptableObject:
[SerializeField]
private PlayerAudioData playerAudio;
Then we access any string variable that we have declared in the ScriptableObject and play our sounds:
void Start()
{
FMODUnity.RuntimeManager.PlayOneShot(playerAudio.jump);
}
In Unity we make sure that we move (and reference) the ScriptableObject .asset file in the inspector field that was created:
Now, if we need to move an event in FMOD Studio, we just need to change the path in the .asset file. This saves us a lot of searching in scripts we wrote weeks or months ago.
Automatically create Scriptable Object .cs files in FMOD Studio by using the Scripting API
We can extend the functionality of FMOD Studio by using the Scripting API and writing JavaScript code. One wonderful use case for this would be to select FMOD Events and automatically export a Scriptable Object .cs file we can directly use in Unity to create our data assets files. To do this first create a Scripts folder inside the root directory of your FMOD project. Inside that folder create a new .js file and insert following code:
studio.menu.addMenuItem({
name: "Unity\\ScriptableObject",
isEnabled: function() {
var events = studio.window.browserSelection();
return events
},
execute: function() {
var events = studio.window.browserSelection(); /* text */
var lineBreak = "\r\n";
var nullText = "null";
var leftCurly = String.fromCharCode(123);
var rightCurly = String.fromCharCode(125);
var tab = String.fromCharCode(9);
var quote = String.fromCharCode(34);
var attribute = "[FMODUnity.EventRef]";
var modifier = "public string "
var using = "using System.Collections;" + lineBreak + "using System.Collections.Generic;" + lineBreak + "using UnityEngine;" + lineBreak + lineBreak;
var menuName = quote + studio.system.getText("Menu Name (Example: 'Axe of Time/Audio/Player'", "") + quote;
var className = studio.system.getText("Class Name", "");
var assetMenuText = "[CreateAssetMenu(menuName = " + menuName + ", " + "fileName = " + quote + className + quote + ")]" + lineBreak;
var classText = "public class " + className + " : ScriptableObject" + lineBreak + leftCurly + lineBreak;
var finalData = using + assetMenuText + classText;
for (x = 0; x < events.length; x++) {
var eventName = events[x].name;
eventName = eventName.replace(/\s+/g, '');
finalData += tab + attribute + lineBreak + tab + modifier + eventName + " = " + nullText + ";" + lineBreak + lineBreak;
}
finalData += rightCurly;
var projectPath = studio.project.filePath;
projectPath = projectPath.substr(0, projectPath.lastIndexOf("/"));
var filePath = projectPath + "/" + className + ".cs";
var file = studio.system.getFile(filePath);
file.open(studio.system.openMode.WriteOnly);
file.writeText(finalData);
file.close();
}
});
Save the file open the FMOD Studio project. Select a few FMOD Events and click on Scripts->Unity->ScriptableObject. Enter a menu path and a class name for your .cs file. The new .cs file will be created inside the root directory of your FMOD Project.