【Unity】サウンドテスト画面を作る - その3.5

f:id:amayoshitqs:20201008013844p:plain

Unityでサウンドテスト画面を作るシリーズの番外編です。 今回は機能追加ではなく、次回に向けての整理になります。

前回はこちら。

amayoshitqs.hatenablog.com

Serializableリストを使おう

f:id:amayoshitqs:20200930230547p:plain

今まで「選択中のメニューは色濃く、選択中でないメニューは半透明」という表現で作ってきました。
それを実装しているコードは以下の通りです。

void OnChangeMenu(GameObject current)
{
    bgmText.color = current == bgm.gameObject? new Color(0, 0, 0, 1) : new Color(0, 0, 0, 0.5f);
    bgmNumberText.color = current == bgm.gameObject? new Color(0, 0, 0, 1) : new Color(0, 0, 0, 0.5f);
    seText.color = current == se.gameObject? new Color(0, 0, 0, 1) : new Color(0, 0, 0, 0.5f);
    seNumberText.color = current == se.gameObject? new Color(0, 0, 0, 1) : new Color(0, 0, 0, 0.5f);
    exitText.color = current == exit.gameObject? new Color(0, 0, 0, 1) : new Color(0, 0, 0, 0.5f);
}

今はメニューが3つしか無いのでこの書き方でも良いかなと思いますが、
メニューは増えますし、次回作る予定のボリューム調整のスライダーは今までと違って画面に表示したいですし、透明処理も「ツマミ」や「背景」などに適用する必要があります。

このOnChangeMenuがどんどん肥大化していくのを防ぐために共通の形としてMenuクラスを作り、透明処理を共通化させましょう!

[Serializable]
public class Menu
{
    public Text head;
    public Text body;
    public Slider slider;
    public Image background;
    public Image fill;

    public void ToggleActive(bool isActive)
    {
        head.color = isActive? new Color(0, 0, 0, 1) : new Color(0, 0, 0, 0.5f);
        body.color = isActive? new Color(0, 0, 0, 1) : new Color(0, 0, 0, 0.5f);
        background.color = isActive? new Color(1, 1, 1, 1) : new Color(1, 1, 1, 0.5f);
        fill.color = isActive? new Color(1, 1, 1, 1) : new Color(1, 1, 1, 0.5f);
    }
}

こんな感じで構造体を定義します。今回は同じSoundManager.cs内で良いと思います。

続いてこのMenuをSoundManagerクラス内で使えるようにします。

using System.Collections.Generic;

usingをお忘れなく。(UnityでC#スクリプト作れば初期定義されてますが)

[SerializeField] List<Menu> menuList;

メンバ変数でMenu型をListで定義します。

f:id:amayoshitqs:20201008013844p:plain

インスペクタではこのように定義します。
今はメニューは3項目あるのでSizeは3。
Headにはメニュー名、Bodyには番号等、Slider以下はSliderの各オブジェクトを参照させます。
ExitのBodyに相当するオブジェクトは無いのでHeadと同じものを置いておきましょう。

void OnChangeMenu(GameObject current)
{
    foreach(var menu in menuList)
    {
        menu.ToggleActive(current == menu.slider.gameObject);
    }
}

OnChangeMenu関数はこのようになります、スリムになりましたね。
今後どれだけメニューが増えても(多分)ここは変わらないので安心です。

完成

f:id:amayoshitqs:20201008015950p:plain

前回と同じように動くと思います。
Sceneビューで各種スライダーを見ればちゃんと透明処理をしているのも確認できます。
今はスライダーはゲーム画面に映らないので意味がないのですが、次回のボリューム調節機能で表示させる予定なので予め対応した次第です。

今回はここまでになります。
機能追加ではないので地味ですが、先を見据えて共通化できる部分はしておくことは大事なので後回しにせずに対応しておくと吉です。
見据えても見据え切れずに後悔することが多々ありますがね...

今回のソースコード

SoundManager.cs

using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[Serializable]
public class Menu
{
    public Text head;
    public Text body;
    public Slider slider;
    public Image background;
    public Image fill;

    public void ToggleActive(bool isActive)
    {
        head.color = isActive? new Color(0, 0, 0, 1) : new Color(0, 0, 0, 0.5f);
        body.color = isActive? new Color(0, 0, 0, 1) : new Color(0, 0, 0, 0.5f);
        background.color = isActive? new Color(1, 1, 1, 1) : new Color(1, 1, 1, 0.5f);
        fill.color = isActive? new Color(1, 1, 1, 1) : new Color(1, 1, 1, 0.5f);
    }
}

public class SoundManager : MonoBehaviour
{
    [SerializeField] EventSystem eventSystem;
    [SerializeField] Slider bgm;
    [SerializeField] Text bgmText;
    [SerializeField] Text bgmNumberText;
    [SerializeField] Slider se;
    [SerializeField] Text seText;
    [SerializeField] Text seNumberText;
    [SerializeField] Slider exit;
    [SerializeField] Text exitText;
    [SerializeField] Text nowPlayingTitleText;
    [SerializeField] AudioSource bgmSource;
    [SerializeField] AudioSource seSource;

    [SerializeField] List<Menu> menuList;

    int bgmLength;
    int seLength;
    GameObject go;

    void Start()
    {
        nowPlayingTitleText.gameObject.SetActive(false);

        bgmLength = Enum.GetNames(typeof(BgmList)).Length;
        bgm.maxValue = bgmLength;
        bgm.minValue = -1;

        seLength = Enum.GetNames(typeof(SeList)).Length;
        se.maxValue = seLength;
        se.minValue = -1;

        OnChangeBGM(0);
        OnChangeSE(0);

        eventSystem.SetSelectedGameObject(bgm.gameObject);
    }

void Update()
{
    var current = eventSystem.currentSelectedGameObject;

    if(current != go)
    {
        OnChangeMenu(current);
        go = eventSystem.currentSelectedGameObject;
    }

    if(Input.GetKeyDown(KeyCode.Z))
    {
        if(current == bgm.gameObject)
        {
            BgmList music = (BgmList)bgm.value;
            bgmSource.clip = Resources.Load<AudioClip>("Audio/" + music.ToString());
            bgmSource.Play();
            
            nowPlayingTitleText.gameObject.SetActive(true);
            nowPlayingTitleText.text = SoundConstants.BgmDictionary[music];
        }
        else if(current == se.gameObject)
        {
            SeList sound = (SeList)se.value;
            AudioClip clip = Resources.Load<AudioClip>("Audio/" + sound.ToString());
            seSource.PlayOneShot(clip);
        }
        else if(current == exit.gameObject)
        {
            bgmSource.Stop();
            SceneManager.LoadScene("Title");
        }
    }
}

    void OnChangeMenu(GameObject current)
    {
        foreach(var menu in menuList)
        {
            menu.ToggleActive(current == menu.slider.gameObject);
        }
    }

    public void OnChangeBGM(float value)
    {
        if(value == bgmLength)
        {
            bgm.value = 0;
        }
        if(value == -1)
        {
            bgm.value = bgmLength-1;
        }

        bgmNumberText.text = bgm.value.ToString("00");
    }

    public void OnChangeSE(float value)
    {
        if(value == seLength)
        {
            se.value = 0;
        }
        if(value == -1)
        {
            se.value = seLength-1;
        }

        seNumberText.text = se.value.ToString("00");
    }
}