【Unity入門】2Dアクションを作ろう【BGM・SE】

この記事は本のように順を追って解説しています。この記事は途中のページになります。
この記事を見ていて、現在の状況がわからない場合や忘れてしまった事などが出てきたら↓のリンクから目次ページへ飛べますので立ち戻って見てください。

<アセットストアで素材を集めよう>

さて、今までタイトルシーンとステージ1を作成してきましたが、今のところ音に関するものが全くなかったのでそろそろ実装していきたいと思います。

実装する前にアセットストアを使ってとりあえず仮の素材を集めようと思います。

アセットストアというのはUnityのマーケット的な場所で、色々な人がゲーム制作に使える素材や便利な機能などを販売しています。

販売と言っていますが無料のものもあるので、今回は無料のものをダウンロードしてこようと思います。

アセットストアを使うには注意点みたいなものが多々存在するので、一旦無料のものでどんな感じか試してみるといいと思います。まぁ、今回は仮のBGMとSEをダウンロードして試しに当てはめてみるみたいな感じです。

アセットストアを開くには上部メニューのWindow>Asset Storeから開きます。

open asset store

するとAsset Storeのウィンドウが開きます。

asset store window

サインインされてない方は右上のアイコンからサインインしてください。

右側のオーディオにチェックを入れるとオーディオに絞り込みされます。

asset store audio sort

Sort byのところをPopularity(人気順)から、Price(Low to High)(安い順)に変更します。

するとFREEのものが並ぶと思います。その中のものを適当に押してください。

asset store music

するとそのアセットの説明が表示されます。

内容プレビューのボタンを押すと中身をみる事ができます。

asset store preview

これでなんのファイルが入っているかがわかります。

ダウンロードをクリックすると

asset import

ダウンロードがインポートに変わると思います。

インポートをクリックすると

asset import select

中身をプロジェクトに入れる選択をする事ができます。チェックが入っているものがプロジェクトに入ります。右下のImportを押すとプロジェクトにインポートされます。

これでBGMになりそうなものとSEになりそうなものをダウンロードしてきてください。

今回は仮素材という事で何も見ずにダウンロードしましたが、正式なゲームとしてリリースする場合著作権に引っかかる恐れがあります。詳しい解説はまた次の機会に行いますので、今回ダウンロードしたものはあくまで「仮素材」としてください。

とりあえず、↓の2つは筆者が確認した限り、正式にリリースするゲームに含めてもOKのようですのでテスト用にこれを使わせていただきましょう。

自作ゲームに含めてもOKで、この曲を含んだゲームを販売、配布することは可能ですが、曲単体で配布、販売したり、自分がその曲を作ったと発言するのはNGになります。まぁやらないと思いますが気には止めておいてください。

<BGMを設定しよう>

さて、素材をダウンロードしてきたところでBGMを設定していきましょう。

BGMの設定は簡単です。

どのシーンでもいいのでカラのゲームオブジェクトを作成し、Add ComponentでAudio Sourceと検索してください。

add component audio clip

これをくっつけると↓のような感じになります。

play on awake loop

これのPlay On AwakeとLoopにチェックを入れます。

Play On Awakeというのはインスタンス化された瞬間から再生をするという意味になります。

Loopは読んで字のごとく曲をループさせます。

その後、AudioClipと書いてあるところの◎をクリックすると

select audio clip

今プロジェクトにあるオーディオファイルを選択する事ができます。

そのシーンで再生したい曲を選べばBGMとして再生する事ができるようになります。

これを各シーンで同じことをすればBGMの設定完了です。

簡単でしたね。

<SEを設定しよう>

SEを再生するにはちょっと工夫が必要です。

BGMで設置したAudioSourceは要するに音源です。

SEは色々な実装方法があるのですが、今回は音が何個も重なって鳴るのが困るので2つ目の音が鳴った時、1つ目の音を上書きするようにします。

要はSEの音源を1つにします。

複数設置するような設計にしても構いません。そこは音をどう扱うのか皆さん次第ですね

音源を1つにするならばゲームマネージャーをくっつけてあるゲームオブジェクトにAudio Sourceをくっつけましょう。

add audio source game manager

Audio SourceのPlay On Awakeを外しましょう。

