OSDN Git Service

選曲ステージを実装。
authorくまかみ工房 <kumakamikoubou@gmail.com>
Sun, 18 Sep 2016 14:28:46 +0000 (23:28 +0900)
committerくまかみ工房 <kumakamikoubou@gmail.com>
Sun, 18 Sep 2016 14:28:46 +0000 (23:28 +0900)
StrokeStyleT/StrokeStyleT.cs
StrokeStyleT/StrokeStyleT.csproj
StrokeStyleT/ステージ/汎用/ドラムセット.cs [new file with mode: 0644]
StrokeStyleT/ステージ/選曲/曲パネルビュー.cs [new file with mode: 0644]
StrokeStyleT/ステージ/選曲/選曲ステージ.cs [new file with mode: 0644]
StrokeStyleT/曲/MusicNode.cs
StrokeStyleT/曲/Node.cs

index d6e4d56..2da43f1 100644 (file)
@@ -15,6 +15,7 @@ namespace SST
                public static FDK.メディア.サウンド.WASAPI排他.ExclusiveDevice Wasapiデバイス => StrokeStyleT.bs_Wasapiデバイス;
                public static Random 乱数 => StrokeStyleT.bs_乱数;
                public static SST.ユーザ.ユーザ管理 ユーザ管理 => StrokeStyleT.bs_ユーザ管理;
+               public static SST.曲.曲ツリー管理 曲ツリー管理 => StrokeStyleT.bs_曲ツリー管理;
 
                public static void すべての入力デバイスをポーリングする()
                {
@@ -34,6 +35,7 @@ namespace SST
 
                        // その他の static の生成。
                        StrokeStyleT.bs_ユーザ管理 = new ユーザ.ユーザ管理();
+                       StrokeStyleT.bs_曲ツリー管理 = new 曲.曲ツリー管理();
                }
 
                public override void 初期化する()
@@ -105,6 +107,7 @@ namespace SST
                        this.起動ステージ = null;
                        this.タイトルステージ = null;
                        this.ログインステージ = null;
+                       this.選曲ステージ = null;
                        //----------------
                        #endregion
                        #region " MIDI入力 を解放する。"
@@ -130,7 +133,6 @@ namespace SST
                        //-----------------
                        #endregion
                }
-
                public override void シーンを描画する()
                {
                        #region " 描画を準備する。"
@@ -211,10 +213,40 @@ namespace SST
                                        break;
 
                                case nameof( ステージ.ログイン.ログインステージ ):
-                                       #region " *** "
-                                       //----------------
-                                       //----------------
+                                       #region " 確定 → ログイン処理を行って、選曲ステージへ。"
+                                       //---------------
+                                       if( this.ログインステージ.現在のフェーズ == ステージ.ログイン.ログインステージ.フェーズ.確定 )
+                                       {
+                                               // ログイン処理(1) ユーザリストに対してユーザを選択する。
+                                               StrokeStyleT.ユーザ管理.ユーザを選択する( this.ログインステージ.ユーザインデックス );
+                                               FDK.Log.Info( $"ユーザが選択されました。[{StrokeStyleT.ユーザ管理.現在選択されているユーザ.名前}]" );
+
+                                               // ログイン処理(2) 選択ユーザの曲ツリーを構築する。
+                                               StrokeStyleT.ユーザ管理.現在選択されているユーザ.曲を検索して曲ツリーを構築する();
+
+                                               // ログイン処理(3) 構築した曲ツリーを曲リスト管理に登録する。
+                                               StrokeStyleT.曲ツリー管理.現在の管理対象ツリー = StrokeStyleT.ユーザ管理.現在選択されているユーザ.曲ツリーのルートノード;
+
+                                               // 選曲ステージへ。
+                                               this.現在のステージ.非活性化する( this.デバイスリソース );
+                                               this.現在のステージ = this.選曲ステージ;
+                                               this.現在のステージ.活性化する( this.デバイスリソース );
+                                       }
+                                       //---------------
                                        #endregion
+                                       #region " キャンセル → タイトルステージへ。"
+                                       //---------------
+                                       else if( this.ログインステージ.現在のフェーズ == ステージ.ログイン.ログインステージ.フェーズ.キャンセル )
+                                       {
+                                               this.現在のステージ.非活性化する( this.デバイスリソース );
+                                               this.現在のステージ = this.タイトルステージ;
+                                               this.現在のステージ.活性化する( this.デバイスリソース );
+                                       }
+                                       //---------------
+                                       #endregion
+                                       break;
+
+                               case nameof( ステージ.選曲.選曲ステージ ):
                                        break;
                        }
                        //----------------
