Unity(C#)初心者・入門者向けチュートリアル ひよこのたまご

AndroidやiOS向けアプリを簡単に作れるゲーム開発環境Unity(ユニティ)の使い方を、チュートリアル方式で一緒に学びましょう!

【Unity7】BGMと効果音を付ける【2Dローグライク13】

Unity5.0.0f4 Personal(2015年6月)

f:id:hiyotama:20150519220636p:plain

前回の続きです〜

今回は、ゲームにBGMと効果音をつけていきます〜


UnityのAudio機能について以前エッセンスを書きましたので、予めご参照下さると進めやすいと思います〜

hiyotama.hatenablog.com

【目標】ゲームにBGMと効果音を付ける!



2D Roguelike 13 of 14 : Audio - YouTube



①SoundManagerオブジェクトの作成
まずは音を管理する、SoundManagerオブジェクトを作成します〜HierarchyからCreateEmptyを選択し、
名前をSoundManagerに変更します〜


f:id:hiyotama:20150603113259p:plain
SoundManagerオブジェクト作成


続いてSoundManagerオブジェクトにAudioSourceをふたつ付けます〜SoundManagerのInspectorビューにあるAddComponentより、Audio>AudioSourceと進んで取り付けて下さい〜


f:id:hiyotama:20150603113502p:plain
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作成時に紹介していますので、ご参照下さい〜

hiyotama.hatenablog.com


続いて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にだけしておいて下さい〜


f:id:hiyotama:20150603120441p:plain
こんな感じ


最後にSoundManagerスクリプトのpublic変数efxSourceに1番目のAudioSourceを、musicSourceに2番目のAudioSourceを指定して下さい〜


f:id:hiyotama:20150603120820p:plain
ドラッグ&ドロップ!


③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を指定して下さい〜


f:id:hiyotama:20150603121753p:plain
ドラッグ&ドロップ!


同じ要領で、
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を指定します〜


f:id:hiyotama:20150603123359p:plain
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を指定します〜


f:id:hiyotama:20150603123815p:plain
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】

【Unity開発7】BGMと効果音を付ける【2Dローグライク13】

【Unity開発7】タッチパネルに対応させたい!【2Dローグライク14(Fin)】