アマゾンバナーリンク

ディスプレイ広告

スポンサーリンク

Unityで作る弾幕シューティングゲームその5 音声を扱うフレームワークの作成

こんにちは!ジェイです。ゲームといえば、BGMやSEがかかせないですよね?今回はそれを簡単に使える様にまとめたフレームワークの作成について説明します。

まずは、通常のUnityで音を出すときの手順を考えてみます。

  • UnityのProjectウィンドウに音声ファイルを直接ドロップ
  • AudioClipをスクリプト内に用意
  • AudioSourceをスクリプト内に用意
  • AudioSource.clipにAudioClipを代入
  • AudioSource.Playメソッド呼び出し
using UnityEngine;
using System.Collections;
public class Player : MonoBehaviour {
public AudioClip audioClip;
AudioSource audioSource;
void Start() {
audioSource = gameObject.GetComponent<AudioSource>();
audioSource.clip = audioClip;
}
void Update () {
if ( Input.GetKeyDown(KeyCode.A) == true ) {
// Torigger
Debug.Log( "A!" );
}
}
}

以上のスクリプトでauidoClipにならしたい音声をアタッチするとAキーを押すたびにその音がなるようになります。

記事内広告

スクリプト内だけで音声を鳴らす

通常スクリプトで音を鳴らすには、わざわざ音声を1つずつアタッチしなければなりません。これはめんどくさいですよね。最初に音声のリソースを一気に読み込んで、名前を指定して音を鳴らせると、とても便利なのでそのような仕組みを作ります。

