yumeの日記: 正しいコード 6
メデューサ・ゲームのシナリオは音楽を元に作っており、肝心の音楽(友人が担当している)がまだできていないので第二話が完成したところで待機中である。
さりとてしばらくコードを書かなければ、さっぱり忘れてしまいそうである。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なる仕組みがあって、これを使えばすんなり実装できるのであった。
とっぴんぱらりのぷう。
キー入力で使えるのかな。 (スコア:2)
KeyConfigクラスとと、キー入力用のクラスInputControlを書いていたので、InputControlはプレイヤーの参照を持たずに、
public Action onKeyDownGuardTop = null;
みたいなメンバー(変数? メソッドではないよな)を必要なだけ持つ。
でキーコンフィグで設定しておいたキーが入力されたらそのActionを呼び出す。
他のオブジェクト(プレイヤーとかポーズメニューとか)はそのシーンの開始時にInputControlに+=しておく。
するとうまく動いてくれた。たくさんActionができるのはどうかなと思ったけど、オブジェクトの関係性がきれいなのでこれでいこう。
Re: (スコア:0)
eventを使うのがいいと思います。
クラス外から登録と解除ができてクラス内からのみ呼べるマルチデリゲートなプロパティ。
Re: (スコア:0)
イベント [ufcpp.net]について。
Re: (スコア:0)
まあ普通に考えるとイベントドリブン的に実装するのがいいよね。
手段はいくつも思いつくけどどれが正しいかわからない (スコア:0)
ありがちですねー。
フレームワークが複雑になると特に。
今更だけど (スコア:0)
DontDestroyOnloadとかの概念と
staticとかInstanceとかを学習すると良さそう。
アプリ起動時にサウンド処理系のclassを呼べばずっと常駐する
ゲーム系のアプリで音が不要になることはないので、それとずっと使う感じ。
既にいるのでSceneを切り替えてもずっといるし、Sceneでわざわざ呼ばなくても良い。
あとscene切り替え時にも同じ曲を流しておける。