Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Unity Audio Manager
/*手動で変更しないでください*/
public enum AudioKey
{
}
using MBLDefine;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEditor;
using UnityEngine;
using UnityEngine.Audio;
/// <summary>
/// サウンドを管理する
/// </summary>
public class SoundManager : SingletonMonoBehaviour<SoundManager>
{
private const float MAX_DECIBEL = 0f;
private const float MIN_DECIBEL = -80f;
[SerializeField]
private AudioMixer mixer = null;
[SerializeField]
private List<string> audioNames = new List<string>();
[SerializeField]
private List<AudioSource> audioDatas = new List<AudioSource>();
private SoundSetting soundSetting = new SoundSetting(ExternalFilePath.SOUND_SETTING);
/// <summary>
/// SEのボリューム
/// </summary>
public float SE
{
get
{ return soundSetting.SEVolume; }
set
{
mixer.SetFloat(AudioMixerParamName.SEVolume.String(), GetDecibelConversion(value));
soundSetting.SEVolume = value;
}
}
/// <summary>
/// BGMのボリューム
/// </summary>
public float BGM
{
get { return soundSetting.BGMVolume; }
set
{
mixer.SetFloat(AudioMixerParamName.BGMVolume.String(), GetDecibelConversion(value));
soundSetting.BGMVolume = value;
}
}
private void Awake()
{
try
{
LoadSettingFile();
}
catch(IOException e)
{
Debug.LogWarning(e.Message);
soundSetting.SEVolume = 1f;
soundSetting.BGMVolume = 1f;
}
}
private void Start()
{
SE = soundSetting.SEVolume;
BGM = soundSetting.BGMVolume;
}
/// <summary>
/// サウンドを再生する
/// </summary>
/// <param name="key">サウンドのキー</param>
public void Play(AudioKey key)
{
audioDatas[(int)key].Play();
}
/// <summary>
/// サウンドを再生する
/// </summary>
/// <param name="key">サウンドのキー</param>
public void Play(string key)
{
Play(ConvertAudioKey(key));
}
/// <summary>
/// サウンドを再生する
/// 同一キーのサウンドを同時に複数鳴らすことが可能
/// </summary>
/// <param name="key">サウンドのキー</param>
public void PlayOneShot(AudioKey key)
{
audioDatas[(int)key].PlayOneShot(audioDatas[(int)key].clip);
}
/// <summary>
/// サウンドを再生する
/// 同一キーのサウンドを同時に複数鳴らすことが可能
/// </summary>
/// <param name="key">サウンドのキー</param>
public void PlayOneShot(string key)
{
PlayOneShot(ConvertAudioKey(key));
}
/// <summary>
/// 再生を停止する
/// </summary>
/// <param name="key">サウンドのキー</param>
public void Stop(AudioKey key)
{
audioDatas[(int)key].Stop();
}
/// <summary>
/// 再生を停止する
/// </summary>
/// <param name="key">サウンドのキー</param>
public void Stop(string key)
{
Stop(ConvertAudioKey(key));
}
/// <summary>
/// 設定ファイルを読み込む
/// </summary>
public void LoadSettingFile()
{
soundSetting.LoadSettingFile();
}
/// <summary>
/// 設定ファイルを保存する
/// </summary>
public void SaveSettingFile()
{
soundSetting.SaveSettingFile();
}
/// <summary>
/// 文字列をAudioKeyに変換する
/// </summary>
/// <param name="key">AudioKeyに変換する文字列</param>
/// <returns>該当するAudioKey</returns>
private AudioKey ConvertAudioKey(string key)
{
var keyIndex = audioNames.IndexOf(key);
if(keyIndex < 0)
throw new Exception(key + "のAudioKeyへの変換に失敗しました");
return (AudioKey)keyIndex;
}
/// <summary>
/// デシベル変換
/// </summary>
private float DecibelConversion(float volume)
{
return 20f * Mathf.Log10(volume);
}
/// <summary>
/// デシベル変換後の値を得る
/// 変換後の値は適切な値を取るようClampされる
/// </summary>
private float GetDecibelConversion(float value)
{
return Mathf.Clamp(DecibelConversion(value), MIN_DECIBEL, MAX_DECIBEL);
}
#region CustomInspector
#if UNITY_EDITOR
[CustomEditor(typeof(SoundManager))]
private class SoundManagetInspector : Editor
{
private SoundManager soundManager;
private int selectAudioSourceIndex;
private string newAudioDataName = string.Empty;
private List<bool> foldSoundDatas = new List<bool>();
private string outputScriptPath = string.Empty;
private const string SCRIPT_FILE_NAME = "AudioSourceKeyMap.cs";
private const string SCRIPT_FILE_CONTENT =
@"
/*手動で変更しないでください*/
public enum AudioKey
{
[CONTENT]
}";
public override void OnInspectorGUI()
{
soundManager = target as SoundManager;
soundManager.mixer = EditorGUILayout.ObjectField("AudioMixer", soundManager.mixer, typeof(AudioMixer), false) as AudioMixer;
EditorGUILayout.Separator();
#region AudioData
for(int i = 0; i < soundManager.audioDatas.Count; ++i)
{
if(foldSoundDatas.Count < i + 1)
foldSoundDatas.Add(false);
if(foldSoundDatas[i] = EditorGUILayout.Foldout(foldSoundDatas[i], soundManager.audioNames[i]))
{
EditorGUILayout.BeginVertical("box");
EditorGUILayout.BeginHorizontal();
//再生ボタン表示
if(soundManager.audioDatas[i] != null && soundManager.audioDatas[i].isPlaying)
{
if(GUILayout.Button(""))
{
soundManager.audioDatas[i].Stop();
}
}
else if(GUILayout.Button(""))
{
soundManager.audioDatas[i].Play();
}
EditorGUILayout.EndHorizontal();
soundManager.audioDatas[i].clip = EditorGUILayout.ObjectField("AudioClip", soundManager.audioDatas[i].clip, typeof(AudioClip), false) as AudioClip;
soundManager.audioDatas[i].loop = EditorGUILayout.Toggle("Loop", soundManager.audioDatas[i].loop);
soundManager.audioDatas[i].playOnAwake = EditorGUILayout.Toggle("PlayOnAwake", soundManager.audioDatas[i].playOnAwake);
soundManager.audioDatas[i].volume = EditorGUILayout.Slider("Volume", soundManager.audioDatas[i].volume, 0, 1);
soundManager.audioDatas[i].pitch = EditorGUILayout.Slider("Pitch", soundManager.audioDatas[i].pitch, -3, 3);
soundManager.audioDatas[i].bypassEffects = EditorGUILayout.Toggle("BypassEffects", soundManager.audioDatas[i].bypassEffects);
soundManager.audioDatas[i].bypassListenerEffects = EditorGUILayout.Toggle("BypassListenerEffects", soundManager.audioDatas[i].bypassListenerEffects);
soundManager.audioDatas[i].bypassReverbZones = EditorGUILayout.Toggle("BypassReverbZone", soundManager.audioDatas[i].bypassReverbZones);
soundManager.audioDatas[i].reverbZoneMix = EditorGUILayout.Slider("ReverbZoneMix", soundManager.audioDatas[i].reverbZoneMix, 0, 1.1f);
soundManager.audioDatas[i].spatialBlend = EditorGUILayout.Slider("SpatialBlend", soundManager.audioDatas[i].spatialBlend, 0, 1);
soundManager.audioDatas[i].mute = EditorGUILayout.Toggle("Mute", soundManager.audioDatas[i].mute);
soundManager.audioDatas[i].outputAudioMixerGroup = EditorGUILayout.ObjectField("OutPut", soundManager.audioDatas[i].outputAudioMixerGroup, typeof(AudioMixerGroup), false) as AudioMixerGroup;
if(soundManager.audioDatas[i].outputAudioMixerGroup == null)
{
EditorGUILayout.HelpBox("Outputが設定されていないオーディオは音量の一括設定等が適用できません", MessageType.Error);
}
EditorGUILayout.EndVertical();
}
}
#endregion AudioData
#region Add/Remove Button
EditorGUILayout.BeginHorizontal();
newAudioDataName = EditorGUILayout.TextField(newAudioDataName);
if(GUILayout.Button("追加"))
{
if(soundManager.audioNames.Contains(newAudioDataName) || string.IsNullOrEmpty(newAudioDataName))
{
GUIContent content = new GUIContent("既にKeyが存在するか、Keyが空です");
EditorWindow.focusedWindow.ShowNotification(content);
}
else
{
soundManager.audioNames.Add(newAudioDataName);
var newAudioSource = soundManager.gameObject.AddComponent<AudioSource>();
newAudioSource.hideFlags = HideFlags.HideInHierarchy | HideFlags.HideInInspector;
soundManager.audioDatas.Add(newAudioSource);
}
newAudioDataName = string.Empty;
}
EditorGUILayout.Space();
selectAudioSourceIndex = EditorGUILayout.Popup(selectAudioSourceIndex, soundManager.audioNames.ToArray());
if(GUILayout.Button("削除"))
{
if(soundManager.audioNames.Count > selectAudioSourceIndex)
{
DestroyImmediate(soundManager.audioDatas[selectAudioSourceIndex]);
soundManager.audioNames.RemoveAt(selectAudioSourceIndex);
soundManager.audioDatas.RemoveAt(selectAudioSourceIndex);
foldSoundDatas.RemoveAt(selectAudioSourceIndex);
}
}
EditorGUILayout.EndHorizontal();
DebugEditorScript();
#endregion Add/Remove Button
EditorGUILayout.Space();
#region CreateScriptFile
if(GUILayout.Button("適用"))
{
outputScriptPath = Directory.GetFiles(Directory.GetCurrentDirectory(), "SoundManager.cs", SearchOption.AllDirectories).FirstOrDefault();
if(string.IsNullOrEmpty(outputScriptPath))
outputScriptPath = Path.Combine(Directory.GetCurrentDirectory(), "Assets");
var fullPath = Path.Combine(Directory.GetParent(outputScriptPath).FullName, SCRIPT_FILE_NAME);
//Assets以下でなければエラーポップアップ
if(!fullPath.Contains(Path.Combine(Directory.GetCurrentDirectory(), "Assets")))
Debug.LogError("スクリプトはAssets以下に配置してください : " + fullPath);
//ファイルが保存できる場合
else
using(var writer = new StreamWriter(fullPath, false))
{
StringBuilder sb = new StringBuilder();
foreach(var key in soundManager.audioNames)
sb.Append("\t" + key + ",\r\n");
var innerContent = sb.ToString();
var content = SCRIPT_FILE_CONTENT.Replace("[CONTENT]", innerContent);
writer.Write(content);
AssetDatabase.Refresh();
Debug.Log(string.Format("保存しました : {0}", fullPath));
}
}
#endregion CreateScriptFile
EditorUtility.SetDirty(soundManager);
}
/// <summary>
/// エディタスクリプトデバッグ用機能
/// </summary>
[System.Diagnostics.Conditional("DEBUG")]
private void DebugEditorScript()
{
EditorGUILayout.Space();
EditorGUILayout.BeginHorizontal();
if(GUILayout.Button("AudioSoueceの表示"))
{
var allAudioSource = soundManager.GetComponents<AudioSource>();
foreach(var data in allAudioSource)
data.hideFlags = HideFlags.None | HideFlags.NotEditable;
}
if(GUILayout.Button("AudioSourceの非表示"))
{
var allAudioSource = soundManager.GetComponents<AudioSource>();
foreach(var data in allAudioSource)
data.hideFlags = HideFlags.HideInHierarchy | HideFlags.HideInInspector | HideFlags.NotEditable;
}
if(GUILayout.Button("AudioSourceのクリア"))
{
var allAudioSource = soundManager.GetComponents<AudioSource>();
//非表示中は削除できない
if(allAudioSource.Any(s => (s.hideFlags & HideFlags.HideInInspector) != HideFlags.HideInInspector))
{
GUIContent content = new GUIContent("AudioSourceを非表示にしてから実行してください");
EditorWindow.focusedWindow.ShowNotification(content);
}
else
{
for(int i = 0; i < allAudioSource.Count(); ++i)
DestroyImmediate(allAudioSource[i]);
soundManager.audioDatas.Clear();
soundManager.audioNames.Clear();
foldSoundDatas.Clear();
}
}
EditorGUILayout.EndHorizontal();
}
}
#endif
#endregion CustomInspector
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using UnityEngine;
[assembly: InternalsVisibleTo("UnitTest")]
/// <summary>
/// MBLで定義する定数を扱う
/// </summary>
namespace MBLDefine
{
/// <summary>
/// 外部ファイルへの参照に必要なパス群
/// </summary>
internal struct ExternalFilePath
{
internal const string KEY_CONFIG = "keyconf.dat";
internal const string SOUND_SETTING = "soundset.dat";
internal const string EVENT = "eventdat.xml";
internal const string EVENT_SCHEMA = "eventdat.xsd";
//TODO:セーブデータ等追加
}
/// <summary>
/// Resoures内のリソースへのパス
/// </summary>
internal struct ResourcePath
{
internal const string GLOBAL_SCRIPTS = "Prefab/System/GlobalScripts";
internal const string MENU = "Prefab/UI/MenuCanvas";
internal const string CHAT = "Prefab/UI/ChatCanvas";
}
/// <summary>
/// 入力値の基底クラス
/// </summary>
public class InputValue
{
public readonly string String;
protected InputValue(string name)
{
String = name;
}
}
/// <summary>
/// 使用するキーを表すクラス
/// </summary>
public sealed class Key : InputValue
{
public readonly List<KeyCode> DefaultKeyCode;
public readonly static List<Key> AllKeyData = new List<Key>();
private Key(string keyName, List<KeyCode> defaultKeyCode)
: base(keyName)
{
DefaultKeyCode = defaultKeyCode;
AllKeyData.Add(this);
}
public override string ToString()
{
return String;
}
public static readonly Key Action = new Key("Action", new List<KeyCode> { KeyCode.Z });
public static readonly Key Jump = new Key("Jump", new List<KeyCode> { KeyCode.Space });
public static readonly Key Balloon = new Key("Balloon", new List<KeyCode> { KeyCode.X });
public static readonly Key Squat = new Key("Squat", new List<KeyCode> { KeyCode.C });
public static readonly Key Menu = new Key("Menu", new List<KeyCode> { KeyCode.Escape });
}
/// <summary>
/// 使用する軸入力を表すクラス
/// </summary>
public sealed class Axis : InputValue
{
public readonly static List<Axis> AllAxisData = new List<Axis>();
private Axis(string AxisName)
: base(AxisName)
{
AllAxisData.Add(this);
}
public override string ToString()
{
return String;
}
public static Axis Horizontal = new Axis("Horizontal");
public static Axis Vertical = new Axis("Vertical");
}
/// <summary>
/// アニメーションパラメーターの型を表すクラス
/// </summary>
public enum AnimationParamType
{
Float,
Int,
Bool,
Trigger
}
/// <summary>
/// アニメーションパラメータの基底クラス
/// </summary>
internal class AnimationParam
{
public readonly string String;
public readonly AnimationParamType ParamType;
protected AnimationParam(string stateName, AnimationParamType type)
{
String = stateName;
ParamType = type;
}
public override string ToString()
{
return String;
}
}
/// <summary>
/// プレイヤーのアニメーションパラメーター
/// </summary>
internal sealed class PlayerAnimationParam : AnimationParam
{
private PlayerAnimationParam(string stateName, AnimationParamType type)
: base(stateName, type)
{
}
public static PlayerAnimationParam Walk = new PlayerAnimationParam("Walk", AnimationParamType.Bool);
public static PlayerAnimationParam JumpStart = new PlayerAnimationParam("JumpStart", AnimationParamType.Trigger);
public static PlayerAnimationParam JumpEnd = new PlayerAnimationParam("JumpEnd", AnimationParamType.Trigger);
public static PlayerAnimationParam TakeObject = new PlayerAnimationParam("TakeObject", AnimationParamType.Bool);
public static PlayerAnimationParam TakeBalloon = new PlayerAnimationParam("TakeBalloon", AnimationParamType.Bool);
public static PlayerAnimationParam Squat = new PlayerAnimationParam("Squat", AnimationParamType.Bool);
public static PlayerAnimationParam Put = new PlayerAnimationParam("Put", AnimationParamType.Trigger);
}
/// <summary>
/// 入力イベントグループ名
/// </summary>
internal enum InputEventGroupName
{
/// <summary>
/// プレイヤー操作イベント
/// </summary>
Player,
/// <summary>
/// メニュー表示・非表示イベント
/// </summary>
SwitchMenu,
/// <summary>
/// メニュー内操作イベント
/// </summary>
InMenu,
}
internal enum AudioMixerParamName
{
BGMVolume,
SEVolume,
}
/// <summary>
/// 列挙型の拡張メソッドを保有する
/// </summary>
internal static class ExtensionEnum
{
public static string String(this InputEventGroupName group)
{
return group.ToString();
}
public static string String(this AudioMixerParamName param)
{
return param.ToString();
}
}
}
using LitJson;
using MBLDefine;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEngine;
/// <summary>
/// サウンド設定の情報を取り扱うクラス
/// </summary>
public class SoundSetting
{
private const string SE = "SE";
private const string BGM = "BGM";
private Dictionary<string, double> data = new Dictionary<string, double>();
private readonly string filePath;
/// <summary>
/// SEのVolumeへのアクセサー
/// </summary>
public float SEVolume
{
get { return (float)data[SE]; }
set { data[SE] = value; }
}
/// <summary>
/// BGMのVolumeへのアクセサー
/// </summary>
public float BGMVolume
{
get { return (float)data[BGM]; }
set { data[BGM] = value; }
}
/// <summary>
/// サウンド設定を扱うクラスを生成する
/// </summary>
/// <param name="filePath"></param>
public SoundSetting(string filePath)
{
this.filePath = filePath;
data.Add(SE, 0);
data.Add(BGM, 0);
}
public void LoadSettingFile()
{
//TODO:復号処理
using(TextReader tr = new StreamReader(filePath, Encoding.UTF8))
data = JsonMapper.ToObject<Dictionary<string, double>>(tr);
}
/// <summary>
/// 設定を保存する
/// </summary>
public void SaveSettingFile()
{
//TODO:暗号化処理
using(TextWriter tw = new StreamWriter(filePath, false, Encoding.UTF8))
tw.Write(JsonMapper.ToJson(data));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment