見出し画像

ダンスゲーを作りたい#40 【試作】プレイシステムの基盤その2(曲の再生管理とアニメーションの同期)

前回なんかものすごく進んだので今回はしょぼく感じますが、今回は曲関係を片付けていこうと思います。

やることとしては

・移動が完了しプレイ開始の段階でAudioManager経由で曲を流す

に相当しますが、実際はゲーム内全体の音系をすべて管理する大枠を作りましょう。

AudioManager

まずこれは独自実装な点に注意してください。今までのマネージャーもそうですが、実態は単なるスクリプトです。詳細は過去記事で。

曲の再生に必要な要素としてはAudioClipとAudioSourceなるものが必要で、いわばCDとプレイヤーみたいなそういう感じです。

まずこのAudioClipと曲の作者とかBPMとかScore判定で使うデータとか諸々をまとめたCD1枚に相当するクラスを作っておきます。

public class MusicData{
   public string Name; //曲名とか
   public float BPM; //BPMとか
   public ScoreData[] ScoreDataList; //score関連のデータとか
   
}
//曲一つそのもの
public class Music{
   public MusicData Data;
   public AudioClip clip;
   public Music(string MusicID){
       this.Data = getData_music(MusicID);
       this.Clip = getClip_music(MusicID);
   }
   MusicData getData_music(string MusicID){
       //どこかから曲データを取得してくる処理
       MusicData data = ---;
       return data;
   }
   AudioClip getClip_music(string MusicID){
       //どこからか曲そのものを取得してくる処理 例えばローカルからの読み出し
       AudioClip ac =  Resources.Load<AudioClip>("path/"+MusicID);
       return ac;
   }
}

取得元はさておき、Music hoge = new Music("hoge_id");みたいな感じで曲が作成できます。

そして、このCD複数枚とプレイヤーを内包するiPodみたいなクラスを作ります。

public class AudioStyle{
   public AudioStyle(string StyleName,int KeepClipCount){
       this.KeepClipCount=KeepClipCount;
       this.StyleName=StyleName;
   }
   //スタイル名を決める
   public string StyleName;
   //宿り木のオブジェクトは自動生成
   public GameObject AudioObj{
       get{
           return GetObj();
       }
   }
   GameObject GetObj(){
       var AO = GameObject.Find(StyleName);
       //なければ
       if (AO == null){
           //オブジェクトを生成
           AO = new GameObject(StyleName);
           //これやるとヒエラルキーに表示されなくて便利っていう記述
           AO.hideFlags = HideFlags.HideAndDontSave;
           return GameObject.Find(StyleName);
       }
       return AO;
   }
   //AudioSourceも自動生成
   public AudioSource AudioSource{
       get{
           SetAudioSource();
           return AudioObj.GetComponent<AudioSource>();
       }
   }
   void SetAudioSource(){
       var AS = AudioObj.GetComponent<AudioSource>();
       if (AS == null){
           AS = AudioObj.AddComponent<AudioSource>();
       }
   }

   //AudioClipを含む曲情報マップ
   public Dictionary<String,Audio> AudioList = new Dictionary<String,Audio>();
   //AudioSourceにセット中のAudioのkey
   string Sourcekey="";
   //曲を辞書に挿入
   void SetAudio(string MusicID){
       List<string> keys = AudioList.Keys.ToList();
       //何曲以下なら削除処理を終了していいか
       int MaxAudioCount = KeepClipCount;
       foreach(string key in keys){
           if(AudioList.Count<KeepClipCount){
               break;
           }
           if(Sourcekey!=key){
               AudioList.Remove(key);
           }
       }
       AudioList.Add(MusicID,new Audio(MusicID));
   }
   //マップ内に何曲保存するか
   //使用中の曲、新しいクリップ分、追加分と直前使用分は残るイメージ
   public int KeepClipCount;
   

   /*外から呼ぶ処理*/

   //CDを準備してすぐめに再生できるようにしておく補助処理(主にSE用)
   //但し上限を超える場合は保証はされないので注意
   public void SetAudio(List<string> AudioIDList){
       foreach(string ID in AudioIDList){
           if(!AudioList.ContainsKey(ID)){
               SetAudio(ID);
           }
       }
   }
   public void Play(string id){
       if(!AudioList.ContainsKey(id)){
           SetAudio(id);
       }
       Sourcekey=id;
       AudioSource.clip = AudioList[id].Clip;
       AudioSource.Play();
   }
   //一時停止
   public void Pose(){
       AudioSource.Pause();
   }
   //中断していた状態から再開する
   public void RePlay(){
       AudioSource.Play();
   }
}

このiPodの役割としては大きく3パターン、

画像1

BGMとプレイ用のゲームミュージック、そしてSEでしょうか。

正直BGMとプレイ用の曲は分けなくてもいいんですけどね。

基本的にBGMは長くてキャッシュを食うのでオーディオクリップは一つしか保持したくない気持ちですが、SEはいちいち読んでたら処理食うのでいったん使ったらある程度は保存しておきたい気持ちです。

このiPod三台をまとめて持ってる人が独自実装のAudioManagerになります。

なんかAudioManagerっていう標準の概念ありそうですが、これは独自実装で無関係なので注意してください。

public class AudioManager : MonoBehaviour{
   AudioStyle _bgm;
   AudioStyle BGM {
       get{
           _bgm ??= new AudioStyle("BGM",1);
           return _bgm;
       }
   }
   AudioStyle _gamemusic;
   AudioStyle GameMusic{
       get{
           _bgm ??= new AudioStyle("GameMusic",1);
           return _bgm;
       }
   }
   AudioStyle _se;
   AudioStyle SE {
       get{
           _bgm ??= new AudioStyle("SE",10);
           return _bgm;
       }
   }
}

というわけで、ここまで作っておくと僕の環境では

Manager.AudioManager.BGM.Play("曲名");

で好きな曲読み込ませて再生出来ちゃいます。

但し読み込みは最終的にはサーバからダウンロードなので、果たしてこの毎回読み込み方式で快適かどうかとか、メモリ大丈夫かとか、色々不安だらけですが、ひとまず良しとしておきましょう。

これで音楽を再生する仕組みが整いました。以前に作ってたのでちょっと改修した程度ですが。

バックで音楽が流れてキャラが動いている状態までできます。これはもうダンスですね。

動画にしようと思ったんですが、画面録画ツールが動いてくれないのでその辺は想像してください。

BPMとアニメーションスピードを合わせる

曲とBPMが取得できるようになったので、animationの再生速度をBPMに合わせる部分もついでにやっておきます。

アニメーションの速度はcontrollerのパラメーターを使って簡単に変えれるっぽいです。

画像2

画像3

新しくfloatのパラメーターをAnimatorControllerに追加し、各アニメーションのSpeed項目に乗算するように設定しておきます。

あとはスクリプトからcontrollerに対しSetFloat("Speed",値)で設定済みのアニメーションの速度は一斉に変わります。

入れる値は、基準として元になるアニメーションは100BPMで作成し曲のBPMとの比率で速度を決めるでいいと思います。

画像4

画像5


上がBPM100の時、下がBPM125の時です。結構違うのが分かりますね。

これで曲のBPMに合わせていい感じに踊ってくれるところまでできました。

次回、いよいよScoreに入りたいところですが、ムーブがないと何とも言えないので処理は最後に回すかも?

リザルトを表示してゲームに戻るとこまで出来たらいよいよお待ちかねのガチャ演出を考えるタイムでしょうかね。

進んだ感あるな~。

この記事が気に入ったらサポートをしてみませんか?