今回は、ボタンを押した際に音を出す方法を聞かれたので、ココにも書いておきます。音の制御というよりは「ボタンを押した時に音を出したい」という要望を叶える為の物です。
ボタンを押したら音が出る
自分が小学生の頃、ボタンを押したら音が出る(あと光る!)玩具で遊んだ事を覚えています。
ボタンを押したら「ジョインジョインドゥ」みたいな音が出る剣とか、ボタンを押したら「パラリパラリラパラリパラリラ(トゥーーーーン)」みたいな音が出るベルトとか。
ゲームでも、大抵のボタンを押すときは何らかの音が出ます。今回はこの動作を、出来るだけコードなしでチャレンジしてみようと思います。
Audio Soureceはスピーカー、Audio Listenerは耳
音は、概ねAudio Source、Audio Listener、Audio Clipの3つの項目から成り立ちます。Audio Sourceが音を発信し、Audio Listenerが音を聞き取ります。Audio Clipは再生する音の種類です。
ソースは発信源なので、設定によっては距離や(Listenerとの)相対速度等が表現に影響するかもしれません。
今回は音場の全く関係ない2D設定で話を進めます。
AudioSource.PlayOneShotで音を再生する。…ボタンから
オーディオを流すAPIはAudioSource.PlayOneShotです。このAPIでAudio Clipを指定してやれば、音が再生されます。
…で、実はこのAPI、ButtonのOnClickで指定出来ます。
ButtonのOn Click(Unityのイベントシステム)では、パブリックかつ戻り値がvoidかつ引数が一つ(シリアライズ可能)なAPIはエディター上で指定する事が出来るので、AudioSourceのPlayOneShotは呼び出すことが出来ます。
その際に引数に出したい音を指定してやれば、ボタンをおした時に指定した音が出ます。
実際の手順
まずAudioSourceを用意します。複数の音源があっても一つのAudioSourceで良いです。SE用として使いまわします。
Buttonを用意して、OnClickの「+」を押します。
None(Object)に先程作成したAudio Sourceを登録、NoFunctionはAudioSource.PlayOneShotを設定します。
あとはNone(AudioClip)の所に再生したいAudioClipを登録すれば完了です。これでボタンを押せば音がなります。
Gifには音が出ないのでAudio Mixerで音が出てるのを確認します。
クリック以外で音を鳴らす場合はEvent Triggerで呼ぶ
上の例だと「クリック時」に音がなります。ソレ以外のケース、例えば「マウスカーソルがボタンの上に乗った」みたいなタイミングで音を出してみます。
クリック以外のタイミングは、Event Triggerで呼び出します。
例えば「ボタンの範囲内に入ったら音が出る」の場合、Add New EventTypeでボタンをクリック後、Pointer Enterを選択、後は上でやってたようにボタンにイベントを登録するだけです。
AudioSourceが違うシーンだったり、ボタンを動的に作ってる場合
UIを複数のシーンで跨いで作成していたり、UIをPrefab化して動的に生成してる場合、Cross Scene Reference問題*1で、単純に参照関係を構築出来ない場合があります。
で、色々と考えましたが、ScriptableObjectを経由して参照関係を構築する方法を思いつきました。Audio Manager(Scriptable Object)にAudio Sourceを登録して、ボタンのイベントで呼び出してもらう方法です。
ScriptableObjectの「アセット」を用意
まず、二つのスクリプトを用意します。
AudioManager は、AudioSourceをScriptableObjectに登録して、登録したAudioSourceのPlayOneShotを呼び出すScriptableObject、
MonoBehaviourEventTriggerは、AwakeとDestroyのタイミングで登録したメソッドを呼び出す汎用コンポーネントです。
まずはScriptableObjectのアセットを用意します。*2。名前は別になんでも良いですが、AudioManagerとしました。
AudioSourceを登録
次に、AudioSourceをAudioManagerに登録する挙動を追加します。
MonobehaviourEventTriggerのOnAwakeのタイミングに、AudioManagerを設定、AudioManager.audioSourceにAudioSourceコンポーネントを設定します。
AudioManager.audioSourceはプロパティですので、イベントに使用出来ちゃいます。
これで、Audio Sourceオブジェクトがロードされると、AudioManagerにAudio Sourceコンポーネントが自動的に登録されます。
ボタンを押したらScriptableObjectを経由して音が出る
ボタンを押した際に音が出る設定も行います。
AudioSourceは直接参照出来ないので、ButtonにはAudioManagerを登録します。
AudioManagerのPlayOneShotは、AudioSourceと同じように任意の音を登録。
これでボタンとAudioSouceが揃った時、即座に音が鳴るようになりました。
下の例では、ボタンをシーンに生成すると、ボタンとAudioSourceが繋がって、ボタンを押した際に音が出るようになってます。
雑記
もう少し複雑な動作を期待するならスクリプトを書いた方が良いでしょうが、ボタンを押して音を出す演出くらいなら、UIのレベルで運用出来たららくじゃない? という感じで作ってみました。