using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
public class CResourcesLoader<T> where T : UnityEngine.Object
{
Dictionary<string, T> ResourcesHandles = new Dictionary<string, T>();
public bool LoadObject(string file_name)
{
string fn = Path.GetFileName(file_name);
if (ResourcesHandles.ContainsKey(fn))
{
Debug.Log("LoadObject()で同じ名前のキーがありました");
Debug.Log(file_name + "にある" + fn + "の名前を変更してください");
return false;
}
else
{
T ob = Resources.Load<T>(file_name);
if (ob)
{
ResourcesHandles.Add(fn, ob);
return true;
}
else
{
Debug.Log("LoadObject()" + file_name + "の読み込みが失敗しました");
return false;
}
}
}
public bool LoadAllObjects(string file_name)
{
T[] obs = Resources.LoadAll<T>(file_name);
if (obs.Length <= 0)
{
return false;
}
foreach (T ob in obs)
{
if (ResourcesHandles.ContainsKey(ob.name))
{
Debug.Log("LoadAllObject()で同じ名前のキーがありました");
Debug.Log(file_name + "にある" + ob.name + "の名前を変更してください");
}
else
{
ResourcesHandles.Add(ob.name, ob);
}
}
return true;
}
public T GetObjectHandle(string name)
{
if (ResourcesHandles.ContainsKey(name))
{
return ResourcesHandles[name];
}
else
{
return null;
}
}
public bool ContainsKey(string key_name)
{
return ResourcesHandles.ContainsKey(key_name);
}
}
public class CSoundPlayer
{
static GameObject SoundPlayerObj, BGMPlayerObj;
static CResourcesLoader<AudioClip> ResourcesLoader = new CResourcesLoader<AudioClip>();
static AudioSource SoundAudioSource, BGMAudioSource;
static CFadeTimer FadeTimer;
public static IEnumerator SetFadeTimer(float start_val, float end_val, float end_time)
{
FadeTimer = new CFadeTimer(start_val, end_val, end_time);
while (true)
{
float t = FadeTimer.CalcTime();
BGMAudioSource.volume = t;
if (t <= 0.0f)
{
BGMAudioSource.Stop();
yield break;
}
else
{
yield return new WaitForSeconds(Time.deltaTime);
}
}
}
public static bool LoadAudioClip(string audio_path)
{
return ResourcesLoader.LoadObject(audio_path);
}
public static bool LoadAllSounds(string audio_path)
{
return ResourcesLoader.LoadAllObjects(audio_path);
}
public static bool StopSound(string se_name)
{
GameObject sound_obj = GameObject.Find(se_name);
if (sound_obj)
{
AudioSource aus = sound_obj.GetComponent<AudioSource>();
if (aus)
{
aus.Stop();
return true;
}
}
return false;
}
public static AudioClip GetAudioClip(string se_name)
{
if (ResourcesLoader.ContainsKey(se_name) == false)
{
return null;
}
else
{
return ResourcesLoader.GetObjectHandle(se_name);
}
}
public static bool PlaySound(string se_name, bool se_flag = true)
{
if (ResourcesLoader.ContainsKey(se_name) == false)
{
return false;
}
if (se_flag)
{
if (SoundPlayerObj == null)
{
SoundPlayerObj = new GameObject("SoundPlayer");
SoundAudioSource = SoundPlayerObj.AddComponent<AudioSource>();
}
SoundAudioSource.PlayOneShot(ResourcesLoader.GetObjectHandle(se_name));
}
else
{
if (BGMPlayerObj == null)
{
BGMPlayerObj = new GameObject("BGMPlayer");
BGMAudioSource = BGMPlayerObj.AddComponent<AudioSource>();
BGMAudioSource.clip = ResourcesLoader.GetObjectHandle(se_name);
BGMAudioSource.volume = 1.0f;
BGMAudioSource.loop = true;
BGMAudioSource.Play();
}
else
{
if (BGMAudioSource)
{
if (BGMAudioSource.isPlaying)
{
BGMAudioSource.Stop();
}
else
{
BGMAudioSource.Play();
}
}
else
{
BGMAudioSource = BGMPlayerObj.AddComponent<AudioSource>();
if (BGMAudioSource)
{
BGMAudioSource.clip = ResourcesLoader.GetObjectHandle(se_name);
BGMAudioSource.volume = 1.0f;
BGMAudioSource.loop = true;
BGMAudioSource.Play();
}
}
}
}
return true;
}
}
// 指定した時間である値からある値まで数を変化させる
// start_val 変化させたい数の初期値
// end_val 変化させたい数の終了値
// end_time 終了時間(end_timeかけて終了させる)
// 使用手順
// 1 宣言する
// CFadeTimer FadeTimer;
// 2 初期化する
// FadeTimer = new CFadeTimer( 100, 1000, 3 );
// 3 毎ループ行う処理を追加する
// print( FadeTimer.CalcTime () );
public class CFadeTimer
{
private bool Flag = true;
private float StartVal, EndVal, StartTime, EndTime, Delta, Result = 0.0f;
public CFadeTimer(float start_val, float end_val, float end_time)
{
StartVal = start_val;
EndVal = end_val;
EndTime = end_time;
StartTime = Time.realtimeSinceStartup;
Delta = (EndVal - StartVal) / EndTime;
}
public float CalcTime()
{
if (Flag)
{
float t = Time.realtimeSinceStartup - StartTime;
if (EndTime <= t)
{
Flag = false;
Result = EndVal;
return Result;
}
Result = Delta * t + StartVal;
return Result;
}
else
{
return -1.0f;
}
}
}

CResourcesLoaderジェネリッククラスで実装していて、where T : UnityEngine.Objectというキーワードで、UnityEngine.Objectのみ型を指定できるように制約することによって、Resources.LoadとResources.LoadAllを使えるようにしています。

実際の使用例