@@ -226,6 +258,7 @@ namespace SST
                protected SST.ステージ.起動.起動ステージ 起動ステージ = new ステージ.起動.起動ステージ();
                protected SST.ステージ.タイトル.タイトルステージ タイトルステージ = new ステージ.タイトル.タイトルステージ();
                protected SST.ステージ.ログイン.ログインステージ ログインステージ = new ステージ.ログイン.ログインステージ();
+               protected SST.ステージ.選曲.選曲ステージ 選曲ステージ = new ステージ.選曲.選曲ステージ();
 
                private SST.ステージ.ステージ 現在のステージ = null;
 
@@ -237,6 +270,7 @@ namespace SST
                private static FDK.メディア.サウンド.WASAPI排他.ExclusiveDevice bs_Wasapiデバイス = null;
                private static readonly Random bs_乱数 = new Random( DateTime.Now.Millisecond );
                private static SST.ユーザ.ユーザ管理 bs_ユーザ管理 = null;
+               private static SST.曲.曲ツリー管理 bs_曲ツリー管理 = null;
                //----------------
                #endregion
 
index 529a963..e7cc54f 100644 (file)
     <Compile Include="ステージ\ステージ.cs" />
     <Compile Include="ステージ\タイトル\タイトルステージ.cs" />
     <Compile Include="ステージ\ログイン\ログインステージ.cs" />
+    <Compile Include="ステージ\汎用\ドラムセット.cs" />
     <Compile Include="ステージ\汎用\フェードアウト.cs" />
     <Compile Include="ステージ\汎用\フェードイン.cs" />
     <Compile Include="ステージ\起動\起動ステージ.cs" />
+    <Compile Include="ステージ\選曲\曲パネルビュー.cs" />
+    <Compile Include="ステージ\選曲\選曲ステージ.cs" />
     <Compile Include="フォルダ.cs" />
     <Compile Include="ユーザ\XML.cs" />
     <Compile Include="ユーザ\ユーザ.cs" />
