2 using System.Collections.Generic;
3 using System.Diagnostics;
6 using FDK; // for SystemStringExtensions
12 public Dictionary<ヒット判定種別, int> ヒットした回数 { get; } = new Dictionary<ヒット判定種別, int>();
20 public フェーズ 現在のフェーズ { get; protected set; } = フェーズ.初期状態;
24 this.子リスト.Add( this.コンボ = new コンボ() );
25 this.子リスト.Add( this.レーンフレーム = new レーンフレーム() );
26 this.子リスト.Add( this.スクロール譜面 = new スクロール譜面() );
27 this.子リスト.Add( this.ヒット判定文字列 = new ヒット判定文字列() );
28 this.子リスト.Add( this.回転羽 = new 回転羽( 最大同時発火数: 32 ) );
29 this.子リスト.Add( this.ドラムサウンド = new ドラムサウンド() );
30 this.子リスト.Add( this.ドラムセット = new ドラムセット() );
31 this.子リスト.Add( this.判定バー = new 画像( @"$(Static)\images\判定バー.png" ) );
32 this.子リスト.Add( this.ステージ台 = new 画像( @"$(Static)\images\ステージ台.png" ) );
33 this.子リスト.Add( this.FPS画像 = new 文字列画像() );
35 // 子Activity の外部依存 Action の実体を定義する。
36 this.スクロール譜面.ヒット判定文字列開始 = ( chipType, hitType ) => { this.ヒット判定文字列.表示開始( chipType, hitType ); };
37 this.スクロール譜面.コンボリセット = () => { this.コンボ.COMBO値 = 0; };
38 this.スクロール譜面.コンボ加算 = () => { this.コンボ.COMBO値++; };
39 this.スクロール譜面.ヒット判定数加算 = ( hitType ) => { this.ヒットした回数[ hitType ]++; };
40 this.スクロール譜面.背景動画再生開始 = () => { this.背景動画開始済み = true; };
41 this.スクロール譜面.チップヒット = ( chip ) => {
42 this.回転羽.発火する( chip.チップ種別 );
43 this.ドラムサウンド.発声する( chip.チップ種別, chip.音量 * 0.25f );
45 this.スクロール譜面.ステージクリア = () => { this.現在のフェーズ = フェーズ.クリアor失敗; };
46 this.スクロール譜面.リアルタイム演奏時刻sec = () => { return this.現在の演奏時刻secを返す(); };
47 this.スクロール譜面.譜面スクロール速度の倍率 = () => { return this.現在進行描画中の譜面スクロール速度の倍率; };
48 this.スクロール譜面.FPSをカウント = () => { this.FPS.FPSをカウントする(); };
50 protected override void On活性化( デバイスリソース dr )
52 FDK.Log.Info( "演奏ステージを開始します。" );
54 #region " すべてのヒット判定種別について、ヒット回数をクリア。"
57 foreach( var 判定 in typeof( ヒット判定種別 ).GetEnumValues() )
58 this.ヒットした回数[ (ヒット判定種別) 判定 ] = 0;
61 #region " 選択ノードの持つ動画ファイルパスが有効なら、背景動画とBGMを初期化する。"
63 var 動画ファイルパス = ( (SST.曲.MusicNode) StrokeStyleT.曲ツリー管理.現在選択されているノード )?.動画ファイルパス;
64 if( 動画ファイルパス.Nullでも空でもない() )
66 this.子リスト.Add( this.背景動画 = new 動画( 動画ファイルパス ) ); // 子リストに追加
67 this.BGM = new FDK.メディア.サウンド.WASAPI排他.Sound();
68 this.BGM.ファイルから作成する( 動画ファイルパス );
69 StrokeStyleT.Wasapiデバイス.サウンドをミキサーに追加する( this.BGM ); // 作成に失敗した Sound を追加しても鳴らないだけなので、ノーチェックで大丈夫。
75 this.現在進行描画中の譜面スクロール速度の倍率 = StrokeStyleT.ユーザ管理.現在選択されているユーザ.譜面スクロール速度の倍率;
76 this.BGM再生開始済み = false;
77 this.背景動画開始済み = false;
78 this.レーンフレーム.左端位置dpx = 400f;
79 this.レーンフレーム.高さdpx = dr.設計画面サイズdpx.Height;
80 this.現在のフェーズ = フェーズ.演奏中;
81 this.活性化した直後である = true;
83 protected override void On非活性化( デバイスリソース dr )
85 FDK.Log.Info( "演奏ステージを終了します。" );
87 //this.BGMを解放する(); → ここではまだ解放しない。結果ステージが終わるときに、外部から解放する。
89 if( null != this.背景動画 )
90 this.子リスト.Remove( this.背景動画 ); // 子リストから削除
92 public override void 進行描画する( デバイスリソース dr )
100 this.活性化した直後である = false;
101 this.FPS = new FDK.カウンタ.FPS();
102 this.演奏開始時刻sec = this.サウンドタイマ.現在のデバイス位置secを取得する( StrokeStyleT.Wasapiデバイス.AudioClock );
107 double 演奏時刻sec = this.現在の演奏時刻secを返す();
109 this.FPS.VPSをカウントする();
110 this.FPS画像.表示文字列 = $"VPS: {this.FPS.現在のVPS.ToString()} / FPS: {this.FPS.現在のFPS.ToString()}";
112 #region " 譜面スクロール速度が変化している場合の追い付き進行。"
114 if( this.現在進行描画中の譜面スクロール速度の倍率 < StrokeStyleT.ユーザ管理.現在選択されているユーザ.譜面スクロール速度の倍率 )
116 // todo: 時間間隔に関係なく進行描画ごとに倍率を変えてしまっているので、あとからVPSに依存しないよう修正すること。
117 this.現在進行描画中の譜面スクロール速度の倍率 =
118 Math.Min( this.現在進行描画中の譜面スクロール速度の倍率 + 0.05, StrokeStyleT.ユーザ管理.現在選択されているユーザ.譜面スクロール速度の倍率 );
120 else if( this.現在進行描画中の譜面スクロール速度の倍率 > StrokeStyleT.ユーザ管理.現在選択されているユーザ.譜面スクロール速度の倍率 )
123 this.現在進行描画中の譜面スクロール速度の倍率 =
124 Math.Max( this.現在進行描画中の譜面スクロール速度の倍率 - 0.05, StrokeStyleT.ユーザ管理.現在選択されているユーザ.譜面スクロール速度の倍率 );
131 // 背景動画チップがヒット済みなら、背景動画の進行描画を行う。
132 // this.背景動画?.進行描画する( dr, new SharpDX.RectangleF( 0f, 0f, dr.設計画面サイズdpx.Width, dr.設計画面サイズdpx.Height ) );
134 // 動画が重たいかもしれないので、演奏時刻をここで再度取得する。
135 演奏時刻sec = this.現在の演奏時刻secを返す();
137 // 背景動画が再生されているのにBGMがまだ再生されていないなら、再生を開始する。
138 if( false == this.BGM再生開始済み )
141 this.BGM再生開始済み = true;
145 this.ステージ台.描画する( dr, 0.0f, 0.0f );
146 this.レーンフレーム.進行描画する( dr );
147 this.コンボ.進行描画する( dr );
148 this.ヒット判定文字列.進行描画する( dr );
149 this.スクロール譜面.小節線拍線を進行描画する( dr, 演奏時刻sec );
150 this.判定バー.描画する( dr, 597f, 座標.判定バーの中央Y座標dpx - 43f );
151 this.ドラムセット.進行描画する( dr );
152 this.スクロール譜面.チップを進行描画する( dr, 演奏時刻sec );
153 this.回転羽.進行描画する( dr );
154 this.FPS画像.進行描画する( dr, 0f, 0f );
158 StrokeStyleT.すべての入力デバイスをポーリングする();
161 if( StrokeStyleT.キーボード入力.キーが押された( SharpDX.DirectInput.Key.Escape ) &&
162 ( false == StrokeStyleT.ビュアーモードである ) )
165 this.現在のフェーズ = フェーズ.キャンセル;
167 // 上矢印押下 → 譜面スクロール速度の倍率を +0.5
168 else if( StrokeStyleT.キーボード入力.キーが押された( SharpDX.DirectInput.Key.Up ) )
170 StrokeStyleT.ユーザ管理.現在選択されているユーザ.譜面スクロール速度の倍率 =
171 Math.Min( StrokeStyleT.ユーザ管理.現在選択されているユーザ.譜面スクロール速度の倍率 + 0.5, 8.0 ); // 最大 8.0
173 // 下矢印押下 → 譜面スクロール速度の倍率を -0.5
174 else if( StrokeStyleT.キーボード入力.キーが押された( SharpDX.DirectInput.Key.Down ) )
176 StrokeStyleT.ユーザ管理.現在選択されているユーザ.譜面スクロール速度の倍率 =
177 Math.Max( StrokeStyleT.ユーザ管理.現在選択されているユーザ.譜面スクロール速度の倍率 - 0.5, 0.5 ); // 最小 0.5
180 public void BGMを解放する()
182 if( null != this.BGM ) // 背景動画がなければ BGM も null である
184 StrokeStyleT.Wasapiデバイス.サウンドをミキサーから削除する( this.BGM );
185 FDK.Utilities.解放する( ref this.BGM );
189 protected bool 活性化した直後である = false;
190 protected bool 背景動画開始済み = false;
191 protected bool BGM再生開始済み = false;
192 protected double 現在進行描画中の譜面スクロール速度の倍率 = 0.0;
193 protected double 演奏開始時刻sec = 0.0;
194 protected readonly FDK.メディア.サウンド.WASAPI排他.SoundTimer サウンドタイマ = new FDK.メディア.サウンド.WASAPI排他.SoundTimer();
195 protected readonly SST.ステージ.演奏.コンボ コンボ;
196 protected readonly SST.ステージ.演奏.レーンフレーム レーンフレーム;
197 protected readonly SST.ステージ.演奏.スクロール譜面 スクロール譜面;
198 protected readonly SST.ステージ.演奏.ヒット判定文字列 ヒット判定文字列;
199 protected readonly SST.ステージ.演奏.回転羽 回転羽 = new 回転羽( 32 );
200 protected readonly SST.ステージ.演奏.ドラムサウンド ドラムサウンド;
201 protected readonly SST.ステージ.ドラムセット ドラムセット;
202 protected readonly FDK.メディア.画像 判定バー;
203 protected readonly FDK.メディア.画像 ステージ台;
204 protected readonly FDK.メディア.文字列画像 FPS画像;
206 /// 解放は、演奏ステージクラスの非活性化後に、外部から行われる。
207 /// <see cref="SST.ステージ.演奏.演奏ステージ.BGMを解放する"/>
209 protected FDK.メディア.サウンド.WASAPI排他.Sound BGM = null;
210 protected FDK.カウンタ.FPS FPS = null;
212 /// 動的子Activity。背景動画を再生しない場合は null のまま。
214 protected FDK.メディア.動画 背景動画 = null;
216 private double 現在の演奏時刻secを返す()
218 return this.サウンドタイマ.現在のデバイス位置secを取得する( StrokeStyleT.Wasapiデバイス.AudioClock ) - this.演奏開始時刻sec;