UnityでFPSをつくる その11-1 [ 音 ]
今回からは「音」を付けていきます。
実装予定の音は以下のとおりです。

音素材を用意してあるので、以下のファイルをダウンロードしてください。
解凍して中身を見てみると、全部で11の音声ファイルが入っています。

“Scratchは、MITメディア・ラボのライフロング・キンダーガーテン・グループの協力により、Scratch財団が進めているプロジェクトです。https://scratch.mit.edu から自由に入手できます。"
まずは、BGMを付けていきましょう。
ProjectウィンドウのAssetsフォルダ直下に「Sounds」という名前で新規にフォルダを作成してください。
Soundsフォルダの直下には「BGM」「SE」という名前でフォルダを作成。
さきほどダウンロードしたファイルの内、ファイル名に"BGM"と付いているものはBGMフォルダへ、それ以外はSEフォルダへインポートします。

TitleSceneに切り替えて、SceneManagerを選択、
Add Componentから「Audio」→「Audio Source」を選択してください。

追加されたAudio Sourceの「AudioClip」に「BGM_TitleScene」ファイルを指定、
「Loop」のチェックボックスにチェックを入れてください。

Unityで音が聞こえる仕組みは、以下のようなイメージです。

鳴らしたい音を、AudioClipとしてAudioSourceから再生し、AudioListenerで音を聞きます。
音を聞く役割をするAudioListenerは、予めCameraにComponentとしてアタッチされています。

実際に音が鳴るようになったか、実行して確認します。
AudioSourceの「Play On Awake」「Loop」にチェックが入っているため、シーン起動と同時にBGMが繰り返し再生されるようなりました。

次はボタンがクリックされた際の”決定音”を実装していきます。

決定音は、ユーザーがボタンをクリックした任意のタイミングで鳴らしたいので、スクリプトを使います。
Scriptsフォルダへ「DecisionSoundManage」と名付けたスクリプトを、新規に作成してください。

ここに以下のコードをコピペします。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DecisionSoundManage : MonoBehaviour
{
[SerializeField] AudioClip decisionSound;
AudioSource audioSource;
//1回だけ音を鳴らす為の判定フラグ
bool justOnce_decisionSound;
void Start()
{
audioSource = GetComponent<AudioSource>();
}
void Update()
{
//マウスの左ボタンがクリックされたら
if (Input.GetMouseButtonDown(0) && !justOnce_decisionSound)
{
//重ねて再生したい音を鳴らす
audioSource.PlayOneShot(decisionSound);
justOnce_decisionSound = true;
}
}
}
7,9行目でAudioClipとAudioSourceを操作する為の変数を宣言。

12行目では、ボタンが1回押された時だけ決定音が再生されればいいので、2回目以降と区別する為のフラグに使う変数を宣言しています。

16行目で、自身のAudioSourceコンポーネントを取得。

22~28行目で、ボタン押下時にAudioSourceからAudioClipを再生して、2回目以降に反応しないようフラグを立てています。

既に再生されているBGMに重ねて音を鳴らす為に、PlayOneShot(AudioClip clip)メソッドを使っています。
このスクリプトをSceneManagerオブジェクトにアタッチし、DecisionSoundの項目に同名の音声ファイルを指定します。

