I’m importing some wavs and mp3 to Unity and for each one I want to create a prefab consisting of a Game Object with Audio Source component filled with proper Audio Clip. It’s a boring task that should be automated and I’m looking at editor scripts to do it right.
1)I’ve made an OnPostprocessAudio script that does that quite well, but in this approach I can only create a properly named prefab and then I have to manualy drag the clip into it. Other problem is that the prefab is generated with each reimport (which means additional dragging each time!), which is not an elegant solution.
2)I’ve tried extending the Audio Clip Inspector, but after selecting the asset in Project Window I can’t actually click the button I created. The button appears not in the “Import Settings” section, but in the “Imported Object” section which is inactive. That would be an ideal solution - I select an audio asset and launch a script associated with it.
3)Adding a Top Menu item is probably a possibility, but I haven’t tried it yet (it doensn’t seem too elegant and usable).
What approach would you suggest, what’s the right way to do it? I’m only learning editor scripting now and maybe don’t see simpler ways to achieve this goal.
tl;dr: Scripted creation of a prefab from Audio Clip asset, how?
Did some basic testing to try catch any simple mistakes. It supports modifying the clip and repopulating existing prefabs.
Basically, you need to get a callback that is safe for you to use the audio clip. Unfortunately the OnPostprocessAudio was not ideal as the object is transient (or if not, I made some silly mistake). I was able to get it working by going through all imported assets at the end of the post processing step, in OnPostprocessAllAssets.
using UnityEngine;
using UnityEditor;
using System.IO;
public class AudioPrefabProcessor : AssetPostprocessor
{
public static void OnPostprocessAllAssets (
string[] importedAssets,
string[] deletedAssets,
string[] movedAssets,
string[] movedFromAssetPaths)
{
foreach (string path in importedAssets)
BuildAudioPrefabs(path);
}
private static void BuildAudioPrefabs (string path)
{
AudioClip clip = LoadAsset<AudioClip>(path);
if (clip)
{
string prefabPath = GetPrefabPath(path);
GameObject oldPrefab = LoadAsset<GameObject>(prefabPath);
if (oldPrefab)
{
// Alternatively we could choose to do nothing.
// If you think about it, all you're doing is setting a ref.
// But in case you renamed a clip, perhaps you want to regen
// the actual prefab.
UpdateOldPrefab(clip, oldPrefab);
}
else
{
CreateNewPrefab(clip, prefabPath);
}
}
}
private static void UpdateOldPrefab (AudioClip clip, GameObject oldPrefab)
{
AssetDatabase.StartAssetEditing();
oldPrefab.GetComponent<AudioSource>().clip = clip;
AssetDatabase.StopAssetEditing();
}
private static void CreateNewPrefab (AudioClip clip, string prefabPath)
{
GameObject temp = new GameObject();
temp.AddComponent<AudioSource>().clip = clip;
PrefabUtility.CreatePrefab(prefabPath, temp);
Object.DestroyImmediate(temp, false);
}
private static T LoadAsset<T> (string path) where T : Object
{
return AssetDatabase.LoadAssetAtPath(path, typeof (T)) as T;
}
private static string GetPrefabPath (string path)
{
string clipName = Path.GetFileNameWithoutExtension(path);
return "Assets/" + clipName + ".prefab";
}
}