audio source play on awake

AudioClipはつけなくてもOKです。

override game manager

ゲームマネージャーのプレハブの変更を全部のシーンでするのはめんどくさいのでApply Allを押してOverrideしましょう。

クリックすると展開します
 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 using UnityEngine.SceneManagement;
 
 public class GManager : MonoBehaviour
 {
     public static GManager instance = null;
 
     public int score = 0;
     public int stageNum = 1;
     public int continueNum = 0;
     public int heartNum = 3;
     public int defaultHeartNum = 3;
 
     public bool isGameOver = false;
 
     private AudioSource audioSource = null;
 
     private void Awake()
     {
         if(instance == null)
         {
             instance = this;
             DontDestroyOnLoad(this.gameObject);
         }
         else
         {
             Destroy(this.gameObject);
         }
     }
 
     private void Start()
     {
         audioSource = GetComponent<AudioSource>();
     }
 
     /// <summary>
     /// 残機を1つ増やす
     /// </summary>
     public void AddHeartNum()
     {
         if(heartNum < 99)
         {
             ++heartNum;
         }
     }
     
     /// <summary>
     /// 残機を1つ減らす
     /// </summary>
     public void SubHeartNum()
     {
         if(heartNum > 0)
         {
             --heartNum;
         }
         else
         {
             isGameOver = true;
         }
     }
     
     /// <summary>
     /// 最初から始める時の処理
     /// </summary>
     public void RetryGame()
     {
         isGameOver = false;
         heartNum = defaultHeartNum;
         score = 0;
         stageNum = 1;
         continueNum = 0;
     }
     
     /// <summary>
     /// SEを鳴らす
     /// </summary>
     public void PlaySE(AudioClip clip)
     {
         audioSource.PlayOneShot(clip);
     }
 }

とりあえず、くっつけたAudioSourceのインスタンスを捕まえます。

     private AudioSource audioSource = null; 
     private void Start()
     {
         audioSource = GetComponent<AudioSource>();
     }

AudioSourceから音を鳴らすのをスクリプトで行うには↓のようにします。

     /// <summary>
     /// SEを鳴らす
     /// </summary>
     public void PlaySE(AudioClip clip)
     {
         audioSource.PlayOneShot(clip);
     }

AudioClipはAudioSourceのインスペクターで設定したやつです。

AudioSourceは音源でAudioClipがSEのファイルになります。

PlayOneShotは1回だけ音を再生します。

後は音を鳴らしたいタイミングでこの関数を呼べばOKです。

