超初心者のゲーム開発記~Unity~

超初心者がUnityでゲーム(目標はAndroidアプリ )を開発していくブログです!

【Unity 2Dローグライク】公式チュートリアルをやってみる part.14~ゲームに音をつける

こんにちは!ヤギです!

Unityの2Dローグライクの
公式チュートリアルをやってみる part.14になります!
※前回(part.13)の記事はこちら
www.yagigame.com

記事概要

今回の記事では、ゲーム内の効果音やBGMについてご説明していきます。
※part.14に対応する公式チュートリアルの章は、Audio and Sound Managerです!
unity3d.com

※この記事は、ひよこのたまご様の記事をリスペクトしています。
hiyotama.hatenablog.com

BGMを鳴らす

まず初めに、音を鳴らすオブジェクトを作成します。
1. GameObject > Create Empty をクリックし、名前をSoundManagerとします。
f:id:yagigame:20181107224621p:plain
2. Inspector > Add ComponentからAudio Sourceコンポーネントを2つ追加します。
※効果音と、BGM用になります!
f:id:yagigame:20181107224946p:plain
3. 音を管理するスクリプト(SoundManager.cs)を作成します。
※ソースの中に解説を記載しております。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SoundManager : MonoBehaviour {
    public AudioSource efxSource; //効果音
    public AudioSource musicSource; //BGM
    public static SoundManager instance = null;

    //音の速さの範囲
    public float lowPitchRange = .95f;
    public float highPitchRange = 1.05f;

    // Use this for initialization
    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();
    }
}

4. SoundManager.csをHierarchy viewのSoundManagerオブジェクトにドラッグし、アタッチします。
5. SoundManager > Inspector viewで、SoundManager.csのEfx SourceとMusic Sourceに、Audio Sourceをドラッグします。
f:id:yagigame:20181107230854p:plain
6. BGM用のAudio Source > AudioClipにscavenger_musicを設定します。
7. BGM用のAudio Souceは音を繰り返し流すために、Loopにチェックをつけます。
8. 効果音用のAudio Sourceは、ゲーム実行と同時に音を鳴らすわけではないので、Play On Awakeのチェックを外します。
f:id:yagigame:20181107231735p:plain
※ここで、一度ゲームを実行してみましょう!BGMが再生されます。

プレイヤーの効果音を鳴らす

1. プレイヤーの効果音を鳴らすために、Player.csを修正していきます。
※ソース内の解説をご確認ください。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement; //シーンの読み込みに必要
using UnityEngine.UI; //part.13で追加

//MovingObjectクラスを継承する
public class Player : MovingObject {
    //ここからpart.14で追加
    //効果音
    public AudioClip moveSound1;
    public AudioClip moveSound2;
    public AudioClip eatSound1;
    public AudioClip eatSound2;
    public AudioClip drinkSound1;
    public AudioClip drinkSound2;
    public AudioClip gameoverSound;
    //ここまでpart.14で追加

    //ここからpart.13で追加
    public Text foodText; //食料を表示するテキスト
    //ここまでpart.13で追加

    public int wallDamage = 1; //壁へのダメージ量
    public int pointsPerFood = 10; //食べ物の回復量
    public int pointsPerSoda = 20; //ソーダの回復量
    public float restartLevelDeray = 1f; //ステージ移動時の時間

    private Animator animator; //アニメーション用変数
    private int food; //食料


	// Use this for initialization
    //MovingObjectクラスのStartを継承する
    protected override void Start () {
        //animatorのコンポーネントを設定
        animator = GetComponent<Animator>();
        //foodをステージ間で引き継げるように、GameManagerから設定
        food = GameManager.instance.playerFoodPoints;
        //ここからpart.13で追加
        //foodTextを初期化
        foodText.text = "Food: " + food;
        //ここまでpart.13で追加
        //GameObjectのStartを呼び出す
        base.Start();
	}

    //PlayerのfoodをGameManageに保存する
    public void OnDisable()
    {
        GameManager.instance.playerFoodPoints = food;
    }

