// 進行と描画 public override void 描画する(DeviceContext dc, float x, float y, 成績 現在の成績) { // パラメータアニメが完了してからフルコンボチェック。 if (this._パラメータアニメ.ストーリーボード.Status == StoryboardStatus.Ready && !(this._フルコンボ再生済み)) { if (現在の成績.MaxCombo == 現在の成績.総ノーツ数) { App進行描画.システムサウンド.再生する(システムサウンド種別.フルコンボ); } this._フルコンボ再生済み = true; // 再生してようがしてまいが } グラフィックデバイス.Instance.D2DBatchDraw(dc, () => { var 拡大率 = new Size2F(1.4f, 1.3f); // 画像が小さいので少々拡大。 var 割合表 = 現在の成績.判定toヒット割合; int 合計 = 0; float 基点X = x; x = 基点X + (float)this._パラメータアニメ.X位置オフセット[0].Value; this.パラメータを一行描画する(dc, x, y, 拡大率, 判定種別.PERFECT, 現在の成績.判定toヒット数[判定種別.PERFECT], 割合表[判定種別.PERFECT], (float)this._パラメータアニメ.透明度[0].Value); 合計 += 現在の成績.判定toヒット数[判定種別.PERFECT]; y += _改行幅dpx; x = 基点X + (float)this._パラメータアニメ.X位置オフセット[1].Value; this.パラメータを一行描画する(dc, x, y, 拡大率, 判定種別.GREAT, 現在の成績.判定toヒット数[判定種別.GREAT], 割合表[判定種別.GREAT], (float)this._パラメータアニメ.透明度[1].Value); 合計 += 現在の成績.判定toヒット数[判定種別.GREAT]; y += _改行幅dpx; x = 基点X + (float)this._パラメータアニメ.X位置オフセット[2].Value; this.パラメータを一行描画する(dc, x, y, 拡大率, 判定種別.GOOD, 現在の成績.判定toヒット数[判定種別.GOOD], 割合表[判定種別.GOOD], (float)this._パラメータアニメ.透明度[2].Value); 合計 += 現在の成績.判定toヒット数[判定種別.GOOD]; y += _改行幅dpx; x = 基点X + (float)this._パラメータアニメ.X位置オフセット[3].Value; this.パラメータを一行描画する(dc, x, y, 拡大率, 判定種別.OK, 現在の成績.判定toヒット数[判定種別.OK], 割合表[判定種別.OK], (float)this._パラメータアニメ.透明度[3].Value); 合計 += 現在の成績.判定toヒット数[判定種別.OK]; y += _改行幅dpx; x = 基点X + (float)this._パラメータアニメ.X位置オフセット[4].Value; this.パラメータを一行描画する(dc, x, y, 拡大率, 判定種別.MISS, 現在の成績.判定toヒット数[判定種別.MISS], 割合表[判定種別.MISS], (float)this._パラメータアニメ.透明度[4].Value); 合計 += 現在の成績.判定toヒット数[判定種別.MISS]; y += _改行幅dpx; x = 基点X + (float)this._パラメータアニメ.X位置オフセット[5].Value; var 矩形 = this._判定種別文字の矩形リスト["MaxCombo"]; this._判定種別文字.描画する(dc, x, y, 転送元矩形: 矩形, 透明度0to1: (float)this._パラメータアニメ.透明度[5].Value); x += 矩形.Width + 16f; this.数値を描画する(dc, x, y, 拡大率, 現在の成績.MaxCombo, 4, (float)this._パラメータアニメ.透明度[5].Value); this.数値を描画する(dc, x + _dr * 拡大率.Width, y, 拡大率, (int)Math.Floor(100.0 * 現在の成績.MaxCombo / 合計), 3, (float)this._パラメータアニメ.透明度[5].Value); // 切り捨てでいいやもう this._パラメータ文字.透明度 = (float)this._パラメータアニメ.透明度[5].Value; this._パラメータ文字.描画する(dc, x + _dp * 拡大率.Width, y, "%", 拡大率); }); }
// 生成と終了 public 結果ステージ(成績 result) { using var _ = new LogBlock(Log.現在のメソッド名); this._結果 = result; this._背景 = new 舞台画像(); this._既定のノード画像 = new 画像(@"$(Images)\DefaultPreviewImage.png"); this._現行化前のノード画像 = new 画像(@"$(Images)\PreviewImageWaitForActivation.png"); this._曲名パネル = new 画像(@"$(Images)\ResultStage\ScoreTitlePanel.png"); this._曲名画像 = new 文字列画像D2D() { フォント名 = "HGMaruGothicMPRO", フォントサイズpt = 40f, フォントの太さ = FontWeight.Regular, フォントスタイル = FontStyle.Normal, 描画効果 = 文字列画像D2D.効果.縁取り, 縁のサイズdpx = 6f, 前景色 = Color4.Black, 背景色 = Color4.White, }; this._サブタイトル画像 = new 文字列画像D2D() { フォント名 = "HGMaruGothicMPRO", フォントサイズpt = 25f, フォントの太さ = FontWeight.Regular, フォントスタイル = FontStyle.Normal, 描画効果 = 文字列画像D2D.効果.縁取り, 縁のサイズdpx = 5f, 前景色 = Color4.Black, 背景色 = Color4.White, }; this._演奏パラメータ結果 = new 演奏パラメータ結果(); this._ランク = new ランク(); this._難易度 = new 難易度(); this._曲別SKILL = new 曲別SKILL(); this._達成率 = new 達成率(); this._システム情報 = new システム情報(); this._黒マスクブラシ = new SolidColorBrush(Global.既定のD2D1DeviceContext, new Color4(Color3.Black, 0.75f)); this._プレビュー枠ブラシ = new SolidColorBrush(Global.既定のD2D1DeviceContext, new Color4(0xFF209292)); Global.App.システムサウンド.再生する(システムサウンド種別.ステージクリア); // 成績の反映。 if (!Global.App.ログオン中のユーザ.AutoPlayがすべてONである) //ただし、全AUTOなら行わない。 { this._成績を反映する(); } var 選択曲 = Global.App.演奏スコア; this._曲名画像.表示文字列 = 選択曲.曲名; this._サブタイトル画像.表示文字列 = 選択曲.アーティスト名; this._背景.ぼかしと縮小を適用する(0.0); // 即時適用 this.現在のフェーズ = フェーズ.表示; }
// 進行と描画 /// <param name="全体の中央位置"> /// パネル(dc)の左上を原点とする座標。 /// </param> public void 進行描画する(DeviceContext dc, アニメーション am, Vector2 全体の中央位置, 成績 現在の成績) { // 進行。 if (this._現在表示中のスコア < 現在の成績.Score) { int 増分 = 現在の成績.Score - this._現在表示中のスコア; int 追っかけ分 = Math.Max((int)(増分 * 0.75), 1); // VPS に依存するけどまあいい this._現在表示中のスコア = Math.Min(this._現在表示中のスコア + 追っかけ分, 現在の成績.Score); } int スコア値 = Math.Min(Math.Max(this._現在表示中のスコア, 0), 999999999); // プロパティには制限はないが、表示は999999999(9桁)でカンスト。 string 数字 = スコア値.ToString().PadLeft(9); // 右詰め9桁、余白は ' '。 var 全体のサイズ = new Vector2(62f * 9f, 99f); // 固定とする // 1桁ずつ描画。 var 文字間隔補正 = -10f; var 文字の位置 = new Vector2(-(全体のサイズ.X / 2f), 0f); グラフィックデバイス.Instance.D2DBatchDraw(dc, () => { var pretrans = dc.Transform; dc.PrimitiveBlend = PrimitiveBlend.SourceOver; for (int i = 0; i < 数字.Length; i++) { // 前回の文字と違うなら、桁アニメーション開始。 if (数字[i] != this._前回表示した数字[i]) { this._各桁のアニメ[i].跳ね開始(am, 0.0); } var 転送元矩形 = this._スコア数字の矩形リスト[数字[i].ToString()]; dc.Transform = //Matrix3x2.Scaling( 画像矩形から表示矩形への拡大率 ) * Matrix3x2.Translation(文字の位置.X, 文字の位置.Y + (float)(this._各桁のアニメ[i].Yオフセット?.Value ?? 0.0f)) * //Matrix3x2.Scaling( 全体の拡大率.X, 全体の拡大率.Y, center: new Vector2( 0f, 全体のサイズ.Y / 2f ) ) * Matrix3x2.Translation(全体の中央位置) * pretrans; dc.DrawBitmap(this._スコア数字画像.Bitmap, 1f, BitmapInterpolationMode.Linear, 転送元矩形); 文字の位置.X += (転送元矩形.Width + 文字間隔補正) * 1f; // 画像矩形から表示矩形への拡大率.X; } }); // 更新。 this._前回表示したスコア = this._現在表示中のスコア; this._前回表示した数字 = 数字; }
// 進行と描画 /// <param name="全体の中央位置"> /// パネル(dc)の左上を原点とする座標。 /// </param> public void 進行描画する(DeviceContext d2ddc, Animation am, Vector2 全体の中央位置, 成績 現在の成績) { // 進行。 if (this._現在表示中のスコア < 現在の成績.Score) { int 増分 = 現在の成績.Score - this._現在表示中のスコア; int 追っかけ分 = Math.Max((int)(増分 * 0.75), 1); // VPS に依存するけどまあいい this._現在表示中のスコア = Math.Min(this._現在表示中のスコア + 追っかけ分, 現在の成績.Score); } int スコア値 = Math.Clamp(this._現在表示中のスコア, min: 0, max: 999999999); // プロパティには制限はないが、表示は999999999(9桁)でカンスト。 string 数字 = スコア値.ToString().PadLeft(9); // 右詰め9桁、余白は ' '。 var 全体のサイズ = new Vector2(62f * 9f, 99f); // 固定とする // 1桁ずつ描画。 var 文字間隔補正 = -10f; var 文字の位置 = new Vector2(-(全体のサイズ.X / 2f), 0f); var preTrans = d2ddc.Transform; for (int i = 0; i < 数字.Length; i++) { // 前回の文字と違うなら、桁アニメーション開始。 if (数字[i] != this._前回表示した数字[i]) { this._各桁のアニメ[i].跳ね開始(am, 0.0); } var 転送元矩形 = this._スコア数字の矩形リスト[数字[i].ToString()] !; d2ddc.Transform = Matrix3x2.Translation(文字の位置.X, 文字の位置.Y + (float)(this._各桁のアニメ[i].Yオフセット?.Value ?? 0.0f)) * Matrix3x2.Translation(全体の中央位置) * preTrans; // todo: フォント画像D2D に置き換える? d2ddc.DrawBitmap(this._スコア数字画像.Bitmap, 1f, BitmapInterpolationMode.Linear, 転送元矩形.Value); 文字の位置.X += (転送元矩形.Value.Width + 文字間隔補正) * 1f; // 画像矩形から表示矩形への拡大率.X; } d2ddc.Transform = preTrans; // 更新。 this._前回表示した数字 = 数字; }
// 進行と描画 public virtual void 進行描画する(DeviceContext dc, float x, float y, 成績 現在の成績) { var scaling = new Size2F(1.0f, 1.4f); var 判定別ヒット数 = 現在の成績.判定別ヒット数; var 割合表 = 現在の成績.判定別ヒット割合; int MaxCombo = 現在の成績.MaxCombo; int 合計 = 0; this.パラメータを一行描画する(dc, x, y, scaling, 判定種別.PERFECT, 判定別ヒット数[判定種別.PERFECT], 割合表[判定種別.PERFECT]); 合計 += 判定別ヒット数[判定種別.PERFECT]; y += _改行幅dpx; this.パラメータを一行描画する(dc, x, y, scaling, 判定種別.GREAT, 判定別ヒット数[判定種別.GREAT], 割合表[判定種別.GREAT]); 合計 += 判定別ヒット数[判定種別.GREAT]; y += _改行幅dpx; this.パラメータを一行描画する(dc, x, y, scaling, 判定種別.GOOD, 判定別ヒット数[判定種別.GOOD], 割合表[判定種別.GOOD]); 合計 += 判定別ヒット数[判定種別.GOOD]; y += _改行幅dpx; this.パラメータを一行描画する(dc, x, y, scaling, 判定種別.OK, 判定別ヒット数[判定種別.OK], 割合表[判定種別.OK]); 合計 += 判定別ヒット数[判定種別.OK]; y += _改行幅dpx; this.パラメータを一行描画する(dc, x, y, scaling, 判定種別.MISS, 判定別ヒット数[判定種別.MISS], 割合表[判定種別.MISS]); 合計 += 判定別ヒット数[判定種別.MISS]; y += _改行幅dpx; y += 3f; // ちょっと間を開けて var 矩形 = this.判定種別文字の矩形リスト["MaxCombo"] !; this._判定種別文字.描画する(dc, x, y, 転送元矩形: 矩形, X方向拡大率: scaling.Width, Y方向拡大率: scaling.Height); x += 矩形.Value.Width + 16f; this.数値を描画する(dc, x, y, scaling, MaxCombo, 桁数: 4); this.数値を描画する(dc, x + _dr, y, scaling, (int)Math.Floor(100.0 * MaxCombo / 合計), 桁数: 3); // 切り捨てでいいやもう this.パラメータ文字.描画する(dc, x + _dp, y, "%", scaling); }
public void 描画する(グラフィックデバイス gd, float x, float y, 成績 結果) { gd.D2DBatchDraw((dc) => { dc.Transform = Matrix3x2.Scaling(1.4f, 1.3f, center: new Vector2(x, y)) * // 画像が小さいので少々拡大。 dc.Transform; var 割合表 = 結果.判定toヒット割合; int 合計 = 0; float 基点X = x; x = 基点X + (float)this._パラメータアニメ.X位置オフセット[0].Value; this.パラメータを一行描画する(dc, x, y, 判定種別.PERFECT, 結果.判定toヒット数[判定種別.PERFECT], 割合表[判定種別.PERFECT], (float)this._パラメータアニメ.透明度[0].Value); 合計 += 結果.判定toヒット数[判定種別.PERFECT]; y += _改行幅dpx; x = 基点X + (float)this._パラメータアニメ.X位置オフセット[1].Value; this.パラメータを一行描画する(dc, x, y, 判定種別.GREAT, 結果.判定toヒット数[判定種別.GREAT], 割合表[判定種別.GREAT], (float)this._パラメータアニメ.透明度[1].Value); 合計 += 結果.判定toヒット数[判定種別.GREAT]; y += _改行幅dpx; x = 基点X + (float)this._パラメータアニメ.X位置オフセット[2].Value; this.パラメータを一行描画する(dc, x, y, 判定種別.GOOD, 結果.判定toヒット数[判定種別.GOOD], 割合表[判定種別.GOOD], (float)this._パラメータアニメ.透明度[2].Value); 合計 += 結果.判定toヒット数[判定種別.GOOD]; y += _改行幅dpx; x = 基点X + (float)this._パラメータアニメ.X位置オフセット[3].Value; this.パラメータを一行描画する(dc, x, y, 判定種別.OK, 結果.判定toヒット数[判定種別.OK], 割合表[判定種別.OK], (float)this._パラメータアニメ.透明度[3].Value); 合計 += 結果.判定toヒット数[判定種別.OK]; y += _改行幅dpx; x = 基点X + (float)this._パラメータアニメ.X位置オフセット[4].Value; this.パラメータを一行描画する(dc, x, y, 判定種別.MISS, 結果.判定toヒット数[判定種別.MISS], 割合表[判定種別.MISS], (float)this._パラメータアニメ.透明度[4].Value); 合計 += 結果.判定toヒット数[判定種別.MISS]; y += _改行幅dpx; x = 基点X + (float)this._パラメータアニメ.X位置オフセット[5].Value; var 矩形 = (RectangleF)this._判定種別文字の矩形リスト["MaxCombo"]; this._判定種別文字.描画する(dc, x, y, 転送元矩形: 矩形, 透明度0to1: (float)this._パラメータアニメ.透明度[5].Value); x += 矩形.Width + 16f; this.数値を描画する(dc, x, y, 結果.MaxCombo, 4, (float)this._パラメータアニメ.透明度[5].Value); this.数値を描画する(dc, x + _dr, y, (int)Math.Floor(100.0 * 結果.MaxCombo / 合計), 3, (float)this._パラメータアニメ.透明度[5].Value); // 切り捨てでいいやもう this._パラメータ文字.透明度 = (float)this._パラメータアニメ.透明度[5].Value; this._パラメータ文字.描画する(dc, x + _dp, y, "%"); }); }
/// <summary> /// 指定したユーザID&曲ファイルハッシュに対応するレコードがデータベースになければレコードを追加し、 /// あればそのレコードを(最高記録であれば)更新する。 /// </summary> public static void 成績を追加または更新する(成績 今回の成績, string ユーザID, string 曲ファイルハッシュ) { using (var userdb = new UserDB()) { var record = userdb.Records.Where((r) => (r.UserId == ユーザID && r.SongHashId == 曲ファイルハッシュ)).SingleOrDefault(); if (null == record) { // (A) レコードが存在しないので、追加する。 userdb.Records.InsertOnSubmit(new Record() { UserId = ユーザID, SongHashId = 曲ファイルハッシュ, Score = 今回の成績.Score, // todo: CountMap を成績クラスに保存する。 CountMap = "", Skill = 今回の成績.Skill, Achievement = 今回の成績.Achievement, }); } else { // (B) レコードがすでに存在するので、更新する。(記録更新したレコードのみ) if (record.Score < 今回の成績.Score) { record.Score = 今回の成績.Score; } // todo: CountMap を成績クラスに保存する。 if (record.Skill < 今回の成績.Skill) { record.Skill = 今回の成績.Skill; } if (record.Achievement < 今回の成績.Achievement) { record.Achievement = 今回の成績.Achievement; } } userdb.DataContext.SubmitChanges(); } }
public void 判定toヒット数割合Test() { #region " 実サンプル1 " //---------------- { var result = new 成績(); result.ヒット数を加算する(判定種別.PERFECT, 48); result.ヒット数を加算する(判定種別.GREAT, 1); result.ヒット数を加算する(判定種別.GOOD, 0); result.ヒット数を加算する(判定種別.OK, 0); result.ヒット数を加算する(判定種別.MISS, 0); var dic = result.判定toヒット割合; Assert.AreEqual(97, dic[判定種別.PERFECT]); Assert.AreEqual(3, dic[判定種別.GREAT]); Assert.AreEqual(0, dic[判定種別.GOOD]); Assert.AreEqual(0, dic[判定種別.OK]); Assert.AreEqual(0, dic[判定種別.MISS]); } //---------------- #endregion #region " 実サンプル2 " //---------------- { var result = new 成績(); result.ヒット数を加算する(判定種別.PERFECT, 49); result.ヒット数を加算する(判定種別.GREAT, 1); result.ヒット数を加算する(判定種別.GOOD, 0); result.ヒット数を加算する(判定種別.OK, 0); result.ヒット数を加算する(判定種別.MISS, 0); var dic = result.判定toヒット割合; Assert.AreEqual(98, dic[判定種別.PERFECT]); Assert.AreEqual(2, dic[判定種別.GREAT]); Assert.AreEqual(0, dic[判定種別.GOOD]); Assert.AreEqual(0, dic[判定種別.OK]); Assert.AreEqual(0, dic[判定種別.MISS]); } //---------------- #endregion #region " 実サンプル3 " //---------------- { var result = new 成績(); result.ヒット数を加算する(判定種別.PERFECT, 90); result.ヒット数を加算する(判定種別.GREAT, 1); result.ヒット数を加算する(判定種別.GOOD, 0); result.ヒット数を加算する(判定種別.OK, 0); result.ヒット数を加算する(判定種別.MISS, 0); var dic = result.判定toヒット割合; Assert.AreEqual(98, dic[判定種別.PERFECT]); Assert.AreEqual(2, dic[判定種別.GREAT]); Assert.AreEqual(0, dic[判定種別.GOOD]); Assert.AreEqual(0, dic[判定種別.OK]); Assert.AreEqual(0, dic[判定種別.MISS]); } //---------------- #endregion #region " 実サンプル4 " //---------------- { var result = new 成績(); result.ヒット数を加算する(判定種別.PERFECT, 90); result.ヒット数を加算する(判定種別.GREAT, 2); result.ヒット数を加算する(判定種別.GOOD, 0); result.ヒット数を加算する(判定種別.OK, 0); result.ヒット数を加算する(判定種別.MISS, 0); var dic = result.判定toヒット割合; Assert.AreEqual(97, dic[判定種別.PERFECT]); Assert.AreEqual(3, dic[判定種別.GREAT]); Assert.AreEqual(0, dic[判定種別.GOOD]); Assert.AreEqual(0, dic[判定種別.OK]); Assert.AreEqual(0, dic[判定種別.MISS]); } //---------------- #endregion #region " 実サンプル5 " //---------------- { var result = new 成績(); result.ヒット数を加算する(判定種別.PERFECT, 148); result.ヒット数を加算する(判定種別.GREAT, 2); result.ヒット数を加算する(判定種別.GOOD, 0); result.ヒット数を加算する(判定種別.OK, 0); result.ヒット数を加算する(判定種別.MISS, 1); var dic = result.判定toヒット割合; Assert.AreEqual(98, dic[判定種別.PERFECT]); Assert.AreEqual(1, dic[判定種別.GREAT]); Assert.AreEqual(0, dic[判定種別.GOOD]); Assert.AreEqual(0, dic[判定種別.OK]); Assert.AreEqual(1, dic[判定種別.MISS]); } //---------------- #endregion #region " 実サンプル6 " //---------------- { var result = new 成績(); result.ヒット数を加算する(判定種別.PERFECT, 883); result.ヒット数を加算する(判定種別.GREAT, 19); result.ヒット数を加算する(判定種別.GOOD, 2); result.ヒット数を加算する(判定種別.OK, 2); result.ヒット数を加算する(判定種別.MISS, 1); var dic = result.判定toヒット割合; Assert.AreEqual(97, dic[判定種別.PERFECT]); Assert.AreEqual(2, dic[判定種別.GREAT]); Assert.AreEqual(1, dic[判定種別.GOOD]); Assert.AreEqual(0, dic[判定種別.OK]); Assert.AreEqual(0, dic[判定種別.MISS]); } //---------------- #endregion #region " 実サンプル7 " //---------------- { var result = new 成績(); result.ヒット数を加算する(判定種別.PERFECT, 1397); result.ヒット数を加算する(判定種別.GREAT, 36); result.ヒット数を加算する(判定種別.GOOD, 1); result.ヒット数を加算する(判定種別.OK, 1); result.ヒット数を加算する(判定種別.MISS, 2); var dic = result.判定toヒット割合; Assert.AreEqual(97, dic[判定種別.PERFECT]); Assert.AreEqual(2, dic[判定種別.GREAT]); Assert.AreEqual(0, dic[判定種別.GOOD]); Assert.AreEqual(0, dic[判定種別.OK]); Assert.AreEqual(1, dic[判定種別.MISS]); } //---------------- #endregion #region " 実サンプル8 " //---------------- { var result = new 成績(); result.ヒット数を加算する(判定種別.PERFECT, 1397); result.ヒット数を加算する(判定種別.GREAT, 36); result.ヒット数を加算する(判定種別.GOOD, 1); result.ヒット数を加算する(判定種別.OK, 1); result.ヒット数を加算する(判定種別.MISS, 2); var dic = result.判定toヒット割合; Assert.AreEqual(97, dic[判定種別.PERFECT]); Assert.AreEqual(2, dic[判定種別.GREAT]); Assert.AreEqual(0, dic[判定種別.GOOD]); Assert.AreEqual(0, dic[判定種別.OK]); Assert.AreEqual(1, dic[判定種別.MISS]); } //---------------- #endregion #region " 実サンプル9 " //---------------- { var result = new 成績(); result.ヒット数を加算する(判定種別.PERFECT, 1055); result.ヒット数を加算する(判定種別.GREAT, 41); result.ヒット数を加算する(判定種別.GOOD, 3); result.ヒット数を加算する(判定種別.OK, 0); result.ヒット数を加算する(判定種別.MISS, 3); var dic = result.判定toヒット割合; Assert.AreEqual(95, dic[判定種別.PERFECT]); Assert.AreEqual(4, dic[判定種別.GREAT]); Assert.AreEqual(1, dic[判定種別.GOOD]); Assert.AreEqual(0, dic[判定種別.OK]); Assert.AreEqual(0, dic[判定種別.MISS]); } //---------------- #endregion #region " 実サンプル10 " //---------------- { var result = new 成績(); result.ヒット数を加算する(判定種別.PERFECT, 403); result.ヒット数を加算する(判定種別.GREAT, 10); result.ヒット数を加算する(判定種別.GOOD, 1); result.ヒット数を加算する(判定種別.OK, 3); result.ヒット数を加算する(判定種別.MISS, 5); var dic = result.判定toヒット割合; Assert.AreEqual(95, dic[判定種別.PERFECT]); Assert.AreEqual(2, dic[判定種別.GREAT]); Assert.AreEqual(1, dic[判定種別.GOOD]); Assert.AreEqual(1, dic[判定種別.OK]); Assert.AreEqual(1, dic[判定種別.MISS]); } //---------------- #endregion }
/// <summary> /// <see cref="AppForm.演奏スコア"/> に対して、ステージを初期化する。 /// </summary> private void _演奏状態を初期化する() { if (null == App進行描画.演奏スコア) { return; } //スコアに依存するデータを初期化する。 this.成績 = new 成績(); this.成績.スコアと設定を反映する(App進行描画.演奏スコア, App進行描画.ユーザ管理.ログオン中のユーザ); this._カウントマップライン?.Dispose(); this._カウントマップライン = new カウントマップライン(); this._描画開始チップ番号 = -1; this._チップの演奏状態 = new Dictionary <チップ, チップの演奏状態>(); foreach (var chip in App進行描画.演奏スコア.チップリスト) { this._チップの演奏状態.Add(chip, new チップの演奏状態(chip)); } this._スコア指定の背景画像 = (App進行描画.演奏スコア.背景画像ファイル名.Nullまたは空である()) ? null : new 画像(Path.Combine(App進行描画.演奏スコア.PATH_WAV, App進行描画.演奏スコア.背景画像ファイル名)); // WAVを生成する。 App進行描画.WAVキャッシュレンタル.世代を進める(); App進行描画.WAV管理?.Dispose(); App進行描画.WAV管理 = new WAV管理(); foreach (var kvp in App進行描画.演奏スコア.WAVリスト) { var wavInfo = kvp.Value; var path = Path.Combine(App進行描画.演奏スコア.PATH_WAV, wavInfo.ファイルパス); App進行描画.WAV管理.登録する(kvp.Key, path, wavInfo.多重再生する, wavInfo.BGMである); } // AVIを生成する。 App進行描画.AVI管理?.Dispose(); App進行描画.AVI管理 = null; if (App進行描画.ユーザ管理.ログオン中のユーザ.演奏中に動画を表示する) { App進行描画.AVI管理 = new AVI管理(); foreach (var kvp in App進行描画.演奏スコア.AVIリスト) { var path = Path.Combine(App進行描画.演奏スコア.PATH_WAV, kvp.Value); App進行描画.AVI管理.登録する(kvp.Key, path, App進行描画.ユーザ管理.ログオン中のユーザ.再生速度); } } // その他。 this._一時停止中 = false; }
// 進行と描画 /// <param name="全体の中央位置"> /// パネル(dc)の左上を原点とする座標。 /// </param> public void 進行描画する(DeviceContext dc, Animation am, Vector2 全体の中央位置, 成績 現在の成績) { int Combo値 = Math.Min(Math.Max(現在の成績.Combo, 0), 9999); // 表示は9999でカンスト。 if (Combo値 < 10) { return; // 10未満は表示しない。 } // 進行。 if ((this._前回表示した値 % 100) > (Combo値 % 100)) { // 100を超えるたびアニメ開始。 this._百ごとのアニメ.開始(am); } var 数字 = Combo値.ToString().PadLeft(4).Replace(' ', 'o'); // 右詰め4桁、余白は 'o'。 var 画像矩形から表示矩形への拡大率 = new Vector2(264f / (142f * 4f), 140f / 188f); var 文字間隔補正 = -10f; var 全体の拡大率 = new Vector2((float)(this._百ごとのアニメ.拡大率?.Value ?? 1.0)); // 全体のサイズを算出。 var 全体のサイズ = new Vector2(0f, 0f); for (int i = 0; i < 数字.Length; i++) { var 矩形 = this._コンボ文字の矩形リスト[数字[i].ToString()]; 全体のサイズ.X += 矩形.Width + 文字間隔補正; // 合計 全体のサイズ.Y = Math.Max(全体のサイズ.Y, 矩形.Height); // 最大値 } 全体のサイズ = 全体のサイズ * 画像矩形から表示矩形への拡大率; // 全体の位置を修正。 全体の中央位置.Y -= 全体のサイズ.Y / 2f; var 振動幅 = (float)(this._百ごとのアニメ.振動幅?.Value ?? 0.0f); if (0.0f < 振動幅) { 全体の中央位置.X += App進行描画.乱数.NextFloat(-振動幅, +振動幅); 全体の中央位置.Y += App進行描画.乱数.NextFloat(-振動幅, +振動幅); } // 1桁ずつ描画。 DXResources.Instance.D2DBatchDraw(dc, () => { var pretrans = dc.Transform; dc.PrimitiveBlend = PrimitiveBlend.SourceOver; #region " 数字を描画。" //---------------- { var 文字の位置 = new Vector2(-(全体のサイズ.X / 2f), 0f); for (int i = 0; i < 数字.Length; i++) { if (数字[i] != this._前回表示した数字[i]) { // 桁アニメーション開始 this._各桁のアニメ[i].落下開始(am); // 1の位以外は、自分より上位の桁を順番に跳ねさせる。 if (3 > i) { for (int p = (i - 1); p >= 0; p--) { this._各桁のアニメ[p].跳ね開始(am, 0.05 * ((i - 1) - p + 1)); } } } var 転送元矩形 = this._コンボ文字の矩形リスト[数字[i].ToString()]; dc.Transform = Matrix3x2.Scaling(画像矩形から表示矩形への拡大率) * Matrix3x2.Translation(文字の位置.X, 文字の位置.Y + (float)(this._各桁のアニメ[i].Yオフセット?.Value ?? 0.0f)) * Matrix3x2.Scaling(全体の拡大率.X, 全体の拡大率.Y, center: new Vector2(0f, 全体のサイズ.Y / 2f)) * Matrix3x2.Translation(全体の中央位置) * pretrans; dc.DrawBitmap(this._コンボ文字画像.Bitmap, (float)(this._各桁のアニメ[i].透明度?.Value ?? 1.0f), BitmapInterpolationMode.Linear, 転送元矩形); 文字の位置.X += (転送元矩形.Width + 文字間隔補正) * 画像矩形から表示矩形への拡大率.X; } } //---------------- #endregion #region " Combo を描画。" //---------------- { var 転送元矩形 = this._コンボ文字の矩形リスト["Combo"]; var 文字の位置 = new Vector2(0f, 130f); dc.Transform = Matrix3x2.Scaling(画像矩形から表示矩形への拡大率) * Matrix3x2.Translation(文字の位置) * Matrix3x2.Scaling(全体の拡大率) * Matrix3x2.Translation(全体の中央位置) * pretrans; dc.DrawBitmap(this._コンボ文字画像.Bitmap, 1.0f, BitmapInterpolationMode.Linear, 転送元矩形); } //---------------- #endregion }); // 保存 this._前回表示した値 = 現在の成績.Combo; this._前回表示した数字 = 数字; }
public void 判定toヒット数割合Test() { // このテストがすべて合格するような計算式を確立する。 #region " 実サンプル1 " //---------------- { var result = new 成績(); result.ヒット数を加算する(判定種別.PERFECT, false, 48); result.ヒット数を加算する(判定種別.GREAT, false, 1); result.ヒット数を加算する(判定種別.GOOD, false, 0); result.ヒット数を加算する(判定種別.OK, false, 0); result.ヒット数を加算する(判定種別.MISS, false, 0); var dic = result.判定toヒット割合; Assert.AreEqual(97, dic[判定種別.PERFECT]); Assert.AreEqual(3, dic[判定種別.GREAT]); Assert.AreEqual(0, dic[判定種別.GOOD]); Assert.AreEqual(0, dic[判定種別.OK]); Assert.AreEqual(0, dic[判定種別.MISS]); } //---------------- #endregion #region " 実サンプル2 " //---------------- { var result = new 成績(); result.ヒット数を加算する(判定種別.PERFECT, false, 49); result.ヒット数を加算する(判定種別.GREAT, false, 1); result.ヒット数を加算する(判定種別.GOOD, false, 0); result.ヒット数を加算する(判定種別.OK, false, 0); result.ヒット数を加算する(判定種別.MISS, false, 0); var dic = result.判定toヒット割合; Assert.AreEqual(98, dic[判定種別.PERFECT]); Assert.AreEqual(2, dic[判定種別.GREAT]); Assert.AreEqual(0, dic[判定種別.GOOD]); Assert.AreEqual(0, dic[判定種別.OK]); Assert.AreEqual(0, dic[判定種別.MISS]); } //---------------- #endregion #region " 実サンプル3 " //---------------- { var result = new 成績(); result.ヒット数を加算する(判定種別.PERFECT, false, 90); result.ヒット数を加算する(判定種別.GREAT, false, 1); result.ヒット数を加算する(判定種別.GOOD, false, 0); result.ヒット数を加算する(判定種別.OK, false, 0); result.ヒット数を加算する(判定種別.MISS, false, 0); var dic = result.判定toヒット割合; Assert.AreEqual(98, dic[判定種別.PERFECT]); Assert.AreEqual(2, dic[判定種別.GREAT]); Assert.AreEqual(0, dic[判定種別.GOOD]); Assert.AreEqual(0, dic[判定種別.OK]); Assert.AreEqual(0, dic[判定種別.MISS]); } //---------------- #endregion #region " 実サンプル4 " //---------------- { var result = new 成績(); result.ヒット数を加算する(判定種別.PERFECT, false, 90); result.ヒット数を加算する(判定種別.GREAT, false, 2); result.ヒット数を加算する(判定種別.GOOD, false, 0); result.ヒット数を加算する(判定種別.OK, false, 0); result.ヒット数を加算する(判定種別.MISS, false, 0); var dic = result.判定toヒット割合; Assert.AreEqual(97, dic[判定種別.PERFECT]); Assert.AreEqual(3, dic[判定種別.GREAT]); Assert.AreEqual(0, dic[判定種別.GOOD]); Assert.AreEqual(0, dic[判定種別.OK]); Assert.AreEqual(0, dic[判定種別.MISS]); } //---------------- #endregion #region " 実サンプル5 " //---------------- { var result = new 成績(); result.ヒット数を加算する(判定種別.PERFECT, false, 148); result.ヒット数を加算する(判定種別.GREAT, false, 2); result.ヒット数を加算する(判定種別.GOOD, false, 0); result.ヒット数を加算する(判定種別.OK, false, 0); result.ヒット数を加算する(判定種別.MISS, false, 1); var dic = result.判定toヒット割合; Assert.AreEqual(98, dic[判定種別.PERFECT]); Assert.AreEqual(1, dic[判定種別.GREAT]); Assert.AreEqual(0, dic[判定種別.GOOD]); Assert.AreEqual(0, dic[判定種別.OK]); Assert.AreEqual(1, dic[判定種別.MISS]); } //---------------- #endregion #region " 実サンプル6 " //---------------- { var result = new 成績(); result.ヒット数を加算する(判定種別.PERFECT, false, 883); result.ヒット数を加算する(判定種別.GREAT, false, 19); result.ヒット数を加算する(判定種別.GOOD, false, 2); result.ヒット数を加算する(判定種別.OK, false, 2); result.ヒット数を加算する(判定種別.MISS, false, 1); var dic = result.判定toヒット割合; Assert.AreEqual(97, dic[判定種別.PERFECT]); Assert.AreEqual(2, dic[判定種別.GREAT]); Assert.AreEqual(1, dic[判定種別.GOOD]); Assert.AreEqual(0, dic[判定種別.OK]); Assert.AreEqual(0, dic[判定種別.MISS]); } //---------------- #endregion #region " 実サンプル7 " //---------------- { var result = new 成績(); result.ヒット数を加算する(判定種別.PERFECT, false, 1397); result.ヒット数を加算する(判定種別.GREAT, false, 36); result.ヒット数を加算する(判定種別.GOOD, false, 1); result.ヒット数を加算する(判定種別.OK, false, 1); result.ヒット数を加算する(判定種別.MISS, false, 2); var dic = result.判定toヒット割合; Assert.AreEqual(97, dic[判定種別.PERFECT]); Assert.AreEqual(2, dic[判定種別.GREAT]); Assert.AreEqual(0, dic[判定種別.GOOD]); Assert.AreEqual(0, dic[判定種別.OK]); Assert.AreEqual(1, dic[判定種別.MISS]); } //---------------- #endregion #region " 実サンプル8 " //---------------- { var result = new 成績(); result.ヒット数を加算する(判定種別.PERFECT, false, 1397); result.ヒット数を加算する(判定種別.GREAT, false, 36); result.ヒット数を加算する(判定種別.GOOD, false, 1); result.ヒット数を加算する(判定種別.OK, false, 1); result.ヒット数を加算する(判定種別.MISS, false, 2); var dic = result.判定toヒット割合; Assert.AreEqual(97, dic[判定種別.PERFECT]); Assert.AreEqual(2, dic[判定種別.GREAT]); Assert.AreEqual(0, dic[判定種別.GOOD]); Assert.AreEqual(0, dic[判定種別.OK]); Assert.AreEqual(1, dic[判定種別.MISS]); } //---------------- #endregion #region " 実サンプル9 " //---------------- { var result = new 成績(); result.ヒット数を加算する(判定種別.PERFECT, false, 1055); result.ヒット数を加算する(判定種別.GREAT, false, 41); result.ヒット数を加算する(判定種別.GOOD, false, 3); result.ヒット数を加算する(判定種別.OK, false, 0); result.ヒット数を加算する(判定種別.MISS, false, 3); var dic = result.判定toヒット割合; Assert.AreEqual(95, dic[判定種別.PERFECT]); Assert.AreEqual(4, dic[判定種別.GREAT]); Assert.AreEqual(1, dic[判定種別.GOOD]); Assert.AreEqual(0, dic[判定種別.OK]); Assert.AreEqual(0, dic[判定種別.MISS]); } //---------------- #endregion #region " 実サンプル10 " //---------------- { var result = new 成績(); result.ヒット数を加算する(判定種別.PERFECT, false, 403); result.ヒット数を加算する(判定種別.GREAT, false, 10); result.ヒット数を加算する(判定種別.GOOD, false, 1); result.ヒット数を加算する(判定種別.OK, false, 3); result.ヒット数を加算する(判定種別.MISS, false, 5); var dic = result.判定toヒット割合; Assert.AreEqual(95, dic[判定種別.PERFECT]); Assert.AreEqual(2, dic[判定種別.GREAT]); Assert.AreEqual(1, dic[判定種別.GOOD]); Assert.AreEqual(1, dic[判定種別.OK]); Assert.AreEqual(1, dic[判定種別.MISS]); } //---------------- #endregion }
// 生成と終了 public 結果ステージ(成績 result) { using var _ = new LogBlock(Log.現在のメソッド名); this._結果 = result; Log.Info($"曲名: {Global.App.演奏譜面.譜面.Title}"); Log.Info($"難易度: {Global.App.演奏譜面.譜面.Level}"); Log.Info($"ヒット数: Pf{this._結果.判定別ヒット数[演奏.判定種別.PERFECT]} / " + $"Gr{this._結果.判定別ヒット数[ 演奏.判定種別.GREAT]} / " + $"Gd{this._結果.判定別ヒット数[ 演奏.判定種別.GOOD]} / " + $"Ok{this._結果.判定別ヒット数[ 演奏.判定種別.OK ]} / " + $"Ms{this._結果.判定別ヒット数[ 演奏.判定種別.MISS]}"); Log.Info($"達成率: {this._結果.Achievement}"); Log.Info($"スキル: {this._結果.スキル}"); Log.Info($"ランク: {this._結果.ランク}"); Global.App.システムサウンド.再生する(システムサウンド種別.ステージクリア); #region " 成績をDBへ反映する。" //---------------- if (result.無効) { // 全Autoまたは無効の場合は反映しない。 this._最高成績である = false; } else { this._最高成績である = this._成績を反映する(); } //---------------- #endregion this._背景 = new 舞台画像(); this._既定のノード画像 = new 画像D2D(@"$(Images)\DefaultPreviewImage.png"); this._現行化前のノード画像 = new 画像D2D(@"$(Images)\PreviewImageWaitForActivation.png"); this._曲名パネル = new 画像D2D(@"$(Images)\ResultStage\ScoreTitlePanel.png"); this._曲名画像 = new 文字列画像D2D() { フォント名 = "HGMaruGothicMPRO", フォントサイズpt = 40f, フォントの太さ = FontWeight.Regular, フォントスタイル = FontStyle.Normal, 描画効果 = 文字列画像D2D.効果.縁取り, 縁のサイズdpx = 6f, 前景色 = Color4.Black, 背景色 = Color4.White, }; this._サブタイトル画像 = new 文字列画像D2D() { フォント名 = "HGMaruGothicMPRO", フォントサイズpt = 25f, フォントの太さ = FontWeight.Regular, フォントスタイル = FontStyle.Normal, 描画効果 = 文字列画像D2D.効果.縁取り, 縁のサイズdpx = 5f, 前景色 = Color4.Black, 背景色 = Color4.White, }; this._演奏パラメータ結果 = new 演奏パラメータ結果(); this._ランク = new ランク(); this._難易度 = new 難易度(); this._曲別SKILL = new 曲別SKILL(); this._達成率 = (this._最高成績である) ? (達成率Base) new 達成率更新() : new 達成率(); this._システム情報 = new システム情報(); this._黒マスクブラシ = new SolidColorBrush(Global.GraphicResources.既定のD2D1DeviceContext, new Color4(Color3.Black, 0.75f)); this._プレビュー枠ブラシ = new SolidColorBrush(Global.GraphicResources.既定のD2D1DeviceContext, new Color4(0xFF209292)); var 選択曲 = Global.App.演奏スコア; this._曲名画像.表示文字列 = 選択曲.曲名; this._サブタイトル画像.表示文字列 = 選択曲.アーティスト名; this._背景.ぼかしと縮小を適用する(0.0); // 即時適用 // 最初のフェーズへ。 this.現在のフェーズ = フェーズ.表示; }
public override void 描画する(DeviceContext1 dc, float x, float y, 成績 結果) { // パラメータアニメが完了してからフルコンボチェック。 if (this._パラメータアニメ.ストーリーボード.Status == StoryboardStatus.Ready && !(this._フルコンボ再生済み)) { if (結果.MaxCombo == 結果.総ノーツ数) { App.システムサウンド.再生する(設定.システムサウンド種別.フルコンボ); } this._フルコンボ再生済み = true; // 再生してようがしてまいが } グラフィックデバイス.Instance.D2DBatchDraw(dc, () => { var pretrans = dc.Transform; dc.Transform = Matrix3x2.Scaling(1.4f, 1.3f, center: new Vector2(x, y)) * // 画像が小さいので少々拡大。 pretrans; var 割合表 = 結果.判定toヒット割合; int 合計 = 0; float 基点X = x; x = 基点X + (float)this._パラメータアニメ.X位置オフセット[0].Value; this.パラメータを一行描画する(dc, x, y, 判定種別.PERFECT, 結果.判定toヒット数[判定種別.PERFECT], 割合表[判定種別.PERFECT], (float)this._パラメータアニメ.透明度[0].Value); 合計 += 結果.判定toヒット数[判定種別.PERFECT]; y += _改行幅dpx; x = 基点X + (float)this._パラメータアニメ.X位置オフセット[1].Value; this.パラメータを一行描画する(dc, x, y, 判定種別.GREAT, 結果.判定toヒット数[判定種別.GREAT], 割合表[判定種別.GREAT], (float)this._パラメータアニメ.透明度[1].Value); 合計 += 結果.判定toヒット数[判定種別.GREAT]; y += _改行幅dpx; x = 基点X + (float)this._パラメータアニメ.X位置オフセット[2].Value; this.パラメータを一行描画する(dc, x, y, 判定種別.GOOD, 結果.判定toヒット数[判定種別.GOOD], 割合表[判定種別.GOOD], (float)this._パラメータアニメ.透明度[2].Value); 合計 += 結果.判定toヒット数[判定種別.GOOD]; y += _改行幅dpx; x = 基点X + (float)this._パラメータアニメ.X位置オフセット[3].Value; this.パラメータを一行描画する(dc, x, y, 判定種別.OK, 結果.判定toヒット数[判定種別.OK], 割合表[判定種別.OK], (float)this._パラメータアニメ.透明度[3].Value); 合計 += 結果.判定toヒット数[判定種別.OK]; y += _改行幅dpx; x = 基点X + (float)this._パラメータアニメ.X位置オフセット[4].Value; this.パラメータを一行描画する(dc, x, y, 判定種別.MISS, 結果.判定toヒット数[判定種別.MISS], 割合表[判定種別.MISS], (float)this._パラメータアニメ.透明度[4].Value); 合計 += 結果.判定toヒット数[判定種別.MISS]; y += _改行幅dpx; x = 基点X + (float)this._パラメータアニメ.X位置オフセット[5].Value; var 矩形 = this._判定種別文字の矩形リスト["MaxCombo"]; this._判定種別文字.描画する(dc, x, y, 転送元矩形: 矩形, 透明度0to1: (float)this._パラメータアニメ.透明度[5].Value); x += 矩形.Width + 16f; this.数値を描画する(dc, x, y, 結果.MaxCombo, 4, (float)this._パラメータアニメ.透明度[5].Value); this.数値を描画する(dc, x + _dr, y, (int)Math.Floor(100.0 * 結果.MaxCombo / 合計), 3, (float)this._パラメータアニメ.透明度[5].Value); // 切り捨てでいいやもう this._パラメータ文字.透明度 = (float)this._パラメータアニメ.透明度[5].Value; this._パラメータ文字.描画する(dc, x + _dp, y, "%"); }); }