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 すべての入力デバイスをポーリングする()
{
// その他の static の生成。
StrokeStyleT.bs_ユーザ管理 = new ユーザ.ユーザ管理();
+ StrokeStyleT.bs_曲ツリー管理 = new 曲.曲ツリー管理();
}
public override void 初期化する()
this.起動ステージ = null;
this.タイトルステージ = null;
this.ログインステージ = null;
+ this.選曲ステージ = null;
//----------------
#endregion
#region " MIDI入力 を解放する。"
//-----------------
#endregion
}
-
public override void シーンを描画する()
{
#region " 描画を準備する。"
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;
}
//----------------
protected SST.ステージ.起動.起動ステージ 起動ステージ = new ステージ.起動.起動ステージ();
protected SST.ステージ.タイトル.タイトルステージ タイトルステージ = new ステージ.タイトル.タイトルステージ();
protected SST.ステージ.ログイン.ログインステージ ログインステージ = new ステージ.ログイン.ログインステージ();
+ protected SST.ステージ.選曲.選曲ステージ 選曲ステージ = new ステージ.選曲.選曲ステージ();
private SST.ステージ.ステージ 現在のステージ = null;
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
<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" />
--- /dev/null
+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
+ }
+}
--- /dev/null
+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" );
+ }
+}
--- /dev/null
+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 曲パネルビュー();
+ }
+}
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 )
{
// まだ既定の曲画像が生成されてなかったら生成する。
if( null == Node.既定の曲画像 )
Node.既定の曲画像 = new 画像( @"$(Static)\images\選曲パネル.png" );
- Node.既定の曲画像.活性化する( dr );
+ if( Node.既定の曲画像.活性化していない )
+ Node.既定の曲画像.活性化する( dr );
// タイトル部の文字画像を作成する。
this.タイトル画像 = new 文字列画像( this.タイトル, 20.0f, "メイリオ" ) {