diff --git a/StrokeStyleT/ステージ/汎用/ドラムセット.cs b/StrokeStyleT/ステージ/汎用/ドラムセット.cs
new file mode 100644 (file)
index 0000000..9e6ec3e
--- /dev/null
@@ -0,0 +1,106 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using FDK.メディア;
+
+namespace SST.ステージ.汎用
+{
+       /// <summary>
+       /// ドラム一式を表す Activity。表示と操作が可能。
+       /// </summary>
+       class ドラムセット : FDK.Activity
+       {
+               /// <summary>
+               /// 0.0:閉じる ~ 1.0:開く
+               /// </summary>
+               public float ハイハットの開度
+               {
+                       get
+                       {
+                               return this.bs_ハイハットの開度;
+                       }
+                       set
+                       {
+                               if( 1.0f < value )
+                                       throw new OutOfMemoryException( "1.0 を超える値は設定できません。" );
+                               else if( 0.0f > value )
+                                       throw new OutOfMemoryException( "負数は設定できません。" );
+                               else
+                                       this.bs_ハイハットの開度 = value;
+                       }
+               }
+
+               public ドラムセット()
+               {
+               }
+               protected override void On活性化( デバイスリソース dr )
+               {
+                       this.HiHatTop.活性化する( dr );
+                       this.HiHatBottom.活性化する( dr );
+                       this.Snare.活性化する( dr );
+                       this.Bass.活性化する( dr );
+                       this.HiTom.活性化する( dr );
+                       this.LowTom.活性化する( dr );
+                       this.FloorTom.活性化する( dr );
+                       this.LCymbal.活性化する( dr );
+                       this.LCymbalStand.活性化する( dr );
+                       this.LCymbalTop.活性化する( dr );
+                       this.RCymbal.活性化する( dr );
+                       this.RCymbalStand.活性化する( dr );
+                       this.RCymbalTop.活性化する( dr );
+               }
+               protected override void On非活性化( デバイスリソース dr )
+               {
+                       this.HiHatTop.非活性化する( dr );
+                       this.HiHatBottom.非活性化する( dr );
+                       this.Snare.非活性化する( dr );
+                       this.Bass.非活性化する( dr );
+                       this.HiTom.非活性化する( dr );
+                       this.LowTom.非活性化する( dr );
+                       this.FloorTom.非活性化する( dr );
+                       this.LCymbal.非活性化する( dr );
+                       this.LCymbalStand.非活性化する( dr );
+                       this.LCymbalTop.非活性化する( dr );
+                       this.RCymbal.非活性化する( dr );
+                       this.RCymbalStand.非活性化する( dr );
+                       this.RCymbalTop.非活性化する( dr );
+               }
+               public void 進行描画する( デバイスリソース dr )
+               {
+                       this.Bass.進行描画する( dr, 881f, 891f );
+                       this.LowTom.進行描画する( dr, 999f, 901f );
+                       this.HiTom.進行描画する( dr, 850f, 901f );
+                       this.FloorTom.進行描画する( dr, 1050f, 981f );
+                       this.Snare.進行描画する( dr, 741f, 988f );
+                       this.HiHatBottom.進行描画する( dr, 657f, 935f );
+                       this.HiHatTop.進行描画する( dr, 657f, ( 935f - 20f * this.ハイハットの開度 ) );
+                       this.RCymbalStand.進行描画する( dr, 1229f, 920f );
+                       this.RCymbal.進行描画する( dr, 1257f - this.RCymbal.サイズdpx.Width / 2f, 923f - this.RCymbal.サイズdpx.Height / 2f );
+                       this.RCymbalTop.進行描画する( dr, 1229f, 880f );
+                       this.LCymbalStand.進行描画する( dr, 472f, 916f );
+                       this.LCymbal.進行描画する( dr, 649f - this.LCymbal.サイズdpx.Width / 2f, 918f - this.LCymbal.サイズdpx.Height / 2f );
+                       this.LCymbalTop.進行描画する( dr, 633f, 891f );
+               }
+
+               private readonly FDK.メディア.画像 HiHatTop = new 画像( @"$(Static)\images\DrumKit HiHat Top.png" );
+               private readonly FDK.メディア.画像 HiHatBottom = new 画像( @"$(Static)\images\DrumKit HiHat Bottom.png" );
+               private readonly FDK.メディア.画像 Snare = new 画像( @"$(Static)\images\DrumKit Snare.png" );
+               private readonly FDK.メディア.画像 Bass = new 画像( @"$(Static)\images\DrumKit Bass.png" );
+               private readonly FDK.メディア.画像 HiTom = new 画像( @"$(Static)\images\DrumKit HiTom.png" );
+               private readonly FDK.メディア.画像 LowTom = new 画像( @"$(Static)\images\DrumKit LowTom.png" );
+               private readonly FDK.メディア.画像 FloorTom = new 画像( @"$(Static)\images\DrumKit FloorTom.png" );
+               private readonly FDK.メディア.画像 LCymbal = new 画像( @"$(Static)\images\DrumKit LCymbal.png" );
+               private readonly FDK.メディア.画像 LCymbalStand = new 画像( @"$(Static)\images\DrumKit LCymbal Stand.png" );
+               private readonly FDK.メディア.画像 LCymbalTop = new 画像( @"$(Static)\images\DrumKit LCymbal Top.png" );
+               private readonly FDK.メディア.画像 RCymbal = new 画像( @"$(Static)\images\DrumKit RCymbal.png" );
+               private readonly FDK.メディア.画像 RCymbalStand = new 画像( @"$(Static)\images\DrumKit RCymbal Stand.png" );
+               private readonly FDK.メディア.画像 RCymbalTop = new 画像( @"$(Static)\images\DrumKit RCymbal Top.png" );
+
+               #region " バックストア。"
+               //-----------------
+               private float bs_ハイハットの開度 = 1.0f;
+               //-----------------
+               #endregion
+       }
+}
diff --git a/StrokeStyleT/ステージ/選曲/曲パネルビュー.cs b/StrokeStyleT/ステージ/選曲/曲パネルビュー.cs
new file mode 100644 (file)
index 0000000..e97f15a
--- /dev/null
@@ -0,0 +1,381 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using FDK.メディア;
+
+namespace SST.ステージ.選曲
+{
+       /// <summary>
+       /// 曲パネルリスト(9列3行)を表示する。
+       /// </summary>
+       /// <remarks>
+       /// 静止時の可視範囲は、左右両端の1列を除いた7列3行。
+       /// 左右両端の列は、静止時は画面外に位置しており、左右スクロール中にのみ画面内に一部が表示される。
+       /// 「カーソル」は、常に、曲パネル内のいずれか1枚の曲を選択している。
+       /// カーソルは、初期状態では中央の曲を選択しており、本クラスのメソッドを使って上下左右に移動することができる。
+       /// カーソルが選択している曲は強調表示(視覚効果として少し拡大して表示)される。
+       /// </remarks>
+       class 曲パネルビュー : FDK.Activity
+       {
+               public 曲パネルビュー()
+               {
+               }
+               protected override void On活性化( デバイスリソース dr )
+               {
+                       this.Nullパネルの画像.活性化する( dr );
+                       this.活性化した直後である = true;
+               }
+               protected override void On非活性化( デバイスリソース dr )
+               {
+                       this.Nullパネルの画像.非活性化する( dr );
+               }
+               public void 進行描画する( デバイスリソース dr )
+               {
+                       // 進行。
+
+                       if( this.活性化した直後である )
+                       {
+                               this.活性化した直後である = false;
+                               this.横スクロール用カウンタ = new FDK.カウンタ.定間隔進行();
+                       }
+                       else
+                       {
+                               this.横スクロール用カウンタ.経過時間の分だけ進行する( 8, () => {
+
+                                       int オフセットの加減算速度 = 1;
+
+                                       #region " カーソルが中央から遠いほど速くなるよう、オフセットの加減算速度(絶対値)を計算する。"
+                                       //------------------
+                                       int 距離 = Math.Abs( 4 - this.カーソル位置.X );
+                                       if( 1 >= 距離 )
+                                               オフセットの加減算速度 = 2;
+                                       else if( 4 >= 距離 )
+                                               オフセットの加減算速度 = 4;
+                                       else if( 6 >= 距離 )
+                                               オフセットの加減算速度 = 6;
+                                       else
+                                               オフセットの加減算速度 = 8;
+                                       //------------------
+                                       #endregion
+
+                                       // オフセット と カーソル位置.X を更新する。
+                                       if( ( 4 > this.カーソル位置.X ) ||
+                                               ( ( 4 == this.カーソル位置.X ) && ( 0 > this.曲パネルのY軸回転オフセット ) ) )
+                                       {
+                                               #region "  パネルは、左から右へ、移動する。"
+                                               //-----------------
+                                               this.曲パネルのY軸回転オフセット += オフセットの加減算速度;
+
+                                               // 1列分移動した
+                                               if( 64 <= this.曲パネルのY軸回転オフセット )
+                                               {
+                                                       this.曲パネルのY軸回転オフセット -= 64;  // 0 付近に戻る
+                                                       this.カーソル位置.X++;
+                                               }
+                                               //-----------------
+                                               #endregion
+                                       }
+                                       else if( ( 4 < this.カーソル位置.X ) ||
+                                               ( ( 4 == this.カーソル位置.X ) && ( 0 < this.曲パネルのY軸回転オフセット ) ) )
+                                       {
+                                               #region " パネルは、右から左へ、移動する。"
+                                               //-----------------
+                                               this.曲パネルのY軸回転オフセット -= オフセットの加減算速度;
+
+                                               // 1列分移動した
+                                               if( -64 >= this.曲パネルのY軸回転オフセット )
+                                               {
+                                                       this.曲パネルのY軸回転オフセット += 64;  // 0 付近に戻る
+                                                       this.カーソル位置.X--;
+                                               }
+                                               //-----------------
+                                               #endregion
+                                       }
+
+                               } );
+                       }
+
+                       // 描画。
+
+                       Debug.Assert( null != StrokeStyleT.ユーザ管理 );
+                       Debug.Assert( null != StrokeStyleT.ユーザ管理.現在選択されているユーザ );
+                       Debug.Assert( null != StrokeStyleT.ユーザ管理.現在選択されているユーザ.曲ツリーのルートノード );
+
+                       var 曲リスト管理 = StrokeStyleT.曲ツリー管理;
+                       var 曲リスト = 曲リスト管理.現在選択されているノード?.親ノード.子ノードリスト ?? 曲リスト管理.現在の管理対象ツリー.子ノードリスト;
+                       var 描画する曲 = 曲リスト管理.現在選択されているノード;    // null 可
+                       var カーソル位置の曲 = (曲.Node) null;   // null 可
+
+                       #region " 左上隅パネルに対応する曲リストノードを検索する。"
+                       //-----------------
+                       int 現在のカーソルから左上隅までの差 = 0 - ( this.カーソル位置.X * 3 + this.カーソル位置.Y );   // 現在のカーソルの位置に、現在選択されている曲が対応するものとする。
+
+                       if( 0 < 現在のカーソルから左上隅までの差 )
+                       {
+                               // 曲リストを前方へたどる。
+                               for( int i = 0; i < 現在のカーソルから左上隅までの差; i++ )
+                                       描画する曲 = 描画する曲?.次のノード;
+                       }
+                       else
+                       {
+                               // 曲リストを後方へたどる。
+                               for( int i = 現在のカーソルから左上隅までの差; i < 0; i++ )
+                                       描画する曲 = 描画する曲?.前のノード;
+                       }
+                       //-----------------
+                       #endregion
+                       #region " 9×3枚の曲パネルを描画する。"
+                       //-----------------
+                       for( int i = 0; i < ( 9 * 3 ); i++ )
+                       {
+                               var パネル位置 = new SharpDX.Point( i / 3, i % 3 );
+
+                               if( パネル位置 == this.カーソル位置 )
+                               {
+                                       // カーソル位置にあるパネルは後で描画するので、覚えておく。
+                                       カーソル位置の曲 = 描画する曲;
+                               }
+                               else
+                               {
+                                       this.パネルを一枚描画する(
+                                               dr,
+                                               描画する曲,
+                                               パネル位置,
+                                               これはカーソル位置のパネルである: false );
+                               }
+
+                               // 次のノードへ移動。(選択ノードは移動しない。)
+                               描画する曲 = 描画する曲?.次のノード;
+                       }
+                       //-----------------
+                       #endregion
+                       #region " カーソル位置のパネルを描画する。"
+                       //-----------------
+                       if( ( 0 <= this.カーソル位置.X ) && ( 9 > this.カーソル位置.X ) &&
+                               ( 0 <= this.カーソル位置.Y ) && ( 3 > this.カーソル位置.Y ) )
+                       {
+                               this.パネルを一枚描画する(
+                                       dr,
+                                       カーソル位置の曲,   // 先の for ループ内で取得済み。
+                                       this.カーソル位置,
+                                       これはカーソル位置のパネルである: true );
+                       }
+                       //-----------------
+                       #endregion
+               }
+               public void カーソルを上に移動する()
+               {
+                       if( 0 < this.カーソル位置.Y )
+                       {
+                               this.カーソル位置.Y--;
+                       }
+                       else
+                       {
+                               this.カーソル位置.X--;
+                               this.カーソル位置.Y = 2;
+                       }
+
+                       // カーソルと一緒に、選択曲も移動する。
+                       StrokeStyleT.曲ツリー管理.前のノードを選択する();
+               }
+               public void カーソルを下に移動する()
+               {
+                       Debug.Assert( this.活性化している );
+
+                       if( 2 > this.カーソル位置.Y )
+                       {
+                               this.カーソル位置.Y++;
+                       }
+                       else
+                       {
+                               this.カーソル位置.X++;
+                               this.カーソル位置.Y = 0;
+                       }
+
+                       // カーソルと一緒に、選択曲も移動する。
+                       StrokeStyleT.曲ツリー管理.次のノードを選択する();
+               }
+               public void カーソルを左に移動する()
+               {
+                       Debug.Assert( this.活性化している );
+
+                       this.カーソル位置.X--;    // 制限なし
+
+                       // カーソルと一緒に、選択曲も移動する。
+                       StrokeStyleT.曲ツリー管理.前のノードを選択する();
+                       StrokeStyleT.曲ツリー管理.前のノードを選択する();
+                       StrokeStyleT.曲ツリー管理.前のノードを選択する();
+               }
+               public void カーソルを右に移動する()
+               {
+                       Debug.Assert( this.活性化している );
+
+                       this.カーソル位置.X++;    // 制限なし
+
+                       // カーソルと一緒に、選択曲も移動する。
+                       StrokeStyleT.曲ツリー管理.次のノードを選択する();
+                       StrokeStyleT.曲ツリー管理.次のノードを選択する();
+                       StrokeStyleT.曲ツリー管理.次のノードを選択する();
+               }
+
+               private void パネルを一枚描画する(
+                       デバイスリソース dr,
+                       曲.Node パネルノード,  // null 可
+                       SharpDX.Point パネル位置,
+                       bool これはカーソル位置のパネルである = false )
+               {
+                       const float カーソル位置のパネルの拡大率 = 1.25f;
+
+                       #region " 2D変換行列を作成する(ノード画像、タイトル画像共通)。"
+                       //-----------------
+                       // 全体的に、画面中央のちょい上に移動する。
+                       float ちょい上dpx = -70.0f;    // ステージ台とパネルが接触しない程度の位置へ。
+                       var 変換行列2Dpx = dr.行列を単位変換するDPXtoPX(
+                               SharpDX.Matrix3x2.Translation(
+                                       dr.設計画面サイズdpx.Width / 2.0f,
+                                       dr.設計画面サイズdpx.Height / 2.0f + ちょい上dpx ) );
+
+                       if( これはカーソル位置のパネルである )
+                       {
+                               // 拡大する分だけ表示座標を移動する。
+                               変換行列2Dpx *= dr.行列を単位変換するDPXtoPX(
+                                       SharpDX.Matrix3x2.Translation(
+                                               -パネルサイズdpx.全体dpx.Width * ( カーソル位置のパネルの拡大率 - 1.0f ) / 2.0f,
+                                               -パネルサイズdpx.全体dpx.Height * ( カーソル位置のパネルの拡大率 - 1.0f ) / 2.0f ) );
+                       }
+                       //-----------------
+                       #endregion
+
+                       // パネル位置にオフセットを加味し、横位置の割合を算出する。
+                       const float 列間隔の角度 = 0.104f;    // 等間隔
+                       float 横位置割合 = 0.920f - ( パネル位置.X + ( this.曲パネルのY軸回転オフセット / 64.0f ) ) * 列間隔の角度;
+
+                       var 変換行列3Dpx = SharpDX.Matrix.Identity;
+
+                       if( null != パネルノード )
+                       {
+                               #region " 3D変換行列を作成してノード画像を表示する。"
+                               //-----------------
+                               変換行列3Dpx = SharpDX.Matrix.Scaling(
+                                       x: パネルサイズdpx.画像領域dpx.Width / パネルノード.ノード画像サイズdpx.Width, // ノード画像を設計値サイズに拡大縮小する。
+                                       y: パネルサイズdpx.画像領域dpx.Height / パネルノード.ノード画像サイズdpx.Height,
+                                       z: 1.0f );
+
+                               // カーソル位置なら拡大する。
+                               if( これはカーソル位置のパネルである )
+                                       変換行列3Dpx *= SharpDX.Matrix.Scaling( カーソル位置のパネルの拡大率 );
+
+                               // 曲面変換。
+                               変換行列3Dpx *= this.横位置割合からパネルの変換行列を算出して返す( dr, 横位置割合, パネル位置.Y );
+
+                               // 描画。
+                               パネルノード.ノード画像を描画する( dr, 変換行列2Dpx, 変換行列3Dpx );
+                               //-----------------
+                               #endregion
+                               #region " 3D変換行列を作成してタイトル画像を表示する。"
+                               //-----------------
+                               変換行列3Dpx = SharpDX.Matrix.Scaling(
+                                       x: パネルサイズdpx.タイトル領域dpx.Width / パネルノード.タイトル画像サイズdpx.Width,  // タイトル画像を設計値サイズに拡大縮小する。
+                                       y: パネルサイズdpx.タイトル領域dpx.Height / パネルノード.タイトル画像サイズdpx.Height,
+                                       z: 1.0f );
+
+                               // カーソル位置なら拡大する。
+                               if( これはカーソル位置のパネルである )
+                                       変換行列3Dpx *= SharpDX.Matrix.Scaling( カーソル位置のパネルの拡大率 );
+
+                               // 曲面変換。
+                               変換行列3Dpx *= this.横位置割合からパネルの変換行列を算出して返す( dr, 横位置割合, パネル位置.Y );
+
+                               // 描画。
+                               パネルノード.タイトル画像を描画する( dr, 変換行列2Dpx, 変換行列3Dpx );
+                               //-----------------
+                               #endregion
+                       }
+                       else
+                       {
+                               #region " 3D変換行列を作成してノード画像を表示する。"
+                               //-----------------
+                               変換行列3Dpx = SharpDX.Matrix.Scaling(
+                                       x: パネルサイズdpx.画像領域dpx.Width / this.Nullパネルの画像.サイズdpx.Width, // ノード画像を設計値サイズに拡大縮小する。
+                                       y: パネルサイズdpx.画像領域dpx.Height / this.Nullパネルの画像.サイズdpx.Height,
+                                       z: 1.0f );
+
+                               // カーソル位置なら拡大する。
+                               if( これはカーソル位置のパネルである )
+                                       変換行列3Dpx *= SharpDX.Matrix.Scaling( カーソル位置のパネルの拡大率 );
+
+                               // 曲面変換。
+                               変換行列3Dpx *= this.横位置割合からパネルの変換行列を算出して返す( dr, 横位置割合, パネル位置.Y );
+
+                               // 描画。
+                               this.Nullパネルの画像.進行描画する( dr, 変換行列2Dpx, 変換行列3Dpx );
+                               //-----------------
+                               #endregion
+                       }
+               }
+
+               private SharpDX.Matrix 横位置割合からパネルの変換行列を算出して返す( デバイスリソース dr, float 横位置割合, int Y位置0to2 )
+               {
+                       // 原点が画像の左上隅から画像の中央へ来るように移動。
+                       var 変換行列3Dpx = SharpDX.Matrix.Translation(
+                               x: -( パネルサイズdpx.全体dpx.Width * dr.拡大率DPXtoPX横方法 / 2.0f ),
+                               y: -( パネルサイズdpx.全体dpx.Height * dr.拡大率DPXtoPX縦方向 / 2.0f ),
+                               z: 0.0f );
+
+                       // 縦方向の移動(上、中、下段のいずれかへ)。
+                       変換行列3Dpx *= SharpDX.Matrix.Translation(
+                               0.0f,
+                               ( ( ( Y位置0to2 - 1 ) * 230.0f ) ) * dr.拡大率DPXtoPX縦方向,    // 1行は 230 dpx
+                               0.0f );
+
+                       // Y軸回転半径分、画面奥に移動。
+                       変換行列3Dpx *= SharpDX.Matrix.Translation(
+                               0.0f,
+                               0.0f,
+                               -1000.0f * dr.拡大率DPXtoPX横方法 );   // 回転半径は 1000 dpx; Direct2D では、画面手前ほどZ軸がプラス。なお、Z 軸だがここでは 横方向 の拡大率を使用。
+
+                       // Y軸回転。
+                       変換行列3Dpx *= SharpDX.Matrix.RotationY(
+                               (float) Math.PI * ( 横位置割合 - 0.5f ) );
+
+                       // 画面に近いので、全体的に画面の奥へ移動。
+                       変換行列3Dpx *= SharpDX.Matrix.Translation(
+                               0.0f,
+                               0.0f,
+                               950.0f * dr.拡大率DPXtoPX横方法 );     // 移動量 950 dpx; なお、Z 軸だがここでは 横方向 の拡大率を使用。
+
+                       // 射影変換。
+                       変換行列3Dpx *= FDK.Utilities.D2DPerspectiveProjection(
+                               dr.設計画面サイズdpx.Height * 1.67f * dr.拡大率DPXtoPX横方法 );        // 深さは約 1800 [px; 縦1080px時]; Direc2D では、パラメータは深さのみ。なお、(同上
+
+                       return 変換行列3Dpx;
+               }
+
+               // 設計定数
+               private struct パネルサイズdpx
+               {
+                       public static readonly SharpDX.Size2F 全体dpx = new SharpDX.Size2F( 314f, 220f );
+                       public static readonly SharpDX.RectangleF 画像領域dpx = new SharpDX.RectangleF( 0f, 0f, 313f, 137f );
+                       public static readonly SharpDX.RectangleF タイトル領域dpx = new SharpDX.RectangleF( 0f, 138f, 313f, 219f );
+               }
+
+               /// <summary>
+               /// 左上隅のパネルを (0,0) とした時の、カーソル位置の座標。
+               /// 負数も可。
+               /// </summary>
+               private SharpDX.Point カーソル位置 = new SharpDX.Point( 4, 1 );
+
+               /// <summary>
+               /// -63~63。パネルの表示位置を、負数は 左 へ、正数は 右 へずらす 。(正負と左右の対応に注意。)
+               /// </summary>
+               /// <remarks>
+               /// ±64 は、パネル1列分を表すサイズ。
+               /// </remarks>
+               private int 曲パネルのY軸回転オフセット = 0;
+
+               private bool 活性化した直後である = false;
+               private FDK.カウンタ.定間隔進行 横スクロール用カウンタ = null;
+               private readonly FDK.メディア.画像 Nullパネルの画像 = new 画像( @"$(Static)\images\選曲パネル.png" );
+       }
+}
diff --git a/StrokeStyleT/ステージ/選曲/選曲ステージ.cs b/StrokeStyleT/ステージ/選曲/選曲ステージ.cs
new file mode 100644 (file)
index 0000000..3431d0a
--- /dev/null
@@ -0,0 +1,127 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using FDK.メディア;
+
+namespace SST.ステージ.選曲
+{
+       class 選曲ステージ : SST.ステージ.ステージ
+       {
+               public enum フェーズ
+               {
+                       初期状態,
+                       表示中,
+                       曲確定,
+                       キャンセル,
+               }
+               public フェーズ 現在のフェーズ { get; protected set; } = フェーズ.初期状態;
+
+               public 選曲ステージ()
+               {
+               }
+               protected override void On活性化( デバイスリソース dr )
+               {
+                       FDK.Log.Info( "選曲ステージを開始します。" );
+
+                       this.ステージ台.活性化する( dr );
+                       this.ドラムセット.活性化する( dr );
+                       this.曲パネルビュー.活性化する( dr );
+                       this.背景動画.活性化する( dr );
+                       this.背景動画.ループ再生する = true;
+                       this.現在のフェーズ = フェーズ.表示中;
+
+                       // 曲ツリーを活性化する。
+                       StrokeStyleT.曲ツリー管理.活性化する( dr );
+               }
+               protected override void On非活性化( デバイスリソース dr )
+               {
+                       FDK.Log.Info( "選曲ステージを終了します。" );
+
+                       this.ステージ台.非活性化する( dr );
+                       this.ドラムセット.非活性化する( dr );
+                       this.曲パネルビュー.非活性化する( dr );
+                       this.背景動画.非活性化する( dr );
+
+                       // 曲ツリーを非活性化する。
+                       StrokeStyleT.曲ツリー管理.非活性化する( dr );
+               }
+               public override void 進行描画する( デバイスリソース dr )
+               {
+                       if( this.現在のフェーズ != フェーズ.表示中 )
+                               return;
+
+                       // 描画。
+                       this.背景動画.進行描画する( dr, new SharpDX.RectangleF( 0f, 0f, dr.設計画面サイズdpx.Width, dr.設計画面サイズdpx.Height ) );
+                       switch( this.現在のフェーズ )
+                       {
+                               case フェーズ.表示中:
+                                       this.現在のフェーズ = フェーズ.表示中;
+                                       this.曲パネルビュー.進行描画する( dr );
+                                       this.ステージ台.進行描画する( dr, 0.0f, 0.0f );
+                                       this.ドラムセット.進行描画する( dr );
+                                       break;
+                       }
+
+                       // 入力。
+
+                       StrokeStyleT.すべての入力デバイスをポーリングする();
+
+                       // Enter 押下
+                       if( StrokeStyleT.キーボード入力.キーが押された( SharpDX.DirectInput.Key.Return ) )
+                       {
+                               if( StrokeStyleT.曲ツリー管理.現在選択されているノード is 曲.MusicNode )
+                               {
+                                       FDK.Log.Info( "MusicNode が選択されました。" );
+                                       this.現在のフェーズ = フェーズ.曲確定;
+                               }
+                               else if( StrokeStyleT.曲ツリー管理.現在選択されているノード is 曲.BoxNode )
+                               {
+                                       FDK.Log.Info( "BoxNode が選択されました。" );
+                                       // todo: BOX が選択された場合の実装。
+                               }
+                               else if( StrokeStyleT.曲ツリー管理.現在選択されているノード is 曲.BackNode )
+                               {
+                                       FDK.Log.Info( "BackNode が選択されました。" );
+                                       // todo: 戻る が選択された場合の実装。
+                               }
+                               else if( null == StrokeStyleT.曲ツリー管理.現在選択されているノード )
+                               {
+                                       FDK.Log.Info( "曲リストが空です。何もしません。" );
+                               }
+                               else
+                               {
+                                       Trace.Fail( "確定された曲が、曲でも BOX でも 戻る でも null でもありません。" );   // バグあり。
+                               }
+                       }
+                       // Esc 押下 → 戻る
+                       else if( StrokeStyleT.キーボード入力.キーが押された( SharpDX.DirectInput.Key.Escape ) )
+                       {
+                               this.現在のフェーズ = フェーズ.キャンセル;
+                       }
+
+                       // カーソルキー(キーバインドに寄らず固定)→ 選曲カーソル移動
+                       else if( StrokeStyleT.キーボード入力.キーが押された( SharpDX.DirectInput.Key.Left ) )
+                       {
+                               this.曲パネルビュー.カーソルを左に移動する();
+                       }
+                       else if( StrokeStyleT.キーボード入力.キーが押された( SharpDX.DirectInput.Key.Right ) )
+                       {
+                               this.曲パネルビュー.カーソルを右に移動する();
+                       }
+                       else if( StrokeStyleT.キーボード入力.キーが押された( SharpDX.DirectInput.Key.Up ) )
+                       {
+                               this.曲パネルビュー.カーソルを上に移動する();
+                       }
+                       else if( StrokeStyleT.キーボード入力.キーが押された( SharpDX.DirectInput.Key.Down ) )
+                       {
+                               this.曲パネルビュー.カーソルを下に移動する();
+                       }
+               }
+
+               private readonly FDK.メディア.動画 背景動画 = new 動画( @"$(Static)\images\選曲画面BGV.mp4" );
+               private readonly FDK.メディア.画像 ステージ台 = new 画像( @"$(Static)\images\ステージ台.png" );
+               private readonly SST.ステージ.汎用.ドラムセット ドラムセット = new 汎用.ドラムセット();
+               private readonly 曲パネルビュー 曲パネルビュー = new 曲パネルビュー();
+       }
+}
index 6439511..0eb30a6 100644 (file)
@@ -40,10 +40,12 @@ namespace SST.曲
                protected override void On活性化( デバイスリソース dr )
                {
                        this.サムネイル画像?.活性化する( dr );
+                       base.On活性化( dr ); // Node の活性化
                }
                protected override void On非活性化( デバイスリソース dr )
                {
                        this.サムネイル画像?.非活性化する( dr );
+                       base.On非活性化( dr );   // Node の非活性化
                }
                public override void ノード画像を描画する( デバイスリソース dr, SharpDX.Matrix3x2 変換行列2Dpx, SharpDX.Matrix 変換行列3Dpx, float 不透明度0to1 = 1 )
                {
index 4eae430..33b739e 100644 (file)
@@ -74,7 +74,8 @@ namespace SST.曲
                        // まだ既定の曲画像が生成されてなかったら生成する。
                        if( null == Node.既定の曲画像 )
                                Node.既定の曲画像 = new 画像( @"$(Static)\images\選曲パネル.png" );
-                       Node.既定の曲画像.活性化する( dr );
+                       if( Node.既定の曲画像.活性化していない )
+                               Node.既定の曲画像.活性化する( dr );
 
                        // タイトル部の文字画像を作成する。
                        this.タイトル画像 = new 文字列画像( this.タイトル, 20.0f, "メイリオ" ) {