クリックすると展開します
 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 
 public class player : MonoBehaviour
 {
     #region//インスペクターで設定する
     [Header("移動速度")]public float speed;
     [Header("重力")]public float gravity;
     [Header("踏みつけ判定の高さの割合")] public float stepOnRate;
     [Header("ジャンプ速度")]public float jumpSpeed;
     [Header("ジャンプする高さ")]public float jumpHeight;
     [Header("ダッシュの速さ表現")]public AnimationCurve dashCurve;
     [Header("ジャンプの速さ表現")]public AnimationCurve jumpCurve;
     [Header("ジャンプする時に鳴らすSE")]public AudioClip jumpSE; //New!
     [Header("やられた鳴らすSE")]public AudioClip downSE; //New
     [Header("コンティニュー時に鳴らすSE")]public AudioClip continueSE;  //New
     #endregion
 
     #region//プライベート変数
     private Animator anim = null;
     private Rigidbody2D rb = null;
     private CapsuleCollider2D capcol = null;
     private SpriteRenderer sr = null;
     private string groundTag = "Ground";
     private string enemyTag = "Enemy";
     private bool isGroundEnter, isGroundStay, isGroundExit;
     private bool isGround = false;
     private bool isJump = false;
     private bool isOtherJump = false;
     private bool isRun = false;
     private bool isDown = false;
     private bool isContinue = false;
     private float jumpPos = 0.0f;
     private float otherJumpHeight = 0.0f;
     private float dashTime, jumpTime,continueTime,blinkTime;
     private float beforeKey;
     #endregion
 
     private void Start()
     {
         //コンポーネントのインスタンスを捕まえる
         anim = GetComponent<Animator>();
         rb = GetComponent<Rigidbody2D>();
         capcol = GetComponent<CapsuleCollider2D>();
         sr = GetComponent<SpriteRenderer>();
     }
 
     private void Update()
     {
         if (isContinue)
         {
             //明滅 ついている時に戻る
             if(blinkTime > 0.2f)
             {
                 sr.enabled = true;
                 blinkTime = 0.0f;
             }
             //明滅 消えているとき
             else if(blinkTime > 0.1f)
             {
                 sr.enabled = false;
             }
             //明滅 ついているとき
             else
             {
                 sr.enabled = true;
             }
             
             //1秒たったら明滅終わり
             if(continueTime > 1.0f)
             {
                 isContinue = false;
                 blinkTime = 0f;
                 continueTime = 0f;
                 sr.enabled = true;
             }
             else
             {
                 blinkTime += Time.deltaTime;
                 continueTime += Time.deltaTime;
             }
         }
     }
 
     private void FixedUpdate()
     {
         if (!isDown)
         {
             GroundCheck();
             rb.velocity = new Vector2(SetX(), SetY());
             SetAnimation();
         }
     }
     
     /// <summary>
     /// ダウンアニメーションが終わっているかどうか
     /// </summary>
     /// <returns><c>true</c>, if down animation end was ised, <c>false</c> otherwise.</returns>
     public bool IsDownAnimEnd()
     {
         if (isDown && anim != null)
         {
             AnimatorStateInfo currentState = anim.GetCurrentAnimatorStateInfo(0);
             if (currentState.IsName("player_down"))
             {
                 if (currentState.normalizedTime >= 1)
                 {
                     return true;
                 }
             }
         }
         return false;
     }
     
     /// <summary>
     /// コンティニューする
     /// </summary>
     public void ContinuePlayer()
     {
         isDown = false;
         anim.Play("player_stand");
         isJump = false;
         isOtherJump = false;
         isRun = false;
         isContinue = true;
         GManager.instance.PlaySE(continueSE);  //New!
     }
     
     /// <summary>
     /// 接地しているかどうかの判定をとる
     /// </summary>
     /// <returns><c>true</c>, 接地している, <c>false</c> 接地していない</returns>
     private void GroundCheck()
     {
         if(isGroundEnter || isGroundStay)
         {
             isGround = true;
         }
         else if(isGroundExit)
         {
             isGround = false;
         }
         isGroundEnter = false;
         isGroundStay = false;
         isGroundExit = false;
     }
     
     /// <summary>
     /// Y成分で必要な計算をし、速度を返す。
     /// </summary>
     /// <returns>The y.</returns>
     private float SetY()
     {
         float verticalKey = Input.GetAxis("Vertical");
         float ySpeed = -gravity;
 
         //地面にいるとき
         if(isGround)
         {
             if (verticalKey > 0)
             {
                 ySpeed = jumpSpeed;
                 jumpPos = transform.position.y;        //ジャンプした位置を記録する
                 GManager.instance.PlaySE(jumpSE);  //New!
                 isJump = true;
             }
             else
             {
                 isJump = false;
             }
             isOtherJump = false;
             jumpTime = 0.0f;
         }
         //何かを踏んだ際のジャンプ
         else if (isOtherJump)
         {
             //現在の高さがジャンプした位置から自分の決めた位置より下ならジャンプを継続する
             if (jumpPos + otherJumpHeight > transform.position.y)
             {
                 ySpeed = jumpSpeed;
                 jumpTime += Time.deltaTime;
             }
             else
             {
                 isOtherJump = false;
                 jumpTime = 0.0f;
             }
         }
         //ジャンプ中
         else if(isJump)
         {
             //上ボタンを押されている。かつ、現在の高さがジャンプした位置から自分の決めた位置より下ならジャンプを継続する
             if (verticalKey > 0 && jumpPos + jumpHeight > transform.position.y)
             {
                 ySpeed = jumpSpeed;
                 jumpTime += Time.deltaTime;
             }
             else
             {
                 isJump = false;
                 jumpTime = 0.0f;
             }
         }
         if (isJump || isOtherJump)
         {
             ySpeed *= jumpCurve.Evaluate(jumpTime);
         }
         return ySpeed;
     }
 
     /// <summary>
     /// X成分で必要な計算をし、速度を返す。
     /// </summary>
     /// <returns>The x.</returns>
     private float SetX()
     {
         float xSpeed;
         float horizontalKey = Input.GetAxis("Horizontal");
         
         if (horizontalKey > 0)
         {
             transform.localScale = new Vector3(1, 1, 1);
             isRun = true;
 
             xSpeed = speed;
             dashTime += Time.deltaTime;
         }
         else if (horizontalKey < 0)
         {
             transform.localScale = new Vector3(-1, 1, 1);
             isRun = true;
             xSpeed = -speed;
             dashTime += Time.deltaTime;
         }
         else
         {
             isRun = false;
             xSpeed = 0.0f;
             dashTime = 0.0f;
         }
         
         if (horizontalKey > 0 && beforeKey < 0)
         {
             dashTime = 0.0f;
         }
         else if (horizontalKey < 0 && beforeKey > 0)
         {
             dashTime = 0.0f;
         }
         
         xSpeed *= dashCurve.Evaluate(dashTime);
         beforeKey = horizontalKey;
         return xSpeed;
     }
     
     /// <summary>
     /// アニメーションを設定する
     /// </summary>
     private void SetAnimation()
     {
         anim.SetBool("jump", isJump || isOtherJump);
         anim.SetBool("ground", isGround);
         anim.SetBool("run", isRun);
     }
 
     #region//接地判定
     private void OnTriggerEnter2D(Collider2D collision)
     {
         if (collision.tag == groundTag)
         {
             isGroundEnter = true;
         }
     }
 
     private void OnTriggerStay2D(Collider2D collision)
     {
         if (collision.tag == groundTag)
         {
             isGroundStay = true;
         }
     }
     
     private void OnTriggerExit2D(Collider2D collision)
     {
         if (collision.tag == groundTag)
         {
             isGroundExit = true;
         }
     }
     #endregion
     
     #region//接触判定
     private void OnCollisionEnter2D(Collision2D collision)
     {
         if (collision.collider.tag == enemyTag)
         {
 
             //踏みつけ判定になる高さ
             float stepOnHeight = (capcol.size.y * (stepOnRate / 100f));
             //踏みつけ判定のワールド座標
             float judgePos = transform.position.y - (capcol.size.y / 2f) + stepOnHeight;
             foreach (ContactPoint2D p in collision.contacts)
             {
                 if (p.point.y < judgePos)
                 {
                     ObjectCollision o = collision.gameObject.GetComponent<ObjectCollision>();
                     if (o != null)
                     {
                         otherJumpHeight = o.boundHeight;    //踏んづけたものから跳ねる高さを取得する
                         o.playerStepOn = true;        //踏んづけたものに対して踏んづけた事を通知する
                         isOtherJump = true;
                         isJump = false;
                         jumpTime = 0.0f;
                     }
                     else
                     {
                         Debug.Log("ObjectCollisionが付いてないよ!");
                     }
                 }
                 else
                 {
                     anim.Play("player_down");
                     isDown = true;
                     GManager.instance.SubHeartNum();
                     GManager.instance.PlaySE(downSE);  //New!
                     break;
                 }
             }
         }
     }
     #endregion
 }
 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 
 public class ScoreItem : MonoBehaviour
 {
     [Header("加算スコア")] public int myScore;
     [Header("アイテム取得時に鳴らすSE")] public AudioClip itemSE;
 
     private string playerTag = "Player";
     private void OnTriggerEnter2D(Collider2D collision)
     {
         if (collision.tag == playerTag)
         {
             if (GManager.instance != null)
             {
                 GManager.instance.score += myScore;
                 GManager.instance.PlaySE(itemSE);
                 Destroy(this.gameObject);
             }
         }
     }
 }
 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 
 public class deadPoint : MonoBehaviour
 {
     [Header("ステージコントローラー")] public stageCtrl ctrl;
     [Header("ゲームオーバー時に鳴らすSE")] public AudioClip gameOverSE;
 
     private string playerTag = "Player";
     private void OnTriggerEnter2D(Collider2D collision)
     {
         if (collision.tag == playerTag)
         {
             if (GManager.instance != null)
             {
                 GManager.instance.SubHeartNum();
                 if (!GManager.instance.isGameOver)
                 {
                     ctrl.PlayerSetContinuePoint();
                 }
                 else
                 {
                     GManager.instance.PlaySE(gameOverSE);
                 }
             }
         }
     }
 }
 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine.SceneManagement;
 using UnityEngine;
 
 public class title : MonoBehaviour
 {
     [Header("フェード")] public FadeImage fade;
     [Header("ゲームスタート時に鳴らすSE")] public AudioClip startSE;
 
     private bool firstPush = false;
 
     // Start is called before the first frame update
     void Start()
     {
     }
 
     // Update is called once per frame
     void Update()
     {
         if (fade.compFadeOut)
         {
             SceneManager.LoadScene("stage1");
         }
     }
 
     public void PressStart()
     {
         Debug.Log("Press Start!");
         if (!firstPush)
         {
             if (fade != null)
             {
                 GManager.instance.PlaySE(startSE);
                 fade.StartFadeOut();
                 firstPush = true;
             }
         }
     }
 }
 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 
 public class enemy_zako1 : MonoBehaviour
 {
     #region//インスペクターで設定する
     [Header("加算スコア")] public int myScore;
     [Header("移動速度")] public float speed;
     [Header("重力")] public float gravity;
     [Header("画面外でも行動する")] public bool nonVisibleAct;
     [Header("接触判定")] public EnemyCollisionCheck checkCollision;
     [Header("やられた時に鳴らすSE")] public AudioClip deadSE;
     #endregion
 
     #region//プライベート変数
     private Rigidbody2D rb = null;
     private SpriteRenderer sr = null;
     private Animator anim = null;
     private ObjectCollision oc = null;
     private BoxCollider2D col = null;
     private bool rightTleftF = false;
     private bool isDead = false;
     private float deadTimer = 0.0f;
     #endregion
 
     // Start is called before the first frame update
     void Start()
     {
         rb = GetComponent<Rigidbody2D>();
         sr = GetComponent<SpriteRenderer>();
         anim = GetComponent<Animator>();
         oc = GetComponent<ObjectCollision>();
         col = GetComponent<BoxCollider2D>();
     }
 
     void FixedUpdate()
     {
         if (!oc.playerStepOn)
         {
             if (sr.isVisible || nonVisibleAct)
             {
                 if (checkCollision.isOn)
                 {
                     rightTleftF = !rightTleftF;
                 }
                 int xVector = -1;
                 if (rightTleftF)
                 {
                     xVector = 1;
                     transform.localScale = new Vector3(-1, 1, 1);
                 }
                 else
                 {
                     transform.localScale = new Vector3(1, 1, 1);
                 }
                 rb.velocity = new Vector2(xVector * speed, -gravity);
                 anim.SetBool("walk", true);
             }
             else
             {
                 anim.SetBool("walk", false);
             }
         }
         else
         {
             if (!isDead)
             {
 
                 anim.Play("dead");
                 rb.velocity = new Vector2(0, -gravity);
                 isDead = true;
                 col.enabled = false;
                 if (GManager.instance != null)
                 {
                     GManager.instance.PlaySE(deadSE);
                     GManager.instance.score += myScore;
                 }
             }
             else
             {
                 transform.Rotate(new Vector3(0, 0, 5));
                 if (deadTimer > 3.0f)
                 {
                     Destroy(this.gameObject);
                 }
                 else
                 {
                     deadTimer += Time.deltaTime;
                 }
             }
         }
     }
 }

各種スクリプトに音を鳴らす処理を追加しました。

音も今回、仮ですので、とりあえず鳴ることがテストできたらOKかなと思います。

<わからない事、質問等があれば>

このサイトの説明ではよくわからなかったとか、もっと知りたい事などがあれば

自分の Youtubeの動画にコメントで質問していただければ動画でお答えしようと思います。

文章同士のやり取りだと伝わりづらいし、ラリーに時間がかかりそうなので動画で回答します。

↓の動画が回答の一例になります。どの動画でもいいのでご遠慮なくコメントしてください

できたらチャンネル登録よろしくお願いします!



タイトルとURLをコピーしました