実行して、聞いてみましょう。
連続でボタンを押しても、1回しか決定音が再生されないように実装できました。
しかし、「PRESS START」のUIがボタン連打に反応してしまう不具合が見つかりました。
これを修正していきましょう。
「ToMainScene」スクリプトを開き、以下をコピペしてください。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
//シーンを扱う為の名前空間
using UnityEngine.SceneManagement;
public class ToMainScene : MonoBehaviour
{
//11-1追記
bool justOnce = false;
[SerializeField] Text pressStart;
[SerializeField] Image fadeBoard;
[SerializeField] SpriteRenderer titleImage;
//色、透明度
float fadeBoardColorRed, fadeBoardColorGreen, fadeBoardColorBlue, fadeBoardColorAlpha,
titleImageColorRed, titleImageColorGreen, titleImageColorBlue, titleImageColorAlpha,
pressStartColorRed, pressStartColorGreen, pressStartColorBlue, pressStartColorAlpha;
//点滅の間隔
float textBlinkDuration = 8f;
//アルファ値加算許可、スタートボタン押下済みフラグ
bool alphaAdditionFlag, pressStartDoneFlag;
// Start is called before the first frame update
void Start()
{
//colorの個々の値は取得できても変更できない為、変数に代入する
fadeBoardColorRed = fadeBoard.color.r;
fadeBoardColorGreen = fadeBoard.color.g;
fadeBoardColorBlue = fadeBoard.color.b;
fadeBoardColorAlpha = fadeBoard.color.a;
titleImageColorRed = titleImage.color.r;
titleImageColorGreen = titleImage.color.g;
titleImageColorBlue = titleImage.color.b;
titleImageColorAlpha = titleImage.color.a;
pressStartColorRed = pressStart.color.r;
pressStartColorGreen = pressStart.color.g;
pressStartColorBlue = pressStart.color.b;
pressStartColorAlpha = pressStart.color.a;
}
// Update is called once per frame
void Update()
{
BeforePressStart();
if (Input.GetMouseButtonDown(0) && !justOnce)
{
//11-1追記
//連打されて何度も呼び出されないようにする為
justOnce = true;
//スタートボタン押下前のテキスト点滅間隔を終了
pressStartDoneFlag = true;
StartCoroutine(FadeOut());
}
}
IEnumerator FadeOut()
{
//フェードアウト
var isFadeOut = true;
//何秒かけて暗転させるか
var fadeOutDuration = 3f;
//点滅の間隔
var textBlinkDurationAfterPressStart = 0.3f;
while (isFadeOut)
{
//fadeOutDurationの時間で暗転するよう、アルファ値を加算
fadeBoardColorAlpha += Time.deltaTime / fadeOutDuration;
//titleImageはfadeBoard暗転の影響を受けないので、個別にアルファ値を減算
titleImageColorAlpha -= Time.deltaTime / fadeOutDuration;
//STARTキーのクリック前より、短い間隔でアルファ値 0.5 ~ 1 の間を行き来するようにする
if (pressStartColorAlpha >= 0.5f && !alphaAdditionFlag)
{
pressStartColorAlpha -= Time.deltaTime / textBlinkDurationAfterPressStart;
pressStart.color = new Color(pressStartColorRed, pressStartColorGreen, pressStartColorBlue, pressStartColorAlpha);
if (pressStartColorAlpha <= 0.5f)
{
alphaAdditionFlag = true;
}
}
else if (pressStartColorAlpha <= 1f && alphaAdditionFlag)
{
pressStartColorAlpha += Time.deltaTime / textBlinkDurationAfterPressStart;
pressStart.color = new Color(pressStartColorRed, pressStartColorGreen, pressStartColorBlue, pressStartColorAlpha);
if (pressStartColorAlpha >= 1f)
{
alphaAdditionFlag = false;
}
}
//アルファ値以外は変更せずにcolorに値を代入
fadeBoard.color = new Color(fadeBoardColorRed, fadeBoardColorGreen, fadeBoardColorBlue, fadeBoardColorAlpha);
titleImage.color = new Color(titleImageColorRed, titleImageColorGreen, titleImageColorBlue, titleImageColorAlpha);
//fadeBoardの透明度が1(完全に不透明)かつ、titleImageの透明度が0(完全に透明)になったら
if (fadeBoardColorAlpha >= 1 && titleImageColorAlpha <= 0)
{
//フェードアウト終了
isFadeOut = false;
}
yield return null;
}
yield return new WaitForSeconds(1f);
//メインシーンに切り替え
SceneManager.LoadScene("MainScene");
}
//スタートボタン押下前のテキスト点滅
void BeforePressStart()
{
//アルファ値加算許可&スタートボタン押下済みフラグが立っていない場合
if (pressStartColorAlpha >= 0.5f && !alphaAdditionFlag && !pressStartDoneFlag)
{
pressStartColorAlpha -= Time.deltaTime / textBlinkDuration;
pressStart.color = new Color(pressStartColorRed, pressStartColorGreen, pressStartColorBlue, pressStartColorAlpha);
if (pressStartColorAlpha <= 0.5f)
{
alphaAdditionFlag = true;
}
}
//完全に透明にならないよう、アルファ値 0.5 ~ 1 の間を行き来するようにする
else if (pressStartColorAlpha <= 1f && alphaAdditionFlag && !pressStartDoneFlag)
{
pressStartColorAlpha += Time.deltaTime / textBlinkDuration;
pressStart.color = new Color(pressStartColorRed, pressStartColorGreen, pressStartColorBlue, pressStartColorAlpha);
if (pressStartColorAlpha >= 1f)
{
alphaAdditionFlag = false;
}
}
}
}
追記部分を見ていきましょう。
11行目、決定音の時と同様に、2回目以降と区別する為のフラグを宣言して初期化しています。

68行目、ボタンが1回押された時にフラグを立て、2回目以降のボタン押下に反応しないようにしています。

再度実行してみましょう。
想定通りの挙動になりました。
長くなってきたので続きは次回にしたいと思います。おつかれさまでした!