【Unity7】BGMと効果音を付ける【2Dローグライク13】
Unity5.0.0f4 Personal(2015年6月)
前回の続きです〜今回は、ゲームにBGMと効果音をつけていきます〜
UnityのAudio機能について以前エッセンスを書きましたので、予めご参照下さると進めやすいと思います〜
【目標】ゲームにBGMと効果音を付ける!
2D Roguelike 13 of 14 : Audio - YouTube
①SoundManagerオブジェクトの作成
まずは音を管理する、SoundManagerオブジェクトを作成します〜HierarchyからCreateEmptyを選択し、
名前をSoundManagerに変更します〜
SoundManagerオブジェクト作成
続いてSoundManagerオブジェクトにAudioSourceをふたつ付けます〜SoundManagerのInspectorビューにあるAddComponentより、Audio>AudioSourceと進んで取り付けて下さい〜
AudioSourceをふたつ取り付ける
②SoundManagerスクリプトを作成する
それではSoundManagerオブジェクトに取り付けるSoundManagerスクリプトを作成していきます〜以下ソースです〜
using UnityEngine; using System.Collections; public class SoundManager : MonoBehaviour { public AudioSource efxSource; //効果音用AudioSource public AudioSource musicSource; //BGM用AudioSource public static SoundManager instance = null; //音の高さにバリエーションを付ける用の変数 public float lowPitchRange = .95f; public float highPitchRange = 1.05f; //シングルトンの処理 void Awake () { if (instance == null) instance = this; else if (instance != this) Destroy (gameObject); DontDestroyOnLoad (gameObject); } //BGMの再生 public void PlaySingle (AudioClip clip) { efxSource.clip = clip; efxSource.Play(); } //数種類の効果音を受け取り、ランダムで再生 public void RandomizeSfx (params AudioClip[] clips) { //受け取った効果音番号をランダムで指定 int randomIndex = Random.Range(0, clips.Length); //音の高さをランダムで指定 float randomPitch = Random.Range(lowPitchRange, highPitchRange); efxSource.pitch = randomPitch; //受け取った効果音を選択 efxSource.clip = clips[randomIndex]; //効果音を再生 efxSource.Play(); } }
SoundManage.cs
Awakeメソッドでは、シングルトンの機能をのせています〜シングルトンについてはGameManager作成時に紹介していますので、ご参照下さい〜
続いてPlaySingleメソッドです〜ここではAudioClipを受け取り、BGMを再生しています〜
最後にRandomizeSfxメソッドです〜ここでは引数として複数の効果音(このゲームでは2種類の効果音)を受け取り、それらをランダムで選択し再生しています〜
まずは受け取った効果音数からランダムで1つ決定します〜
次に音の高さをランダムで指定します〜pitch(音の高さ)は、基本の高さが1で、今回0.95〜1.05の範囲からランダムで指定する形となっています〜
次にAudioSourceのclipを設定していきます〜
引数として受け取ったclips配列の中からrandomIndex番目の効果音を選択し、clipに指定します〜
最後にPlayメソッドで効果音を再生します〜
スクリプトができましたらSoundManagerオブジェクトに取り付け、BGMの設定をしていきます〜
SoundManagerオブジェクトについているAudioSourceのうち、2番目のAudioSourceのAudioClipに「scavengers_music」を指定します〜また、BGMなのでLoopをtrueにして下さい〜
1番目のAudioSourceについてはPlay On aAwakeをfalseにだけしておいて下さい〜
こんな感じ
最後にSoundManagerスクリプトのpublic変数efxSourceに1番目のAudioSourceを、musicSourceに2番目のAudioSourceを指定して下さい〜
ドラッグ&ドロップ!
③Playerスクリプトに効果音の処理を追加する
Playerスクリプトに効果音を鳴らす処理を追加していきます〜以下ソースです〜
using UnityEngine; using System.Collections; using UnityEngine.UI; public class Player : MovingObject { public int wallDamage = 1; public int pointsPerFood = 10; public int pointsPerSoda = 20; public float restartlevelDelay = 1f; public Text foodText; //********** 開始 **********// //各効果音を指定 public AudioClip moveSound1; public AudioClip moveSound2; public AudioClip eatSound1; public AudioClip eatSound2; public AudioClip drinkSound1; public AudioClip drinkSound2; public AudioClip gameOverSound; //********** 終了 **********// private Animator animator; private int food; protected override void Start () { animator = GetComponent<Animator>(); food = GameManager.instance.playerFoodPoints; foodText.text = "Food: " + food; base.Start(); } private void OnDisable () { GameManager.instance.playerFoodPoints = food; } void Update () { if (!GameManager.instance.playersTurn) return; int horizontal = 0; int vertical = 0; horizontal = (int)Input.GetAxisRaw ("Horizontal"); vertical = (int)Input.GetAxisRaw ("Vertical"); if (horizontal != 0) { vertical = 0; } if (horizontal != 0 || vertical != 0) { AttemptMove<Wall>(horizontal, vertical); } } protected override void AttemptMove <T> (int xDir, int yDir) { food--; foodText.text = "Food: " + food; base.AttemptMove <T> (xDir, yDir); RaycastHit2D hit; //********** 開始 **********// //移動可能である時、moveSound1かmoveSound2どちらかを鳴らす if (Move (xDir, yDir, out hit)) { SoundManager.instance.RandomizeSfx(moveSound1, moveSound2); } //********** 終了 **********// CheckIfGameOver(); GameManager.instance.playersTurn = false; } protected override void OnCantMove <T> (T component) { Wall hitWall = component as Wall; hitWall.DamageWall(wallDamage); animator.SetTrigger("PlayerChop"); } private void OnTriggerEnter2D (Collider2D other) { if (other.tag == "Exit") { Invoke ("Restart", restartlevelDelay); enabled = false; } else if (other.tag == "Food") { food += pointsPerFood; foodText.text = "+" + pointsPerFood + " Food: " + food; //********** 開始 **********// //Foodを取った時、eatSound1かeatSound2を鳴らす SoundManager.instance.RandomizeSfx(eatSound1, eatSound2); //********** 終了 **********// other.gameObject.SetActive(false); } else if (other.tag == "Soda") { food += pointsPerSoda; foodText.text = "+" + pointsPerSoda + " Food: " + food; //********** 開始 **********// //Sodaを取った時、drinkSound1かdrinkSound2を鳴らす SoundManager.instance.RandomizeSfx(drinkSound1, drinkSound2); //********** 終了 **********// other.gameObject.SetActive(false); } } private void Restart () { Application.LoadLevel(Application.loadedLevel); } public void LoseFood (int loss) { animator.SetTrigger("PlayerHit"); food -= loss; foodText.text = "-" + loss + " Food: " + food; CheckIfGameOver(); } private void CheckIfGameOver () { if (food <= 0) { //********** 開始 **********// //gameOverSoundを鳴らす SoundManager.instance.PlaySingle(gameOverSound); //BGMは停止する SoundManager.instance.musicSource.Stop(); //********** 終了 **********// GameManager.instance.GameOver(); } } }
メンバ変数として、たくさんのAudioClipを指定しています〜これらはpublic変数なので、Inspectorより設定していきます〜
まずはmoveSound1にはscavengers_footstep1を指定して下さい〜
ドラッグ&ドロップ!
同じ要領で、
moveSound2 = scavengers_footstep2
eatSound1=scavengers_fruit1
eatSound2=scavengers_eatSound2
drinkSound1=scavengers_soda1
drinkSound2=scavengers_soda2
gameOverSound=scavengers_dir
を指定して下さい〜
それではスクリプトの解説に入っていきます〜
まずはAttemptMoveメソッドにて、Moveメソッドを実行し、trueが帰ってきたら移動可能ということなので、歩く音を鳴らしていきます〜
シングルトンであるSoundManagerクラスにアクセスし、RandomizeSfxメソッドを呼び出しています〜引数にはmoveSound1とmoveSound2と2つの効果音を指定し、どちらか一方をランダムで鳴らしています〜
OnTriggerEnter2Dメソッドでも、同じような処理を行っています〜アイテムを取ったタイミングでSoundManagerクラスのRandomizeSfxメソッドを実行しています〜
最後のCheckIfGameOverメソッドでは単一の効果音を渡すので、SoundManagerクラスのPlaySingleメソッドを実行しています〜引数にはgameOverSoundのみを指定しています〜また、BGMをこのタイミングでStopしています〜
④Enemyスクリプトに効果音処理を追加する
続いてEnemyが攻撃した時に効果音を発生させるため、Enemyスクリプトに加筆していきます〜以下ソースです〜
using UnityEngine; using System.Collections; public class Enemy : MovingObject { public int playerDamage; private Animator animator; private Transform target; private bool skipMove; //********** 開始 **********// //攻撃用の効果音を指定 public AudioClip enemyAttack1; public AudioClip enemyAttack2; //********** 終了 **********// protected override void Start () { GameManager.instance.AddEnemyToList(this); animator = GetComponent<Animator>(); target = GameObject.FindGameObjectWithTag("Player").transform; base.Start(); } protected override void AttemptMove <T> (int xDir, int yDir) { if (skipMove) { skipMove = false; return; } base.AttemptMove <T> (xDir, yDir); skipMove = true; } public void MoveEnemy () { int xDir = 0; int yDir = 0; if (Mathf.Abs (target.position.x - transform.position.x) < float.Epsilon) { yDir = target.position.y > transform.position.y ? 1 : -1; } else { xDir = target.position.x > transform.position.x ? 1 : -1; } AttemptMove <Player> (xDir, yDir); } protected override void OnCantMove <T> (T component) { Player hitPlayer = component as Player; animator.SetTrigger("enemyAttack"); hitPlayer.LoseFood(playerDamage); //********** 開始 **********// //攻撃用効果音をSoundManagerに渡し、ランダムで再生 SoundManager.instance.RandomizeSfx(enemyAttack1, enemyAttack2); //********** 終了 **********// } }
Enemy.cs
メンバー変数にpublicでAudioClipを2つ作成しましたので、Inspectorビューから指定していきます〜
PrefabフォルダからEnemy1とEnemy2を選択し、InspectorビューのenemyAttack1にscavengers_enemy1を、enemyAttack2にscavengers_enemy2を指定します〜
AudioClipを指定
OnCantMoveメソッドにて先ほどと同様、効果音2つを引数にSoundManagerクラスのRandomizeSfxメソッドを呼び出しています〜
⑤Wallスクリプトに効果音処理を追加する
最後にプレイヤーがWallを攻撃した時に音を出すため、Wallスクリプトに加筆していきます〜以下ソースです〜
using UnityEngine; using System.Collections; public class Wall : MonoBehaviour { //********** 開始 **********// public AudioClip chopSound1; public AudioClip chopSound2; //********** 終了 **********// public Sprite dmgSprite; public int hp = 3; private SpriteRenderer spriteRenderer; void Awake () { spriteRenderer = GetComponent<SpriteRenderer> (); } public void DamageWall (int loss) { //********** 開始 **********// SoundManager.instance.RandomizeSfx (chopSound1, chopSound2); //********** 終了 **********// spriteRenderer.sprite = dmgSprite; hp -= loss; if(hp <= 0) gameObject.SetActive (false); } }
Wall.cs
これまでと全く同じ要領で効果音を出すことができます〜
まずは、PrefabフォルダにあるWall1〜Wall8を選択した状態で、public変数chopSound1にscavengers_chop1を、chopSound2にscavengers_chop2を指定します〜
Wall1〜Wall8を選択した状態でAudioSourceを指定
DamageWallメソッドの先頭でSoundManagerクラスのRandomizeSfxメソッドを実行しています〜引数には効果音2つを指定しています〜
今回は音の実装のためテスト結果の表示はブログでは難しいので、ここまでとします〜
ありがとうございました〜
【Unity開発7】Unity公式チュートリアル2Dローグライクの導入【2Dローグライク1】
【Unity開発7】アニメーションを作りたい!【2Dローグライク2】
【Unity開発7】Floorやアイテムを作成したい!【2Dローグライク3】
【Unity開発7】床や敵キャラ、アイテム等を自動生成させたい!【2Dローグライク4】
【Unity開発7】BoardManagerを呼び出すGameManagerを作成する【2Dローグライク5】
【Unity開発7】キャラクターを動かすための抽象クラス作成【2Dローグライク6】
【Unity開発7】破壊可能な壁(Wall)を作成したい!【ローグライク7】
【Unity開発7】プレイヤーのアニメーターを設定したい!【ローグライク8】
【Unity開発7】プレイヤー用のスクリプトを設定したい!【ローグライク9】
【Unity開発7】敵キャラクターのスクリプトを作成したい!【2Dローグライク10】
【Unity開発7】敵キャラクターのアニメーターを設定したい!【2Dローグライク11】
【Unity開発7】uGUIでUIの表示とレベル機能を実装したい!【2Dローグライク12】