プログラミング演習Ⅲ(2023)

【WPF練習14】壁崩し(6/9)

プロジェクトタイプC# WPFアプリケーション※
プロジェクト名T10b
ソリューション名PET10
ターゲットフレームワーク.NET 6.0 (長期的なサポート)

※ 「WPFアプリ(.NET Framework)」ではないので注意せよ!

注意
  • 本ページの作業内容は 前のページまでの続き になっていることに注意せよ.
    • 先に前のページまでをすべて読み,指示されている作業を済ませてから本ページを読むこと.
    • プロジェクトの作成作業については準備を参照せよ.

14-6. デバッグ用のチートコマンドの作成

終了処理を作成して試すためには,ボールをわざと取りこぼして画面の下に落としたり, ブロックをすべて破壊する必要がある.前者に関しては簡単に試すことができるが, 後者は難しい.また後述するが現在のボールの挙動の実装では,ボールがいつまで経っても すべてのブロックを破壊できない場合がある.

このためブロックをすべて破壊するためのデバッグ用のチートコマンドを実装することにしよう. 今回はキーボードのコントロールキーが押されている場合に,ボールが手近なブロックにホーミングするようにしてみる. Timer_Tick()メソッドに_に示す内容を追記しよう.

MainWindow.xaml.csの追記内容
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
private void Timer_Tick(object sender, EventArgs e)
{
    const double EPSILON = 1 / 256.0;

    double ballX = Canvas.GetLeft(ball) + ball.Width / 2;
    double ballY = Canvas.GetTop(ball) + ball.Height / 2;

    // デバッグ用のチートコマンド    
    if (Keyboard.Modifiers.HasFlag(ModifierKeys.Control)) // コントロールキーが押されている場合
    {
        // canavs1 に含まれている四角形のうちブロック役のものを取り出す.
        var bricks = from r in GetRectangles()
                        where (string)r.Tag == "Bricks"
                        select r;

        Rectangle nearestBrick = null;                 // 最も近いブロックを格納するための変数
        double nearestDist = double.PositiveInfinity;  // そのブロックまでの距離
        Vector targetDir;                              // そのブロックへの方向ベクトル

        var ballPos = new Vector(ballX, ballY);            // 現在のボールの位置

        // ボールに最も近いブロックを検索する.
        foreach (var brick in bricks)
        {
            // ブロックの中心位置を計算する
            var brickCenter = new Vector(
                Canvas.GetLeft(brick) + brick.Width / 2,
                Canvas.GetTop(brick) + brick.Height / 2
            );
                
            var dir = brickCenter - ballPos; // ボールの位置からブロックへ向かうベクトル           
            
            if (dir.Length < nearestDist) // より近い(≒短い)ベクトルが見つかった場合
            {
                // 最も近いブロックなどの変数を更新する.              
                nearestBrick = brick;
                nearestDist = dir.Length;
                targetDir = dir;
            }// if
        }// foreach
        
        if (nearestBrick != null) // 最も近いブロックが見つかった場合
        {
            targetDir.Normalize();
            targetDir *= 10;

            speedX = targetDir.X; // 速度ベクトルを見つかったブロックへの
            speedY = targetDir.Y; // 方向ベクトルに強制的に変更する.
        }// if
    }//if

    double nextBallX = ballX + speedX;
    double nextBallY = ballY + speedY;

    if (nextBallX < 0 || canvas1.Width <= nextBallX) 
    {
        speedX *= -1;
        ballX = Math.Clamp(nextBallX, 0, canvas1.Width) + EPSILON * speedX;
        nextBallX = ballX + speedX;
    }// if
    
    // (..以下略..)

ここまで書けたら起動してみよう.コントロールキーを押しっぱなしにしておくと, _に示すようにボールが自動的にすべてのブロックを破壊してくれるはずである. このチートコマンドを用意したうえで次節でゲームの処理を実装することにしよう.

作業結果
Last updated on 2024-01-09
Published on 2024-01-09

Powered by Hugo. Theme by TechDoc. Designed by Thingsym.