Unityでゲームを作る時、BGMやSE(サウンド音)が必要になってくると思う。これを実装するのはそんなに難しくはないが、よくゲームに実装されているBGMやSEをスライダーで調整する機能を取り入れると難しかったので、これをまとめてみる。他のやり方で実装する方法もあると思うので参考程度に見てほしい。
主に、参考欄に貼ったテラシュールブログさんの記事を参考に実装してます。
また、記事内では2Dプロジェクトで作成してます。
BGMとSEを設定する
まずは、スライダーを設定する前にBGMとSEを設定してみる。余裕のある人は、以下の手順で実際に手を動かしてみてほしい。
音源をAssets以下に入れておく
BGMやSEをフリーサイトからあらかじめ取得しておこう。取得したらBGMフォルダをAsset内に作成してその中に落としてきた音源を突っ込んでおく。
BGMを設定する
次にUnityのプロジェクトを立ち上げて、BGMを流す用のオブジェクトを作成する。
BgmManagerが作成できたら、今度はAudioSourceコンポーネントをそのオブジェクトにくっつけよう。
このAudioSourceコンポーネントのAudioClipに流したい曲をくっ付けるだけで、もうゲームでBGMを流すことができる。
試しにPlayボタンを押してみよう。おそらく設定したBGMが流れるはずだ。
SEを設定する
SEとは、サウンドエフェクトの略でボタンを押した時に、ピコッってなったりする効果音のことだ。これも設定していこう。
今回は、ボタンをクリックしたら音が出るといったような感じにしたいので、まずはボタンを作成する。
「ボタンを押したら〜」って実装をしたい場合は、ButtonコンポーネントのOnClick()の部分に設定をしていく。
このOnClick()の部分にサウンドを設定したい。その為に、SE用のオブジェクトを新しく作成しよう。空のGameObjectを作ってSEManagerとでもしておこう。
そしたら、これにまたAudioSourceコンポーネントをくっ付ける。
そして、AudioClipの欄にSEを設定する、、、と思いきやSEの場合はそうではない。SEの場合はとりあえず何も設定しなくていい。ただ、AudioSourceコンポーネントをくっ付ければいいだけだ。
出来たら、ButtonのOnClick()に戻って、そこにSEManagerを突っ込もう。
突っ込んだら、No Functionとなっている部分から、AudioSource→PlayOneShot(AudioClip)を選択する。
ここで何をやっているかというと、AudioSourceクラスのPlayOneShotメソッドを呼び出している。
なぜ、AudioSourceクラスを呼べるかというと、SEManagerにAudioSourceコンポーネントがくっついているからだ。このAudioSourceコンポーネントを通して、PlayOneShotを呼んでいる。
PlayOneShotメソッドは1回だけ音を鳴らすメソッドで、このメソッドに音源を設定していく。
音源を設定したら、再生してから実際にボタンをクリックして音を鳴らしてみよう。おそらく1回音がなるはずだ。
ちょっとこのSEのところを整理してみよう。
- 空のGameObjectを作り、AudioSourceコンポーネントをくっつける。(ここではSEManager)
- Buttonオブジェクトを作成して、そこのOnClick()部分に、SEmanagerを突っ込む。
- Functionの部分にPlayOneShotを設定する
- AudioClipに再生したいSEを設定する
こんなところだろうか。途中意味がわからなくても、実際に手を動かせばそのうち意味もわかってくるはずなので、まずは試しに作ってみてください。
Buttonをもう一つ作成してSEを設定する
BGMと違ってSE音は色々なところで使うはずだ。ボタンはおそらく1つじゃなく複数あるだろうし、このボタンでは違うSE音を鳴らしたい等必要に応じてSE音を変更しなくてはならない。これの実装もわりと簡単なので、以下の手順に沿ってやってみよう。
HierarchyにButtonをもう1つ作成する。
ButtonコンポーネントのOnClickに上で作ったSEManagerをアタッチ。
上と同様の手順でPlayOnShotを呼び出して、SEに好きな音源を設定するだけだ。
これで再生すると、Button1と2にはそれぞれ設定した音がボタンを押すたびに聞こえるはずだ。
つまりはAudioSourceコンポーネントがついたGameObjectを一つ用意しておくだけで、それを通してさまざまなオブジェクトにSEを設定できる。
そして、これのVolumeを変更していくことで、全体のSEの音量を変更していくことができるようになるというわけだ。(この部分がSliderを動かして音を変更する本記事の目的に繋がる)
【応用】Prefabにしたボタンにはどうやって音を設定するのか?
ここで一つ自分が詰まってしまったものを紹介する。やらない人は結構長いので飛ばしてもOK。(本当はこれを自分用にまとめておきたかったのが目的だったりもする。)
既にHierarchy上にあるボタンにはOnClick()からSEManagerをくっ付けることができた。
ただ、何らかの操作をしてから生成したボタンなどのオブジェクトにはどうやってSEを設定すればよいだろうか。
普通にOnClick()にSEManagerをくっ付ければよくないか?と思うかもしれないが、PrefabにしてしまうとSEManagerをくっ付けることができなくなる。
試しに、上で作ったボタンをPrefab化しよう。
元々、Button1のOnClick()上には、SE音が設定されていたはずだ。ただ、Button1のPrefabを開くと、そこにSEは設定されていない。(Hierarchy上のButton1には設定されたままです。このButton1は使わないので削除してください。)
これでは、このボタンをInstantiateしたとしても、OnClick()に音が設定されていないのでSE音が鳴らない。どうしたものかと詰まって検索したところ、こちらの記事で解決できた。今回はこの記事に沿って、PrefabにもSEを設定する。
こうすることで、SEManagerのVolumeを変更すれば、PrefabのSEもPrefabじゃないSEも一括で音量変更ができるようになる。
その解決方法を見ていこう。自分もよくわかってない用語ややり方もあるのでそこは理解次第追記予定です。
ScriptableObjectを作成する
いきなりわからん単語が出てきた。ScriptableObjectのことをイマイチわかってないけど、とりあえずこいつはGameObjectにはくっつけないものらしい。詳しくはググってみてください。
新規に C#スクリプトを作成して、SEManagerという名前をつけて以下の用に記述しよう。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu]
public class SEManager : ScriptableObject
{
public AudioSource audioSource{ get; set; }
public void PlayOneShot(AudioClip clip)
{
if (audioSource != null)
{
audioSource.PlayOneShot(clip);
}
}
}
わからないなりにも、ポイントだけ見ていく。
まずは継承。ScriptableObjectはGameObjectにくっつけるわけではないので、:MonoBehaviorではなく:ScriptableObjectを継承しよう。
そして、[CreateAssetMenu]をクラスの定義の上に記述する。これを書くと、Projectの+ボタンからScriptableObjectのアセットを作ることができる。(自分は、「クラスのインスタンス」的な意味で解釈してるけどあってるかしら)
このScriptableObjectのアセットを通して、PrefabのOnClick()にも音を設定できるようにしていく。
残りのコード部分、public AudioSource audioSource{ get; set; }は普通にプロパティで。public void PlayOneShot(AudioClip clip)はメソッドだ。
ScriptableObjectのアセットを生成する
ここまで出来たら実際にProjectの+ボタン→SE ManagerをクリックしてScriptableObjectのアセットを生成しよう。
生成されたらAssetsフォルダ以下にNew SE Managerと書かれたアセットが生成されるはずだ。
インスペクタを見てみると、スクリプトに書いたプロパティも表示されてる。
ButtonコンポーネントのOnClickにScriptableObjectのアセットをくっつける
ScriptableObjectのアセットが出来たらButtonの方に戻ろう。ButtonのPrefabを開いて、OnClick()からこのアセットを設定する。
そう、つまりは、このアセットを生成することで、PrefabのOnClick()にこれをくっつけることができるようになるのだ。
アセットファイルを設定したら、SEManagerからPlayOneShotメソッドを呼び出す。あくまで、このメソッドはScriptableObject内で定義したメソッドで、AudioSourceコンポーネントから呼んできているものとは違うぞ。
//SEManager(ScriptableObject)の中のメソッド
public void PlayOneShot(AudioClip clip)
{
if (audioSource != null)
{
audioSource.PlayOneShot(clip);
}
}
SEを設定する
ScriptableObjectのファイルをアタッチし、PlayOneShotを選択したら、後はサウンドを設定しよう。
ボタンPrefabをHierarchyに持っていき、再生する
設定し終わったら、ボタンPrefabを、Hierarchy上にD&Dしよう。そしたら、実際に再生してボタンをクリックして音が鳴るか確かめる。
ボタンをクリックして、、あれ?音が鳴らない。実はまだこれだけだと音を鳴らすことができない。なぜだかわかるだろうか?
それはScriptableObjectのpublic AudioSource audioSource;
部分にaudioSourceコンポーネントが入っていないからである。ScriptableObject内のPlayOneShotメソッドでは、audioSourceのPlayOneShotを呼び出しいる。ただ、audioSource自体にはまだ何もつけていない。これを解決するのが以下の方法だ。
MonoBehaviourEventTriggerスクリプトを作成する
新たにProject内からC#スクリプトを作成して、MonoBehaviourEventTriggerと名付けよう。これは、Hierarchy上のSEManagerオブジェクトにくっ付けるもので、
ファイルが生成できたら、以下をコピペしよう。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
public class MonoBehaviourEventTrigger : MonoBehaviour
{
public UnityEvent onAwake = new UnityEvent();
public UnityEvent onDestroy = new UnityEvent();
void Awake()
{
onAwake.Invoke();
}
void OnDestroy()
{
onDestroy.Invoke();
}
}
このスクリプトに書かれていることは正直全く理解していないが、とりあえずコピペしよう。
SEManagerオブジェクトにMonoBehaviourEventTriggerスクリプトをアタッチする
スクリプトができたら、それを「SEを設定する」で作成した、SEManagerオブジェクトにアタッチする。SEManagerオブジェクトはSEを管理するオブジェクトだ。このオブジェクトにMonoBehaviourEventTriggerをアタッチしよう。
OnAwake()にNewSEManagerアセットファイルを設定
MonoBehaviourEventTriggerがアタッチできたら、OnAwaka()にアセットファイルを設定する。
設定できたら、NoFunction→SEManager→AudioSource audioSourceを選択する。AudioSource audioSourceはメソッドじゃなくてプロパティだが、イベントに使用できるとのこと。(プロパティはフィールドを取得できるメソッドみたいなもの、フィールド・プロパティ・メソッドの概念が頭に入っていると理解しやすいかも)
audioSourceプロパティを選択したら、AudioSourceをこれに入れてあげる。
OnAwake()はおそらく、SEManagerオブジェクトが生成されたら呼ばれるコールバック関数(ある関数が呼ばれた後に呼ばれる関数みたいなイメージ:ここではAwake関数が呼ばれた後に、OnAwakeが呼ばれている)みたいなもので、そのタイミングでプロパティにAudioSourceコンポーネントを突っ込んでいるという意味合いであってると思う。
これを行うことで、再生のタイミングで、ScriptableObjectのpublic AudioSource audioSource;
部分にaudioSourceコンポーネントを突っ込むことができた。これで、Prefabから作成したボタンでも音がでるようになるはずだ。
実際に再生して確かめてほしい。
Sliderを作成する
いよいよこれから本題に入っていく。まずはSliderを作成しよう。
BGMとSE用に2本Sliderを用意するが、とりあえず最初はBGMから調整できるように設定していく。
Sliderを作ったら、インスペクタのSliderコンポーネントの中のValueを0から1に変更しておく。
これは、音量は最初からMax値で鳴らし、段々音を下げるようにするためだ。このValueを弄ることで音量を変更できるように設定する。
BgmMangerScriptを作成する
次にBgmManagerScriptを作成する。このスクリプトは、Bgmオブジェクトにくっ付ける用途のスクリプトでここで、上で作成したSliderを受け取って操作する。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class BgmManager : MonoBehaviour
{
public Slider slider;
AudioSource audioSource;
void Start()
{
audioSource = GetComponent<AudioSource>();
slider.onValueChanged.AddListener(value => this.audioSource.volume = value);
}
}
sliderをアウトレット接続で受け取って、sliderのvalueがチェンジする度に、自身にくっついているaudioSourceコンポーネントのvolumeをいじっているっていう感じのスクリプトだ。
AddLisnerの中身はよくわからんけど、動けば問題ない。
BgmManagerスクリプトをアタッチする
BgmManagerスクリプトが出来たらそれをBgmオブジェクトにアタッチする。そして、Sliderを突っ込んであげる。これでBGMの音量が調整できるようになったはずだ。
SEの方も同様の手順で行えば、音量の変更ができるぞ。
まとめ
一応、これでBGMとSEの音量をスライダーから変更することができた。特に、PrefabのOnClick()に音を設定する方法は難しかったと思う。今はまだわからなくてもそのうちわかるようになるはずだ。(自分に言い聞かせてます。)
たぶん他にももっとやり方があると思うんだが今の自分にはこれが精一杯なので、他のやり方を見つけたら別記事でまとめます。