パスワードを忘れた? アカウント作成
15085373 journal
日記

yumeの日記: 正しいコード 5

日記 by yume

メデューサ・ゲームのシナリオは音楽を元に作っており、肝心の音楽(友人が担当している)がまだできていないので第二話が完成したところで待機中である。
さりとてしばらくコードを書かなければ、さっぱり忘れてしまいそうである。iPadを買ったので背景を描きたしたり、swiftの学習などやってみるなどした。
そんなおり、itch.ioで2週間期限のゲームジャムが開催されているではないか。というわけで参加したのだった。

--

それはそれでよいのだが、さて新たにプロジェクトを立ち上げて、いざコードを書いていくと以前よりずっときれいなコードが書けている実感がある。今回は期限も厳しいのだから、尚更きれいなコードを書くことを強く意識していきたい。

ところで問題がひとつ。音量調節機能(メデューサゲームではすでに作っているが)の仕組みを改めて考えると、ちょっと気に食わない部分が出てきた。
以下は音量を管理するサウンドコンフィグ用のスクリプトである(実際にはさらにBGMとSE用の音量とスライダーも管理する)。


/// <summary>
/// 音量を管理する
/// </summary>
public class SoundConfig : MonoBehaviour
{
        /// <summary>
        /// マスター音量。
        /// </summary>
        public float MasterVolume { get; private set; } = 0.5f;

        /// <summary>
        /// マスター音量のスライダー。Unity側で参照を繋ぐ。
        /// </summary>
        [SerializeField] private Slider masterVolumeSlider = default;

        /// <summary>
        /// スライダーでマスター音量を変更。
        /// </summary>
        public void OnSliderMasterVolume()
        {
                MasterVolume = masterVolumeSlider.value;
        }
}

UnityUIのスライダー機能は、好きな値をスライダーで動かしたタイミングですぐに返してくれる。よってOnSliderMasterVolumeメソッドは、スライダーを動かしたら即座にコンフィグで保持する値を変えてくれる。

さて、今回のプロジェクトにはBGMを管理しているクラス「MusicJockey」がシーン上にほぼ必ずいる。BGMは常にかかっているわけだから、音量スライダーを動かしたら当然すぐに反映してあげたい。
そうなると……素直に考えるなら、こういうコードを追加することになりそうだ。


/// <summary>
/// シーン上にいるMusicJockey(BGM管理者)
/// </summary>
[SerializeField] private MusicJockey musicJockey = default;

/// <summary>
/// スライダーでマスター音量を変更。
/// </summary>
public void OnSliderMasterVolume()
{
        MasterVolume = masterVolumeSlider.value;
        musicJockey.ChangeVolume(MasterVolume);
}

これがいかにも気に食わない。まぁゲームではほぼ全てのシーンにMusicJockeyはいるだろうから、実用上問題はない。実際、メデューサ・ゲームではこういうコードを書いた。
でも、仮にMusicJockeyがいないシーンを今後作りたいとなったとしたら、そのシーンのSoundConfigはMusicJockeyの参照を空で持つか、意味のないMusicJockeyを持つ必要があるだろう。
あるいはMusicJockeyが複数必要なシーンがでてきたとしたら?

このふたつのクラスの関係性を考えると、SoundConfigにとってMusicJockeyは必須ではない一方、MusicJockeyにとってはSoundConfigは必須だ。
なら、MusicJockey側からSoundConfigへの参照を持つのが「正しい」ように思う。
しかし……SoundConfigがMusicJockeyへの参照なしに、音量を変えたことをMusicJockeyへどう伝えるかがイマイチわからない。
例えばMusicJockeyが、参照しているSoundConfigに全てのフレームで値を尋ね続ける……なんてのは明らかに馬鹿馬鹿しい。

色々調べたところデリゲートというものが使えるらしい。
よっしゃじゃあ試してみるか……。と思ったがUnityにはAudio Mixerなる仕組みがあって、これを使えばすんなり実装できるのであった。

とっぴんぱらりのぷう。

  • KeyConfigクラスとと、キー入力用のクラスInputControlを書いていたので、InputControlはプレイヤーの参照を持たずに、
    public Action onKeyDownGuardTop = null;
    みたいなメンバー(変数? メソッドではないよな)を必要なだけ持つ。
    でキーコンフィグで設定しておいたキーが入力されたらそのActionを呼び出す。
    他のオブジェクト(プレイヤーとかポーズメニューとか)はそのシーンの開始時にInputControlに+=しておく。

    するとうまく動いてくれた。たくさんActionができるのはどうかなと思ったけど、オブジェクトの関係性がきれいなのでこれでいこう。

    ここに返信
    • by Anonymous Coward

      eventを使うのがいいと思います。
      クラス外から登録と解除ができてクラス内からのみ呼べるマルチデリゲートなプロパティ。

      • by Anonymous Coward

        イベント [ufcpp.net]について。

      • by Anonymous Coward

        まあ普通に考えるとイベントドリブン的に実装するのがいいよね。

  • ありがちですねー。
    フレームワークが複雑になると特に。

    ここに返信
typodupeerror

一つのことを行い、またそれをうまくやるプログラムを書け -- Malcolm Douglas McIlroy

読み込み中...