OSDN Git Service

レーンとレーンフレームの座標の持ち方を変更。
[strokestylet/CsWin10Desktop3.git] / StrokeStyleT / ステージ / 演奏 / 演奏ステージ.cs
1 using System;
2 using System.Collections.Generic;
3 using System.Diagnostics;
4 using System.Linq;
5 using System.Threading;
6 using System.Threading.Tasks;
7 using SharpDX;
8 using SharpDX.DirectInput;
9 using FDK;
10 using FDK.メディア;
11 using FDK.メディア.サウンド.WASAPI;
12 using FDK.カウンタ;
13 using FDK.同期;
14 using SSTFormat.v2;
15 using SST.入力;
16 using SST.設定;
17
18 using Utilities = FDK.Utilities;
19
20 namespace SST.ステージ.演奏
21 {
22         class 演奏ステージ : ステージ
23         {
24                 public enum フェーズ
25                 {
26                         演奏中,
27                         Failed,
28                         クリア,
29                         キャンセル,
30                         ビュアーメッセージ待機,
31                 }
32
33                 public フェーズ 現在のフェーズ
34                 {
35                         get;
36                         set;
37                 }
38
39
40                 public 演奏ステージ()
41                 {
42                         this.子リスト.Add( this._ステージ台 = new 画像( @"$(System)\images\ステージ台.png" ) );
43                         this.子リスト.Add( this._レーンフレーム = new レーンフレーム() );
44                         this.子リスト.Add( this._ドラムセット = new ドラムセット() );
45                         this.子リスト.Add( this._ヒット判定バー = new 画像( @"$(System)\images\判定バー.png" ) );
46                         this.子リスト.Add( this._チップ画像 = new 画像( @"$(System)\images\Chips.png" ) );
47                         this.子リスト.Add( this._回転羽 = new 回転羽( 最大同時発火数: 32 ) );
48                         this.子リスト.Add( this._コンソールフォント = new コンソールフォント() );
49                         this.子リスト.Add( this._ドラムサウンド = new ドラムサウンド() );
50                         this.子リスト.Add( this._コンボ = new コンボ() );
51                 }
52
53                 protected override void On活性化( デバイスリソース dr )
54                 {
55                         using( Log.Block( Utilities.現在のメソッド名 ) )
56                         {
57                                 lock( this._スレッド間同期 )
58                                 {
59                                         this._活性化した直後である = true;
60                                         this._演奏開始時刻sec = 0.0;
61                                         this._背景動画開始済み = false;
62                                         this._現在進行描画中の譜面スクロール速度の倍率 = App.ユーザ管理.選択されているユーザ.オプション設定.譜面スクロール速度の倍率;
63                                         this._描画開始チップ番号 = -1;
64                                         this._背景動画 = null;
65                                         this._BGM = null;
66                                         //this._デコード済みWaveSource = null;    --> キャッシュなので消さない。
67                                         this._背景動画開始済み = false;
68                                         this._BGM再生開始済み = false;
69                                         this._高頻度進行タスクへのイベント = new TriStateEvent( TriStateEvent.状態種別.OFF );
70                                         this._チップ画像の矩形リスト = new 矩形リスト( @"$(System)\images\Chips Rectangle List.xml" );      // デバイスリソースは持たないので、子Activityではない。
71
72                                         #region " 背景動画とBGMを生成する。"
73                                         //----------------
74                                         if( ( null != App.演奏スコア ) && ( App.演奏スコア.背景動画ファイル名.Nullでも空でもない() ) )
75                                         {
76                                                 Log.Info( "背景動画とBGMを読み込みます。" );
77
78                                                 // 動画を子リストに追加。
79                                                 this.子リスト.Add( this._背景動画 = new 動画( App.演奏スコア.背景動画ファイル名, App.システム設定.動画デコーダのキューサイズ ) );
80
81                                                 // 動画から音声パートを抽出して BGM を作成。
82                                                 if( ( null != this._デコード済みWaveSource ) && this._デコード済みWaveSource.Path.Equals( App.演奏スコア.背景動画ファイル名 ) )
83                                                 {
84                                                         // (A) 前回生成したBGMとパスが同じなので、前回のデコード済み WaveSource をキャッシュとして再利用する。
85                                                         Log.Info( "前回生成したサウンドデータを再利用します。" );
86                                                 }
87                                                 else
88                                                 {
89                                                         // (B) 初めての生成か、または前回生成したBGMとパスが違うので、新しくデコード済み WaveSource を生成する。
90                                                         this._デコード済みWaveSource?.Dispose();
91                                                         this._デコード済みWaveSource = new DecodedWaveSource( App.演奏スコア.背景動画ファイル名, App.サウンドデバイス.WaveFormat );
92                                                 }
93
94                                                 this._BGM?.Dispose();
95                                                 this._BGM = App.サウンドデバイス.サウンドを生成する( this._デコード済みWaveSource );
96                                         }
97                                         else
98                                         {
99                                                 Log.Info( "背景動画とBGMはありません。" );
100                                         }
101                                         //----------------
102                                         #endregion
103
104                                         #region " 最初のフェーズを設定する。"
105                                         //----------------
106                                         if( App.ビュアーモードではない )
107                                         {
108                                                 this.現在のフェーズ = フェーズ.演奏中;
109                                         }
110                                         else
111                                         {
112                                                 this.現在のフェーズ = フェーズ.ビュアーメッセージ待機;
113                                         }
114                                         //----------------
115                                         #endregion
116                                 }
117                         }
118                 }
119
120                 protected override void On非活性化( デバイスリソース dr )
121                 {
122                         using( Log.Block( Utilities.現在のメソッド名 ) )
123                         {
124                                 lock( this._スレッド間同期 )
125                                 {
126                                         #region " 高頻度進行タスクがまだ実行されていれば、終了する。 "
127                                         //----------------
128                                         if( this._高頻度進行タスクへのイベント.現在の状態 == TriStateEvent.状態種別.ON )
129                                         {
130                                                 // タスクへ終了を指示。
131                                                 this._高頻度進行タスクへのイベント.現在の状態 = TriStateEvent.状態種別.OFF;
132                                                 //this._高頻度進行タスクへのイベント.無効になるまでブロックする();   --> タスクが終了するまで待つ必要はない。
133                                         }
134                                         //----------------
135                                         #endregion
136
137                                         // 背景動画を生成した場合は子リストから削除。
138                                         if( null != this._背景動画 )
139                                                 this.子リスト.Remove( this._背景動画 );
140
141                                         this._活性化した直後である = false;
142                                 }
143                         }
144                 }
145
146                 public override void 進行描画する( デバイスリソース dr )
147                 {
148                         Debug.Assert( this.活性化している );
149                         Debug.Assert( null != dr );
150
151                         lock( this._スレッド間同期 )
152                         {
153                                 double 演奏時刻sec = this._演奏開始からの経過時間secを返す();
154
155                                 switch( this.現在のフェーズ )
156                                 {
157                                         case フェーズ.演奏中:
158                                                 #region " 初めての進行描画。"
159                                                 //----------------
160                                                 if( this._活性化した直後である )
161                                                 {
162                                                         this._活性化した直後である = false;
163                                                         this._描画開始チップ番号 = 0; // -1 → 0; 演奏開始。
164                                                         this._チップアニメ.開始する( 最初の値: 0, 最後の値: 48, 値をひとつ増加させるのにかける時間ms: 10 );
165
166                                                         #region " 演奏開始時刻を初期化し、動画とBGMの再生を開始する。"
167                                                         //----------------
168                                                         double 演奏開始位置の先頭からの時間sec = 0.0;
169
170                                                         // 演奏開始時刻の設定(1)
171                                                         this._演奏開始時刻sec = App.サウンドデバイス.GetDevicePosition() - 演奏開始位置の先頭からの時間sec;
172                                                         Log.Info( $"演奏開始時刻(背景動画再生チェック前): {this._演奏開始時刻sec} sec" );
173
174                                                         // 演奏開始時刻の設定(2) ビュアーメッセージがある場合、開始時刻を修正する。
175                                                         var msg = App.最後に取得したビュアーメッセージ;
176                                                         if( null != msg )
177                                                         {
178                                                                 // 演奏開始位置を設定。
179                                                                 演奏開始位置の先頭からの時間sec = this._演奏開始小節番号を設定しその時刻secを返す( msg.演奏を開始する小節番号 );
180                                                                 Log.Info( $"演奏開始の先頭からの時間: {演奏開始位置の先頭からの時間sec} sec" );
181
182                                                                 // その他のオプション。
183                                                                 App.システム設定.Autoチップのドラム音を再生する = msg.ドラムチップのヒット時に発声する;
184                                                         }
185
186                                                         // BGMと動画を(必要あれば)再生開始。
187                                                         this._再生中の時刻なら動画とBGMを再生開始する( 演奏開始位置の先頭からの時間sec );
188
189                                                         // 演奏開始時刻の設定(3) 動画とBGMが再生された場合、ここに到達するまでに多少の遅延が生じているので、ここで演奏開始時刻を再取得しておく。
190                                                         this._演奏開始時刻sec = App.サウンドデバイス.GetDevicePosition() - 演奏開始位置の先頭からの時間sec;
191                                                         Log.Info( $"演奏開始時刻(背景動画再生チェック後): {this._演奏開始時刻sec} sec" );
192                                                         //----------------
193                                                         #endregion
194
195                                                         this._FPS = new FPS();
196
197                                                         // 高頻度進行タスクを生成、実行開始。
198                                                         Task.Factory.StartNew( this._高頻度進行処理タスクエントリ );
199                                                 }
200                                                 //----------------
201                                                 #endregion
202                                                 #region " 背景動画とBGMの進行描画。"
203                                                 //----------------
204                                                 if( this._背景動画開始済み )
205                                                 {
206                                                         // 背景動画チップがヒット済みなら、背景動画の進行描画を行う。
207                                                         switch( App.ユーザ管理.選択されているユーザ.オプション設定.動画表示パターン種別 )
208                                                         {
209                                                                 case 動画表示パターン種別.中央表示:
210                                                                         #region " *** "
211                                                                         //----------------
212                                                                         {
213                                                                                 float w = dr.設計画面サイズdpx.Width;
214                                                                                 float h = dr.設計画面サイズdpx.Height;
215
216                                                                                 this._背景動画?.進行描画する( dr, new RectangleF( 0f, 0f, w, h ), 0.2f );  // 全体
217
218                                                                                 float 拡大縮小率 = 0.75f;
219                                                                                 float 上移動dpx = 100.0f;
220
221                                                                                 this._背景動画?.進行描画する( dr, new RectangleF(
222                                                                                         w * ( 1f - 拡大縮小率 ) / 2f,
223                                                                                         h * ( 1f - 拡大縮小率 ) / 2f - 上移動dpx,
224                                                                                         w * 拡大縮小率,
225                                                                                         h * 拡大縮小率 ) );
226                                                                         }
227                                                                         //----------------
228                                                                         #endregion
229                                                                         break;
230
231                                                                 case 動画表示パターン種別.最大表示:
232                                                                         #region " *** "
233                                                                         //----------------
234                                                                         this._背景動画?.進行描画する( dr, new RectangleF( 0f, 0f, dr.設計画面サイズdpx.Width, dr.設計画面サイズdpx.Height ), 1.0f );
235                                                                         //----------------
236                                                                         #endregion
237                                                                         break;
238                                                         }
239
240                                                         // 動画が重たいかもしれないので、演奏時刻をここで更新しておく。
241                                                         演奏時刻sec = this._演奏開始からの経過時間secを返す();
242
243                                                         // 背景動画が再生されているのにBGMがまだ再生されていないなら、すぐに再生を開始する。
244                                                         if( false == this._BGM再生開始済み )
245                                                         {
246                                                                 this._BGM?.Play();
247                                                                 this._BGM再生開始済み = true;
248                                                         }
249                                                 }
250                                                 //----------------
251                                                 #endregion
252                                                 this._FPS.VPSをカウントする();
253                                                 this._ステージ台.描画する( dr, 0f, 0f );
254                                                 this._レーンフレーム.進行描画する( dr, レーンフレームの左端位置dpx );
255                                                 this._コンボ.進行描画する( dr );
256                                                 this._小節線拍線を描画する( dr, 演奏時刻sec );
257                                                 this._ヒット判定バー.描画する( dr, 597f, ヒット判定バーの中央Y座標dpx - 43f );
258                                                 this._ドラムセット.進行描画する( dr );
259                                                 this._チップを描画する( dr, 演奏時刻sec );
260                                                 this._回転羽.進行描画する( dr );
261                                                 break;
262
263                                         case フェーズ.Failed:
264                                                 break;
265
266                                         case フェーズ.クリア:
267                                                 break;
268
269                                         case フェーズ.キャンセル:
270                                                 break;
271
272                                         case フェーズ.ビュアーメッセージ待機:
273                                                 break;
274                                 }
275                         }
276                 }
277
278                 public void 演奏を停止する()
279                 {
280                         using( Log.Block( Utilities.現在のメソッド名 ) )
281                         {
282                                 lock( this._スレッド間同期 )
283                                 {
284                                         this._描画開始チップ番号 = -1;   // 演奏停止
285
286                                         this.BGMを停止する();
287                                         this._背景動画開始済み = false;
288
289                                         this._コンボ.COMBO値 = 0;
290                                 }
291                         }
292                 }
293
294                 /// <remarks>
295                 ///             演奏クリア時には、次の結果ステージに入ってもBGMが鳴り続ける。
296                 ///             そのため、後からBGMだけを別個に停止するためのメソッドが必要になる。
297                 /// </remarks>
298                 public void BGMを停止する()
299                 {
300                         using( Log.Block( Utilities.現在のメソッド名 ) )
301                         {
302                                 lock( this._スレッド間同期 )
303                                 {
304                                         this._BGM?.Stop();
305                                         this._BGM?.Dispose();
306                                         this._BGM = null;
307
308                                         //this._デコード済みWaveSource?.Dispose();        --> ここではまだ解放しない。
309                                         //this._デコード済みWaveSource = null;
310                                 }
311                         }
312                 }
313
314                 public void BGMのキャッシュを解放する()
315                 {
316                         using( Log.Block( Utilities.現在のメソッド名 ) )
317                         {
318                                 this.BGMを停止する();
319                                 Utilities.解放する( ref this._デコード済みWaveSource );
320                         }
321                 }
322
323
324                 private const float ヒット判定バーの中央Y座標dpx = 869f + 43f;
325
326                 private const float レーンフレームの左端位置dpx = 619f;
327
328
329                 private double _演奏開始時刻sec = 0.0;
330
331                 private double _現在進行描画中の譜面スクロール速度の倍率 = 1.0;
332
333                 /// <summary>
334                 ///             演奏スコア.チップリスト[] のうち、描画を始めるチップのインデックス番号。
335                 ///             未演奏時・演奏終了時は -1 。
336                 /// </summary>
337                 /// <remarks>
338                 ///             演奏開始直後は 0 で始まり、対象番号のチップが描画範囲を流れ去るたびに +1 される。
339                 ///             このメンバの更新は、高頻度進行タスクではなく、進行描画メソッドで行う。(低精度で構わないので)
340                 /// </remarks>
341                 private int _描画開始チップ番号 = -1;
342
343                 private 動画 _背景動画 = null;
344
345                 /// <remarks>
346                 ///             停止と解放は、演奏ステージクラスの非活性化後に、外部から行われる。
347                 ///             <see cref="SST.ステージ.演奏.演奏ステージ.BGMを停止する"/>
348                 ///             <see cref="SST.ステージ.演奏.演奏ステージ.BGMのキャッシュを解放する"/>
349                 /// </remarks>
350                 private Sound _BGM = null;
351
352                 /// <summary>
353                 ///             BGM の生成もとになるデコード済みサウンドデータ。
354                 ///     </summary>
355                 ///     <remarks>
356                 ///             活性化と非活性化に関係なく、常に最後にデコードしたデータを持つ。(キャッシュ)
357                 ///             演奏ステージインスタンスを破棄する際に、このインスタンスもDisposeすること。
358                 /// </remarks>
359                 private DecodedWaveSource _デコード済みWaveSource = null;
360
361                 private bool _背景動画開始済み = false;
362
363                 private bool _BGM再生開始済み = false;
364
365                 private 画像 _ステージ台 = null;
366
367                 private レーンフレーム _レーンフレーム = null;
368
369                 private ドラムセット _ドラムセット = null;
370
371                 private 画像 _ヒット判定バー = null;
372
373                 private 画像 _チップ画像 = null;
374
375                 private 矩形リスト _チップ画像の矩形リスト = null;
376
377                 private 単純増加後反復カウンタ _チップアニメ = new 単純増加後反復カウンタ();
378
379                 private 回転羽 _回転羽 = null;
380
381                 private コンソールフォント _コンソールフォント = null;
382
383                 private ドラムサウンド _ドラムサウンド = null;
384
385                 private コンボ _コンボ = null;
386
387                 private FPS _FPS = null;
388
389                 private bool _活性化した直後である;
390
391                 /// <summary>
392                 ///             OFF:タスク未生成、ON:タスク稼働中、無効:タスク終了済み
393                 /// </summary>
394                 private TriStateEvent _高頻度進行タスクへのイベント = null;
395
396                 private readonly object _スレッド間同期 = new object();
397
398
399                 // 譜面描画。
400
401                 /// <summary>
402                 ///             指定した小節の先頭を演奏開始位置として設定し、その位置(時刻)を返す。
403                 /// </summary>
404                 /// <returns>演奏開始位置sec。(0~)</returns>
405                 private double _演奏開始小節番号を設定しその時刻secを返す( int 演奏開始小節番号 )
406                 {
407                         lock( this._スレッド間同期 )
408                         {
409                                 var score = App.演奏スコア;
410
411                                 if( null == score )
412                                         return 0.0;
413
414                                 double 演奏開始時刻sec = 0.0;
415
416                                 for( int i = 0; i < score.チップリスト.Count; i++ )
417                                 {
418                                         if( score.チップリスト[ i ].小節番号 < 演奏開始小節番号 )
419                                         {
420                                                 // 開始チップ以前のチップはヒット済みとする。
421                                                 score.チップリスト[ i ].ヒット済みの状態にする();
422                                         }
423                                         else
424                                         {
425                                                 // 開始チップを設定。
426                                                 this._描画開始チップ番号 = i;
427
428                                                 // 演奏開始時刻は、開始チップの発声時刻から少し早めの値に。
429                                                 演奏開始時刻sec = score.チップリスト[ i ].発声時刻sec;
430                                                 演奏開始時刻sec -= 0.5;
431
432                                                 // 開始チップ以降のすべてのチップをヒット前の状態にする。
433                                                 for( int j = i; j >= 0; j-- )
434                                                 {
435                                                         if( score.チップリスト[ j ].ヒット済みである )
436                                                                 score.チップリスト[ j ].ヒット前の状態にする();
437                                                 }
438
439                                                 break;
440                                         }
441                                 }
442
443                                 return 演奏開始時刻sec;
444                         }
445                 }
446
447                 private void _再生中の時刻なら動画とBGMを再生開始する( double 時刻sec )
448                 {
449                         using( Log.Block( Utilities.現在のメソッド名 ) )
450                         {
451                                 lock( this._スレッド間同期 )
452                                 {
453                                         Log.Info( $"指定された時刻: {時刻sec} sec" );
454
455                                         var 背景動画チップ = App.演奏スコア.チップリスト.FirstOrDefault( (chip) => ( chip.チップ種別 == チップ種別.背景動画 ) );
456
457                                         if( null == 背景動画チップ )
458                                         {
459                                                 Log.Info( "背景動画チップは存在しません。" );
460                                                 return;
461                                         }
462
463                                         double 背景動画の長さsec = this._BGM?.長さsec ?? 0.0;        // 動画のサイズは、映像ではなく音声を優先する。
464
465                                         Log.Info( $"背景動画の発生時刻: {背景動画チップ.発声時刻sec} sec" );
466                                         Log.Info( $"背景動画の長さ: {背景動画の長さsec} sec" );
467
468                                         // 指定された時刻secは再生期間内?
469                                         if( ( 背景動画チップ.発声時刻sec <= 時刻sec ) && ( 時刻sec < ( 背景動画チップ.発声時刻sec + 背景動画の長さsec ) ) )
470                                         {
471                                                 // 背景動画の再生を開始する。
472                                                 double 再生開始時刻sec = ( 時刻sec - 背景動画チップ.発声時刻sec );
473                                                 this._背景動画?.再生を開始する( 再生開始時刻sec );
474                                                 this._背景動画開始済み = true;
475                                                 this._BGM?.Play( 再生開始時刻sec );
476                                                 this._BGM再生開始済み = true;
477                                                 Log.Info( $"背景動画の再生を開始しました。(再生開始時刻: {再生開始時刻sec} sec)" );
478                                         }
479                                         else
480                                         {
481                                                 Log.Info( $"指定された時刻は背景動画の再生期間内ではないので、何もしません。" );
482                                         }
483                                 }
484                         }
485                 }
486
487                 private double _演奏開始からの経過時間secを返す()
488                 {
489                         lock( this._スレッド間同期 )
490                         {
491                                 return App.サウンドデバイス.GetDevicePosition() - this._演奏開始時刻sec;
492                         }
493                 }
494
495                 // 小節線・拍線 と チップ は描画階層(奥行き)が異なるので、別々のメソッドに分ける。
496
497                 private void _小節線拍線を描画する( デバイスリソース dr, double 現在の演奏時刻sec )
498                 {
499                         if( null == this._チップ画像 )
500                                 return;
501
502                         this._チップ画像.加算合成 = false;
503
504                         this._描画範囲のチップに処理を適用する( ( chip, index, ヒット判定バーとの時間sec, ヒット判定バーとの距離dpx ) => {
505
506                                 if( chip.チップ種別 == チップ種別.小節線 )
507                                 {
508                                         #region " 小節線 "
509                                         //----------------
510                                         float 左位置dpx = レーンフレームの左端位置dpx + レーンフレーム.レーンto横中央相対位置dpx[ 表示レーン種別.LeftCrash ] - 1f;               // -1f はレーン線の幅の半分。
511                                         float 上位置dpx = (float)( ヒット判定バーの中央Y座標dpx + ヒット判定バーとの距離dpx - 1f );   // -1f は小節線の厚みの半分。
512                                         if( this._チップ画像の矩形リスト[ nameof( チップ種別.小節線 ) ] is RectangleF 画像範囲 )
513                                         {
514                                                 this._チップ画像.描画する( dr, 左位置dpx, 上位置dpx, 転送元矩形dpx: 画像範囲 );
515                                                 this._チップ画像.描画する( dr, 左位置dpx + 画像範囲.Width, 上位置dpx, 転送元矩形dpx: 画像範囲 );
516                                         }
517                                         //----------------
518                                         #endregion
519                                 }
520                                 else if( chip.チップ種別 == チップ種別.拍線 )
521                                 {
522                                         #region " 拍線 "
523                                         //----------------
524                                         float 左位置dpx = レーンフレームの左端位置dpx + レーンフレーム.レーンto横中央相対位置dpx [ 表示レーン種別.LeftCrash ] - 1f;       // -1f はレーン線の幅の半分。
525                                         float 上位置dpx = (float)( ヒット判定バーの中央Y座標dpx + ヒット判定バーとの距離dpx - 1f );   // -1f は拍線の厚みの半分。
526                                         if( this._チップ画像の矩形リスト[ nameof( チップ種別.拍線 ) ] is RectangleF 画像範囲 )
527                                         {
528                                                 this._チップ画像.描画する( dr, 左位置dpx: 左位置dpx, 上位置dpx: 上位置dpx, 転送元矩形dpx: 画像範囲 );
529                                                 this._チップ画像.描画する( dr, 左位置dpx: 左位置dpx + 画像範囲.Width, 上位置dpx: 上位置dpx, 転送元矩形dpx: 画像範囲 );
530                                         }
531                                         //----------------
532                                         #endregion
533                                 }
534
535                         } );
536                 }
537
538                 private void _チップを描画する( デバイスリソース dr, double 現在の演奏時刻sec )
539                 {
540                         if( null == this._チップ画像 )
541                                 return;
542
543                         this._チップ画像.加算合成 = false;
544
545                         this._描画範囲のチップに処理を適用する( ( chip, index, ヒット判定バーとの時間sec, ヒット判定バーとの距離dpx ) => {
546
547                                 float 縦中央位置dpx = (float)( ヒット判定バーの中央Y座標dpx + ヒット判定バーとの距離dpx );
548
549                                 #region " チップが描画開始チップであり、かつ、そのY座標が画面下端を超えたなら、描画開始チップ番号を更新する。"
550                                 //----------------
551                                 if( ( index == this._描画開始チップ番号 ) &&
552                                         ( dr.設計画面サイズdpx.Height + 40.0 < 縦中央位置dpx ) )   // +40 はチップが隠れるであろう適当なマージン。
553                                 {
554                                         this._描画開始チップ番号++;
555
556                                         if( App.演奏スコア.チップリスト.Count <= this._描画開始チップ番号 )
557                                         {
558                                                 this.現在のフェーズ = フェーズ.クリア;
559                                                 this._描画開始チップ番号 = -1;    // 演奏完了。
560                                                 return;
561                                         }
562                                 }
563                                 //----------------
564                                 #endregion
565
566                                 if( chip.不可視 )
567                                         return;
568
569                                 #region " チップを個別に描画する。"
570                                 //----------------
571                                 float 音量0to1 = chip.音量 / (float) チップ.最大音量;
572
573                                 switch( chip.チップ種別 )
574                                 {
575                                         case チップ種別.LeftCrash:
576                                                 _単画チップを1つ描画する( 表示レーン種別.LeftCrash, this._チップ画像の矩形リスト[ nameof( チップ種別.LeftCrash ) ], 縦中央位置dpx, 音量0to1 );
577                                                 break;
578
579                                         case チップ種別.HiHat_Close:
580                                                 _アニメチップを1つ描画する( 表示レーン種別.HiHat, this._チップ画像の矩形リスト[ nameof( チップ種別.HiHat_Close ) ], 縦中央位置dpx, 音量0to1 );
581                                                 break;
582
583                                         case チップ種別.HiHat_HalfOpen:
584                                                 _アニメチップを1つ描画する( 表示レーン種別.HiHat, this._チップ画像の矩形リスト[ nameof( チップ種別.HiHat_Close ) ], 縦中央位置dpx, 音量0to1 );
585                                                 _単画チップを1つ描画する( 表示レーン種別.Foot, this._チップ画像の矩形リスト[ nameof( チップ種別.HiHat_HalfOpen ) ], 縦中央位置dpx, 1.0f );
586                                                 break;
587
588                                         case チップ種別.HiHat_Open:
589                                                 _アニメチップを1つ描画する( 表示レーン種別.HiHat, this._チップ画像の矩形リスト[ nameof( チップ種別.HiHat_Close ) ], 縦中央位置dpx, 音量0to1 );
590                                                 _単画チップを1つ描画する( 表示レーン種別.Foot, this._チップ画像の矩形リスト[ nameof( チップ種別.HiHat_Open ) ], 縦中央位置dpx, 1.0f );
591                                                 break;
592
593                                         case チップ種別.HiHat_Foot:
594                                                 _単画チップを1つ描画する( 表示レーン種別.Foot, this._チップ画像の矩形リスト[ nameof( チップ種別.HiHat_Foot ) ], 縦中央位置dpx, 1.0f );
595                                                 break;
596
597                                         case チップ種別.Snare:
598                                                 _アニメチップを1つ描画する( 表示レーン種別.Snare, this._チップ画像の矩形リスト[ nameof( チップ種別.Snare ) ], 縦中央位置dpx, 音量0to1 );
599                                                 break;
600
601                                         case チップ種別.Snare_ClosedRim:
602                                                 _単画チップを1つ描画する( 表示レーン種別.Snare, this._チップ画像の矩形リスト[ nameof( チップ種別.Snare_ClosedRim ) ], 縦中央位置dpx, 1.0f );
603                                                 break;
604
605                                         case チップ種別.Snare_OpenRim:
606                                                 _単画チップを1つ描画する( 表示レーン種別.Snare, this._チップ画像の矩形リスト[ nameof( チップ種別.Snare_OpenRim ) ], 縦中央位置dpx, 音量0to1 );
607                                                 // ↓ないほうがいいかも。
608                                                 //_単画チップを1つ描画する( 表示レーン種別.Snare, this._チップ画像の矩形リスト[ nameof( チップ種別.Snare ) ], 縦中央位置dpx, 音量0to1 );
609                                                 break;
610
611                                         case チップ種別.Snare_Ghost:
612                                                 _単画チップを1つ描画する( 表示レーン種別.Snare, this._チップ画像の矩形リスト[ nameof( チップ種別.Snare_Ghost ) ], 縦中央位置dpx, 1.0f );
613                                                 break;
614
615                                         case チップ種別.Bass:
616                                                 _アニメチップを1つ描画する( 表示レーン種別.Bass, this._チップ画像の矩形リスト[ nameof( チップ種別.Bass ) ], 縦中央位置dpx, 音量0to1 );
617                                                 break;
618
619                                         case チップ種別.Tom1:
620                                                 _アニメチップを1つ描画する( 表示レーン種別.Tom1, this._チップ画像の矩形リスト[ nameof( チップ種別.Tom1 ) ], 縦中央位置dpx, 音量0to1 );
621                                                 break;
622
623                                         case チップ種別.Tom1_Rim:
624                                                 _単画チップを1つ描画する( 表示レーン種別.Tom1, this._チップ画像の矩形リスト[ nameof( チップ種別.Tom1_Rim ) ], 縦中央位置dpx, 1.0f );
625                                                 break;
626
627                                         case チップ種別.Tom2:
628                                                 _アニメチップを1つ描画する( 表示レーン種別.Tom2, this._チップ画像の矩形リスト[ nameof( チップ種別.Tom2 ) ], 縦中央位置dpx, 音量0to1 );
629                                                 break;
630
631                                         case チップ種別.Tom2_Rim:
632                                                 _単画チップを1つ描画する( 表示レーン種別.Tom2, this._チップ画像の矩形リスト[ nameof( チップ種別.Tom2_Rim ) ], 縦中央位置dpx, 1.0f );
633                                                 break;
634
635                                         case チップ種別.Tom3:
636                                                 _アニメチップを1つ描画する( 表示レーン種別.Tom3, this._チップ画像の矩形リスト[ nameof( チップ種別.Tom3 ) ], 縦中央位置dpx, 音量0to1 );
637                                                 break;
638
639                                         case チップ種別.Tom3_Rim:
640                                                 _単画チップを1つ描画する( 表示レーン種別.Tom3, this._チップ画像の矩形リスト[ nameof( チップ種別.Tom3_Rim ) ], 縦中央位置dpx, 1.0f );
641                                                 break;
642
643                                         case チップ種別.RightCrash:
644                                                 _単画チップを1つ描画する( 表示レーン種別.RightCrash, this._チップ画像の矩形リスト[ nameof( チップ種別.RightCrash ) ], 縦中央位置dpx, 音量0to1 );
645                                                 break;
646
647                                         case チップ種別.China:
648                                                 if( App.ユーザ管理.選択されているユーザ.オプション設定.表示レーンの左右.Chinaは左 )
649                                                 {
650                                                         _単画チップを1つ描画する( 表示レーン種別.LeftCrash, this._チップ画像の矩形リスト[ "LeftChina" ], 縦中央位置dpx, 音量0to1 );
651                                                 }
652                                                 else
653                                                 {
654                                                         _単画チップを1つ描画する( 表示レーン種別.RightCrash, this._チップ画像の矩形リスト[ "RightChina" ], 縦中央位置dpx, 音量0to1 );
655                                                 }
656                                                 break;
657
658                                         case チップ種別.Ride:
659                                                 if( App.ユーザ管理.選択されているユーザ.オプション設定.表示レーンの左右.Rideは左 )
660                                                 {
661                                                         _単画チップを1つ描画する( 表示レーン種別.LeftCrash, this._チップ画像の矩形リスト[ "LeftRide" ], 縦中央位置dpx, 音量0to1 );
662                                                 }
663                                                 else
664                                                 {
665                                                         _単画チップを1つ描画する( 表示レーン種別.RightCrash, this._チップ画像の矩形リスト[ "RightRide" ], 縦中央位置dpx, 音量0to1 );
666                                                 }
667                                                 break;
668
669                                         case チップ種別.Ride_Cup:
670                                                 if( App.ユーザ管理.選択されているユーザ.オプション設定.表示レーンの左右.Rideは左 )
671                                                 {
672                                                         _単画チップを1つ描画する( 表示レーン種別.LeftCrash, this._チップ画像の矩形リスト[ "LeftRide_Cup" ], 縦中央位置dpx, 音量0to1 );
673                                                 }
674                                                 else
675                                                 {
676                                                         _単画チップを1つ描画する( 表示レーン種別.RightCrash, this._チップ画像の矩形リスト[ "RightRide_Cup" ], 縦中央位置dpx, 音量0to1 );
677                                                 }
678                                                 break;
679
680                                         case チップ種別.Splash:
681                                                 if( App.ユーザ管理.選択されているユーザ.オプション設定.表示レーンの左右.Splashは左 )
682                                                 {
683                                                         _単画チップを1つ描画する( 表示レーン種別.LeftCrash, this._チップ画像の矩形リスト[ "LeftSplash" ], 縦中央位置dpx, 音量0to1 );
684                                                 }
685                                                 else
686                                                 {
687                                                         _単画チップを1つ描画する( 表示レーン種別.RightCrash, this._チップ画像の矩形リスト[ "RightSplash" ], 縦中央位置dpx, 音量0to1 );
688                                                 }
689                                                 break;
690
691                                         case チップ種別.LeftCymbal_Mute:
692                                                 _単画チップを1つ描画する( 表示レーン種別.LeftCrash, this._チップ画像の矩形リスト[ "LeftCymbal_Mute" ], 縦中央位置dpx, 1.0f );
693                                                 break;
694
695                                         case チップ種別.RightCymbal_Mute:
696                                                 _単画チップを1つ描画する( 表示レーン種別.RightCrash, this._チップ画像の矩形リスト[ "RightCymbal_Mute" ], 縦中央位置dpx, 1.0f );
697                                                 break;
698                                 }
699                                 //----------------
700                                 #endregion
701
702                         } );
703
704                         #region " ローカル関数 "
705                         //----------------
706                         void _単画チップを1つ描画する( 表示レーン種別 lane, RectangleF? 元矩形dpx, float 上位置dpx, float 音量0to1 )
707                         {
708                                 lock( this._スレッド間同期 )
709                                 {
710                                         if( null == 元矩形dpx )
711                                                 return;
712
713                                         var 画像範囲dpx = (RectangleF) 元矩形dpx;
714
715                                         this._チップ画像?.描画する(
716                                                 dr,
717                                                 左位置dpx: レーンフレームの左端位置dpx + レーンフレーム.レーンto横中央相対位置dpx[ lane ] - ( 画像範囲dpx.Width / 2f ),
718                                                 上位置dpx: 上位置dpx - ( ( 画像範囲dpx.Height / 2f ) * 音量0to1 ),
719                                                 転送元矩形dpx: 元矩形dpx,
720                                                 Y方向拡大率: 音量0to1 );
721                                 }
722                         }
723                         void _アニメチップを1つ描画する( 表示レーン種別 lane, RectangleF? 画像範囲orNull, float Ydpx, float 音量0to1 )
724                         {
725                                 lock( this._スレッド間同期 )
726                                 {
727                                         if( null == 画像範囲orNull )
728                                                 return;
729
730                                         var 画像範囲 = (RectangleF) 画像範囲orNull;
731
732                                         float チップ1枚の高さdpx = 18f;
733                                         画像範囲.Offset( 0f, this._チップアニメ.現在値 * 15f );   // 下端3dpxは下のチップと共有する前提のデザインなので、18f-3f = 15f。
734                                         画像範囲.Height = チップ1枚の高さdpx;
735                                         float 左位置dpx = レーンフレームの左端位置dpx + レーンフレーム.レーンto横中央相対位置dpx[ lane ] - ( 画像範囲.Width / 2f );
736                                         float 上位置dpx = Ydpx - ( チップ1枚の高さdpx / 2f ) * 音量0to1;
737
738                                         this._チップ画像?.描画する( dr, 左位置dpx, 上位置dpx, 転送元矩形dpx: 画像範囲, Y方向拡大率: 音量0to1 );
739                                 }
740                         }
741                         //----------------
742                         #endregion
743                 }
744
745                 // 高頻度進行処理関連。
746
747                 /// <summary>
748                 ///             音声の再生と演奏用入力を行う。
749                 /// </summary>
750                 /// <remarks>
751                 ///             描画処理とは独立したタスクを使い、より高頻度にループさせる。
752                 /// </remarks>
753                 private void _高頻度進行処理タスクエントリ()
754                 {
755                         Log.Info( "高頻度進行処理タスクを開始します。" );
756
757                         this._高頻度進行タスクへのイベント.現在の状態 = TriStateEvent.状態種別.ON;
758
759                         while( this._高頻度進行タスクへのイベント.現在の状態 == TriStateEvent.状態種別.ON )
760                         {
761                                 lock( this._スレッド間同期 )
762                                 {
763                                         // 進行。
764
765                                         this._FPS.FPSをカウントしプロパティを更新する();
766
767                                         #region " 自動ヒット処理。"
768                                         //----------------
769                                         this._描画範囲のチップに処理を適用する( ( chip, index, ヒット判定バーとの時間sec, ヒット判定バーとの距離dpx ) => {
770
771                                                 var オプション設定 = App.ユーザ管理.選択されているユーザ.オプション設定;
772                                                 var 対応表 = オプション設定.ドラムとチップと入力の対応表.対応表[ chip.チップ種別 ];
773                                                 var AutoPlay = オプション設定.AutoPlay[ 対応表.AutoPlay種別 ];
774
775                                                 bool チップはヒット済みである = chip.ヒット済みである;
776                                                 bool チップはMISSエリアに達している = ( ヒット判定バーとの時間sec > オプション設定.最大ヒット距離sec[ ヒットランク種別.POOR ] );
777                                                 bool チップはヒット判定バーを通過した = ( 0 <= ヒット判定バーとの距離dpx );
778
779                                                 if( チップはヒット済みである )
780                                                 {
781                                                         // 何もしない。
782                                                         return;
783                                                 }
784
785                                                 if( チップはMISSエリアに達している )
786                                                 {
787                                                         // MISS判定。
788                                                         if( AutoPlay && 対応表.AutoPlayON.MISS判定 )
789                                                         {
790                                                                 this._チップのヒット処理を行う( chip, ヒットランク種別.MISS, 対応表.AutoPlayON.自動ヒット時処理 );
791                                                                 return;
792                                                         }
793                                                         else if( !AutoPlay && 対応表.AutoPlayOFF.MISS判定 )
794                                                         {
795                                                                 this._チップのヒット処理を行う( chip, ヒットランク種別.MISS, 対応表.AutoPlayOFF.自動ヒット時処理 );
796                                                                 return;
797                                                         }
798                                                         else
799                                                         {
800                                                                 // 通過。
801                                                         }
802                                                 }
803
804                                                 if( チップはヒット判定バーを通過した )
805                                                 {
806                                                         // 自動ヒット判定。
807                                                         if( AutoPlay && 対応表.AutoPlayON.自動ヒット )
808                                                         {
809                                                                 this._チップのヒット処理を行う( chip, ヒットランク種別.AUTO, 対応表.AutoPlayON.自動ヒット時処理 );
810                                                                 return;
811                                                         }
812                                                         else if( !AutoPlay && 対応表.AutoPlayOFF.自動ヒット )
813                                                         {
814                                                                 this._チップのヒット処理を行う( chip, ヒットランク種別.AUTO, 対応表.AutoPlayOFF.自動ヒット時処理 );
815                                                                 return;
816                                                         }
817                                                         else
818                                                         {
819                                                                 // 通過。
820                                                         }
821                                                 }
822
823                                         } );
824                                         //----------------
825                                         #endregion
826
827                                         // 入力。
828
829                                         App.入力管理.すべての入力デバイスをポーリングする( 入力履歴を記録する: false );
830
831                                         if( App.入力管理.キーボードデバイス.キーが押された( 0, Key.Escape ) )
832                                         {
833                                                 #region " ESC → ステージキャンセル "
834                                                 //----------------
835                                                 if( App.ビュアーモードではない )
836                                                 {
837                                                         this.BGMを停止する();
838                                                         this.現在のフェーズ = フェーズ.キャンセル;
839                                                         break;  // このタスクを終了。
840                                                 }
841                                                 else
842                                                 {
843                                                         // ビュアーモード時のキャンセルは無効。
844                                                 }
845                                                 //----------------
846                                                 #endregion
847                                         }
848                                         if( App.入力管理.キーボードデバイス.キーが押された( 0, Key.Up ) )
849                                         {
850                                                 #region " 上 → 譜面スクロールを加速 "
851                                                 //----------------
852                                                 const double 最大倍率 = 8.0;
853                                                 App.ユーザ管理.選択されているユーザ.オプション設定.譜面スクロール速度の倍率 =
854                                                         Math.Min( App.ユーザ管理.選択されているユーザ.オプション設定.譜面スクロール速度の倍率 + 0.5, 最大倍率 );
855                                                 //----------------
856                                                 #endregion
857                                         }
858                                         if( App.入力管理.キーボードデバイス.キーが押された( 0, Key.Down ) )
859                                         {
860                                                 #region " 下 → 譜面スクロールを減速 "
861                                                 //----------------
862                                                 const double 最小倍率 = 0.5;
863                                                 App.ユーザ管理.選択されているユーザ.オプション設定.譜面スクロール速度の倍率 =
864                                                         Math.Max( App.ユーザ管理.選択されているユーザ.オプション設定.譜面スクロール速度の倍率 - 0.5, 最小倍率 );
865                                                 //----------------
866                                                 #endregion
867                                         }
868
869                                         #region " ユーザヒット処理。"
870                                         //----------------
871                                         foreach( var 入力 in App.入力管理.ポーリング結果 )
872                                         {
873                                                 if( 入力.InputEvent.離された )
874                                                         continue;   // 押下イベントじゃないなら無視。
875
876                                                 // todo: チップに対応するパッドのアニメを開始。
877                                         }
878
879                                         var 処理済み入力 = new List<ドラム入力イベント>(); // ヒット処理が終わった入力は、二重処理しないよう、この中に追加しておく。
880
881                                         this._描画範囲のチップに処理を適用する( ( chip, index, ヒット判定バーとの時間sec, ヒット判定バーとの距離dpx ) => {
882
883                                                 var オプション設定 = App.ユーザ管理.選択されているユーザ.オプション設定;
884                                                 var 対応表 = オプション設定.ドラムとチップと入力の対応表.対応表[ chip.チップ種別 ];
885                                                 var AutoPlay = オプション設定.AutoPlay[ 対応表.AutoPlay種別 ];
886                                                 var ヒット判定バーとの時間の絶対値sec = Math.Abs( ヒット判定バーとの時間sec );
887
888                                                 bool チップはヒット済みである = chip.ヒット済みである;
889                                                 bool チップはMISSエリアに達している = ( ヒット判定バーとの時間sec > オプション設定.最大ヒット距離sec[ ヒットランク種別.POOR ] );
890                                                 bool チップはヒット可能エリアにある = ( ヒット判定バーとの時間sec >= -( オプション設定.最大ヒット距離sec[ ヒットランク種別.POOR ] ) && !チップはMISSエリアに達している );
891
892                                                 if( AutoPlay || チップはヒット済みである || !( 対応表.AutoPlayOFF.ユーザヒット ) || !( チップはヒット可能エリアにある ) )
893                                                         return;
894
895                                                 // チップにヒットする入力を探す。
896
897                                                 // todo: シンバルフリーを実装する。
898                                                 var ヒット入力 = App.入力管理.ポーリング結果.FirstOrDefault( ( 入力 ) =>
899                                                         ( 入力.InputEvent.押された ) &&
900                                                         ( 対応表.ドラム入力種別 == 入力.Type ) &&
901                                                         !( 処理済み入力.Contains( 入力 ) )
902                                                 );
903
904                                                 if( null == ヒット入力 )
905                                                 {
906                                                         return;
907                                                 }
908                                                 else
909                                                 {
910                                                         処理済み入力.Add( ヒット入力 );
911                                                 }
912
913                                                 // ヒットランクを判定する。
914
915                                                 var ヒットランク = ヒットランク種別.POOR;
916
917                                                 if( ヒット判定バーとの時間の絶対値sec <= オプション設定.最大ヒット距離sec[ ヒットランク種別.PERFECT ] )
918                                                 {
919                                                         ヒットランク = ヒットランク種別.PERFECT;
920                                                 }
921                                                 else if( ヒット判定バーとの時間の絶対値sec <= オプション設定.最大ヒット距離sec[ ヒットランク種別.GREAT ] )
922                                                 {
923                                                         ヒットランク = ヒットランク種別.GREAT;
924                                                 }
925                                                 else if( ヒット判定バーとの時間の絶対値sec <= オプション設定.最大ヒット距離sec[ ヒットランク種別.GOOD ] )
926                                                 {
927                                                         ヒットランク = ヒットランク種別.GOOD;
928                                                 }
929
930                                                 // ヒット処理。
931
932                                                 this._チップのヒット処理を行う( chip, ヒットランク, 対応表.AutoPlayOFF.ユーザヒット時処理 );
933
934                                         } );
935                                         //----------------
936                                         #endregion
937                                 }
938
939                                 // ウェイト
940                                 Thread.Sleep( 1 );
941                         }
942
943                         this._高頻度進行タスクへのイベント.現在の状態 = TriStateEvent.状態種別.無効;
944
945                         Log.Info( "高頻度進行処理タスクを終了しました。" );
946                 }
947
948                 /// <summary>
949                 ///             <see cref="_描画開始チップ番号"/> から画面上端にはみ出すまでの間の各チップに対して、指定された処理を適用する。
950                 /// </summary>
951                 /// <param name="適用する処理">引数は、順に、対象のチップ、チップ番号、ヒット判定バーとの時間sec、ヒット判定バーとの距離dpx</param>
952                 private void _描画範囲のチップに処理を適用する( Action<チップ, int, double, double> 適用する処理 )
953                 {
954                         lock( this._スレッド間同期 )
955                         {
956                                 var スコア = App.演奏スコア;
957                                 if( null == スコア )
958                                         return;
959
960                                 double リアルタイム演奏時刻sec = this._演奏開始からの経過時間secを返す();
961
962                                 for( int i = this._描画開始チップ番号; ( 0 <= i ) && ( i < スコア.チップリスト.Count ); i++ )
963                                 {
964                                         var チップ = スコア.チップリスト[ i ];
965
966                                         // ヒット判定バーとチップの間の、時間 と 距離 を算出。→ いずれも、負数ならバー未達、0でバー直上、正数でバー通過。
967                                         double ヒット判定バーとの時間sec = リアルタイム演奏時刻sec - チップ.描画時刻sec;
968                                         double ヒット判定バーとの距離dpx = スコア.指定された時間secに対応する符号付きピクセル数を返す( this._現在進行描画中の譜面スクロール速度の倍率, ヒット判定バーとの時間sec );
969
970                                         // 終了判定。
971                                         bool チップは画面上端より上に出ている = ( ( ヒット判定バーの中央Y座標dpx + ヒット判定バーとの距離dpx ) < -40.0 );   // -40dpx はチップが隠れるであろう適当なマージン。
972                                         if( チップは画面上端より上に出ている )
973                                                 break;
974
975                                         // 処理実行。開始判定(描画開始チップ番号の更新)もこの中で。
976                                         適用する処理( チップ, i, ヒット判定バーとの時間sec, ヒット判定バーとの距離dpx );
977                                 }
978                         }
979                 }
980
981                 private void _チップのヒット処理を行う( チップ chip, ヒットランク種別 ヒットランク, ドラムとチップと入力の対応表.Column.Columnヒット処理 ヒット処理表 )
982                 {
983                         lock( this._スレッド間同期 )
984                         {
985                                 chip.ヒット済みである = true;
986
987                                 if( ヒット処理表.再生 )
988                                 {
989                                         #region " チップを再生する。"
990                                         //----------------
991                                         if( chip.チップ種別 == チップ種別.背景動画 )
992                                         {
993                                                 // 背景動画の再生を開始する。
994                                                 this._背景動画?.再生を開始する();
995                                                 this._背景動画開始済み = true;
996
997                                                 // BGMの再生を開始する。
998                                                 this._BGM?.Play();
999                                                 this._BGM再生開始済み = true;
1000                                         }
1001                                         else
1002                                         {
1003                                                 if( App.システム設定.Autoチップのドラム音を再生する )
1004                                                         this._ドラムサウンド.発声する( chip.チップ種別, ( chip.音量 / ( float ) チップ.最大音量 ) );
1005                                         }
1006                                         //----------------
1007                                         #endregion
1008                                 }
1009                                 if( ヒット処理表.判定 )
1010                                 {
1011                                         #region " チップの判定処理を行う。"
1012                                         //----------------
1013                                         if( ヒットランク != ヒットランク種別.MISS )
1014                                         {
1015                                                 this._コンボ.COMBO値++;
1016                                                 this._回転羽.発火する( 
1017                                                         new Vector2(
1018                                                                 レーンフレームの左端位置dpx + レーンフレーム.レーンto横中央相対位置dpx[ App.ユーザ管理.選択されているユーザ.オプション設定.ドラムとチップと入力の対応表.対応表[ chip.チップ種別 ].表示レーン種別 ],
1019                                                                 ヒット判定バーの中央Y座標dpx ) );
1020                                                 // todo: this.ヒットした回数[ hitType ]++;
1021                                                 // todo: this._ヒット判定文字列.表示開始( chipType, hitType );
1022                                         }
1023                                         else
1024                                         {
1025                                                 this._コンボ.COMBO値 = 0;
1026                                                 // todo: this._ヒット判定文字列.表示開始( chipType, ヒットランク種別.MISS );
1027                                         }
1028                                         //----------------
1029                                         #endregion
1030                                 }
1031                                 if( ヒット処理表.非表示 )
1032                                 {
1033                                         #region " チップを非表示にする。"
1034                                         //----------------
1035                                         if( ヒットランク != ヒットランク種別.MISS )
1036                                         {
1037                                                 chip.可視 = false;        // PERFECT~POOR チップは非表示。
1038                                         }
1039                                         else
1040                                         {
1041                                                 if( chip.可視 )
1042                                                         chip.可視 = false;    // MISSチップは最後まで表示し続ける。
1043                                         }
1044                                         //----------------
1045                                         #endregion
1046                                 }
1047                         }
1048                 }
1049         }
1050 }