    protected override void AttemptMove<T>(int xDir, int yDir)
    {
        //移動するたびに食料が減る
        food--;
        //ここからpart.13で追加
        //減らした食料をUIに表示する
        foodText.text = "Food: " + food;
        //ここまでpart.13で追加
        //MovingObjectのAttemptMoveを呼び出す
        base.AttemptMove<T>(xDir, yDir);

        RaycastHit2D hit;

        //ここからpart.14で追加
        //動いた時の効果音を鳴らす
        if(Move(xDir,yDir,out hit)){
            SoundManager.instance.RandomizeSFx(moveSound1, moveSound2);
        }
        //ここまでpart.14で追加

        //ゲームオーバーか確認
        CheckIfGameOver();
        //プレイヤーのターン終了
        GameManager.instance.playerTurn = false;
    }

    //プレイヤーが壁にぶつかった場合、壁をチョップする
    protected override void OnCantMove<T>(T component)
    {
        //Wallスクリプトを使えるように設定
        Wall hitWall = component as Wall;
        //壁にダメージを与える
        hitWall.DamageWall(wallDamage);
        //チョップするアニメーションを呼び出す
        animator.SetTrigger("PlayerChop");
    }

    //プレイヤーが、Exit、Food、Sodaと接触した場合に呼び出す
    private void OnTriggerEnter2D(Collider2D other)
    {
        //Exitと接触した場合
        if(other.tag == "Exit"){
            //ステージ移動の時間分待ってから、次のツテージに移動する
            Invoke("Restart", restartLevelDeray);
            enabled = false;
        }else if(other.tag == "Food"){ //食料と接触した場合
            //食料を回復
            food += pointsPerFood;
            //ここからpart.13で追加
            //増えた食料をUIに表示する
            foodText.text = "+"+pointsPerFood+" Food: " + food;

            //ここからpart.14で追加
            //食べた時の効果音を鳴らす
            SoundManager.instance.RandomizeSFx(eatSound1, eatSound2);
            //ここまでpart.14で追加

            //ここまでpart.13で追加
            //食料を削除
            other.gameObject.SetActive(false);
        }else if(other.tag == "Soda"){
            //食料を回復
            food += pointsPerSoda;
            //ここからpart.13で追加
            //増えた食料をUIに表示する
            foodText.text = "+" + pointsPerFood + " Food: " + food;

            //ここからpart.14で追加
            //飲んだ時の効果音を鳴らす
            SoundManager.instance.RandomizeSFx(drinkSound1, drinkSound2);
            //ここまでpart.14で追加

            //ここまでpart.13で追加
            //ソーダを削除
            other.gameObject.SetActive(false);
        }
    }

    //プレイヤーがExitに到達した場合、次のステージを呼び出す
    private void Restart(){
        //シーンを呼び直す
        SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex,LoadSceneMode.Single);
    }

    //プレイヤーが敵に攻撃された場合、食料を減らす
    public void LoseFood(int loss){
        //攻撃を受けたアニメーションを呼び出す
        animator.SetTrigger("PlayerHit");
        //食料を減らす
        food -= loss;
        //ここからpart.13で追加
        //減らした食料をUIに表示する
        foodText.text = "-" + loss+ " Food: " + food;
        //ここまでpart.13で追加
        //ゲームオーバーか判定
        CheckIfGameOver();
    }

    // Update is called once per frame
    void Update () {
        //プレイヤーのターンではない場合、何も実行しない
        if (!GameManager.instance.playerTurn)
        {
            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);
        }
	}

    //食料が0いかになった場合、ゲームオーバーにする
    private void CheckIfGameOver(){
        if(food <= 0){

            //ここからpart.14で追加
            //ゲームオーバーの時の効果音を鳴らす
            SoundManager.instance.PlaySingle(gameoverSound);
            SoundManager.instance.musicSource.Stop();
            //ここまでpart.14で追加

            //GameManagerのGameOverを呼び出す
            GameManager.instance.GameOver();
        }
    }
}

2. Player > InspectorのAudio Clipを以下の画像の内容で設定します。
f:id:yagigame:20181108232637p:plain
※これで、プレイヤーの効果音が鳴るようになりました。

敵の効果音を鳴らす