using UnityEngine;
using System.Collections;
public class CResourcesManager : MonoBehaviour
{
private static CResourcesLoader<GameObject> ResourcesLoader = new CResourcesLoader<GameObject>();
private static CResourcesLoader<Texture2D> TexturesLoader = new CResourcesLoader<Texture2D>();
public static bool LoadTexture( string file_name )
{
return TexturesLoader.LoadObject( file_name );
}
public static Texture2D GetTextureHandle( string obj_name )
{
return TexturesLoader.GetObjectHandle( obj_name );
}
public static GameObject GetObjectHandle( string obj_name )
{
return ResourcesLoader.GetObjectHandle( obj_name );
}
void Awake()
{
// Prefabsフォルダから、すべてのGameObjectを読み込む
if ( !ResourcesLoader.LoadAllObjects( "Prefabs" ) )
{
print ("Prefabsファイル読み込みに失敗しました");
}
// 音声ファイルを一曲だけ読み込む
if (!CSoundPlayer.LoadAudioClip("Sound/Luste"))
{
print("Lusteファイル読み込みに失敗しました");
}
// Soundフォルダからすべての音声ファイルを読み込む
if (!CSoundPlayer.LoadAllSounds("Sound"))
{
print("Soundファイル読み込みに失敗しました");
}
// se_flagオフで音声ファイルを再生する
CSoundPlayer.PlaySound("Luste", false);
}
void Update()
{
// Jキーを押すと5秒かけて再生中のフェードアウトする
if (Input.GetKeyDown(KeyCode.J))
{
StartCoroutine(CSoundPlayer.SetFadeTimer(1.0f, 0.0f, 5.0f));
}
// Kキーを押すと5秒かけてフェードイン再生する
if (Input.GetKeyDown(KeyCode.K))
{
CSoundPlayer.PlaySound("dangeon07", false);
StartCoroutine(CSoundPlayer.SetFadeTimer(0.0f, 1.0f, 5.0f));
}
}
}

上記のようにGameObjectやTexture2Dなどのリソースをフォルダごと読み込んだり、ファイル1つずつ読み込んだりできるので便利です。

ゲームに組み込んでみる

次に実際に音をならしてみましょう。まず準備でCGameManagerのStartでCSoundPlayer.LoadAllSounds(“se")でSEファイルをすべて読み込みます。

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CGameManager : MonoBehaviour
{
~中略~
void Start()
{
for(int i = 0; i < BulletFactory.Length; ++i)
{
BulletFactory[i].Load();
}
// Soundフォルダからすべての音声ファイルを読み込む
if (!CSoundPlayer.LoadAllSounds("se"))
{
print("seファイル読み込みに失敗しました");
}
}
}

鳴らしたい箇所にプログラムを組み込む

まずはプレイヤーのショット音 CSoundPlayer.PlaySound(“cshot", true);
やられた時の音 CSoundPlayer.PlaySound(“char_death", true);を追加する。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CPlayerControl : MonoBehaviour
{
void Update()
{
// Zキー押しっぱなしで6フレームに1度ショットを発射する
if (0 < InputArray["Fire1"] && InputArray["Fire1"] % 6 == 0)
{
CSoundPlayer.PlaySound("cshot", true);
if (0 < InputArray["Fire3"])//低速移動中なら
{
LowerSpeedShot();
}
else // 通常移動中なら
{
HiSpeedShot();
}
}
}
~中略~
private void OnTriggerEnter2D(Collider2D collision)
{
if(collision.tag == "Bullet")
{
CSoundPlayer.PlaySound("char_death", true);
Destroy(gameObject);
Destroy(collision.gameObject);
}
}
} 

次に敵のショット音 CSoundPlayer.PlaySound(“enemy_shot", true);の追加。

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
public class CEnemy : MonoBehaviour
{
~中略~
void Update()
{
if (Cnt < 100)
{
transform.position += new Vector3(0.0f, -3.0f, 0.0f) * Time.deltaTime;
}
if( Cnt == 300)
{
CSoundPlayer.PlaySound("enemy_shot", true);
GameObject.Find("GameManager").GetComponent<CGameManager>().BulletFactory[3].CreateBullet(transform.position, 0);
}
if (Cnt > 100 + 240)
{
transform.position += new Vector3(0.0f, 3.0f, 0.0f) * Time.deltaTime;
}
Cnt++;
if(CUtility.IsOut(transform.position))
{
Destroy(gameObject);
}
}
~中略~
}

以上でプレイヤーのショットとやられ音、敵のショット、を追加することができました。