1. 敵の効果音を鳴らすために、Enemy.csを修正していきます。
※ソース内の解説をご確認ください!

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//MovingObjectを継承する
public class Enemy : MovingObject {

    //ここからpart.14で追加
    //敵の攻撃の効果音
    public AudioClip enemyAttack1;
    public AudioClip enemyAttack2;
    //ここまで、part.14で追加

    public int playerDamage; //プレイヤーへのダメージ

    private Animator animator;
    private Transform target; //プレイヤー場所
    private bool skipMove; //動くか判定


	// Use this for initialization
    //継承クラス
    protected override void Start () {

        //ここからpart.12で追加
        //敵をリストに加える
        GameManager.instance.AddEnemyList(this);
        //ここまでpart.12で追加

        //Animatorコンポーネント取得
        animator = GetComponent<Animator>();
        //プレイヤーの場所を取得する
        target = GameObject.FindGameObjectWithTag("Player").transform;
        //MovingObjectクラスのStartを呼び出す
        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; //上下の移動量

        //敵とプレイヤーの左右の距離がほぼ0の場合
        if(Mathf.Abs(target.position.x - transform.position.x) < float.Epsilon){
            //プレイヤーが上にいれば、1とし、下にいれば、-1とする
            yDir = target.position.y > transform.position.y ? 1 : -1;
        }else{
            //プレイヤーが右にいれば、1とし、左にいれば、-1とする
            xDir = target.transform.position.x > transform.position.x ? 1 : -1;
        }
        //移動する
        AttemptMove<Player>(xDir, yDir);
    }

    //敵が移動できない時の処理
    protected override void OnCantMove<T>(T component)
    {
        //衝突したプレイヤーを設定
        Player hitPlayer = component as Player;

        //ここからpart.12で追加
        //プレイヤーに攻撃するアニメーションを呼び出す
        animator.SetTrigger("EnemyAttack");
        //ここまでpart.12で追加

        //プレイヤーの食料を減らす
        hitPlayer.LoseFood(playerDamage);

        //ここからpart.14で追加
        //攻撃する効果音を鳴らす
        SoundManager.instance.RandomizeSFx(enemyAttack1, enemyAttack2);
        //ここまでpart.14で追加
    }
}

2. Enemy1とEnemy2 > InspectorのAudio Clipを以下の画像の内容で設定します。
f:id:yagigame:20181108233355p:plain
※これで、敵の効果音が鳴るようになりました。

壁が攻撃された時の効果音を鳴らす

1. 次に、壁が攻撃された時の効果音が鳴るようするために、Wall.csを修正していきます。
※ソース内の解説をご確認ください!

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Wall : MonoBehaviour {
    //ここからpart.14で追加
    public AudioClip chopSound1;
    public AudioClip chopSound2;
    //ここまでpart.14で追加

    public Sprite dmgSprite;
    public int hp = 4;
    private SpriteRenderer spriteRenderer;

	// Use this for initialization
	void Awake () {
        spriteRenderer = GetComponent<SpriteRenderer>();
	}
	
    //壁が攻撃された場合の処理
    public void DamageWall(int loss){
        //ここからpart.14で追加
        SoundManager.instance.RandomizeSFx(chopSound1, chopSound2);
        //ここまでpart.14で追加

        spriteRenderer.sprite = dmgSprite;
        //壁のhpを減らす
        hp -= loss;
        //hpが0になった場合
        if(hp <= 0){
            //壁を破壊する
            gameObject.SetActive(false);
        }
    }
}

2. Wall1からWall8 > InspectorのAudio Clipを以下の画像の内容で設定します。
f:id:yagigame:20181108235249p:plain
※これで、壁が攻撃された時の効果音がなるようになりました。

ゲームを実行してみよう!

ゲームを実行してみましょう!
プレイヤーが移動した時や、敵が攻撃した時など、効果音がなるようになりました!

次回予告

次回の記事では、スマートフォンに対応させる方法について書いていきたいと思います。
※次回で最終回となります!!!
※Unityのチュートリアルの章としては、次回は(Adding Mobile Controls)となります!

読んでいただきありがとうございました!