セージ の メモ書き

メモこそ命の恩人だ

ブロック崩し

XAML

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Height="600" Width="1000"
        WindowState="Maximized"
        ContentRendered="Window_ContentRendered" 
        KeyDown="Window_KeyDown" 
        MouseMove="Window_MouseMove">

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="1*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

        <Border Grid.Row="0" BorderBrush="Orange" BorderThickness="3">
            <Grid>
                <Image Source="Back.jpg" Stretch="Fill" />
                <Canvas x:Name="CanvasArea">

                    <Rectangle x:Name="RectangleBar" Width="500" Height="100" Canvas.Left="-100" Canvas.Top="-100">
                        <Rectangle.Fill>
                            <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                <GradientStop Color="LightGray" Offset="0"/>
                                <GradientStop Color="DimGray" Offset="1"/>
                            </LinearGradientBrush>
                        </Rectangle.Fill>
                    </Rectangle>

                    <Ellipse x:Name="EllipseBall" Width="80" Height="80" Canvas.Left="-100" Canvas.Top="-100">
                        <Ellipse.Fill>
                            <RadialGradientBrush>
                                <GradientStop Color="Red" Offset="0.5"/>
                                <GradientStop Color="LightPink" Offset="1"/>
                            </RadialGradientBrush>
                        </Ellipse.Fill>
                    </Ellipse>

                </Canvas>
            </Grid>
        </Border>

        <WrapPanel Grid.Row="1" VerticalAlignment="Center" Margin="15">
            <Button x:Name="ButtonBall" Content="はじめから" Height="50" FontSize="20" Click="ButtonBall_Click" />
            <Label x:Name="LabelScore" VerticalAlignment="Center" FontSize="20">
                <TextBlock>
                    <Run Text=" てんすう:" />
                    <Run x:Name="RunScore" />
                </TextBlock>
            </Label>
            <CheckBox x:Name="CheckBoxEndless" Content="おちない" FontSize="20" Margin="30,0,0,0" />
        </WrapPanel>
        
    </Grid>
</Window>

コードビハインド

public partial class MainWindow : Window
{
    public class Block
    {
        public int Number { get; set; }
        public Image Image { get; set; }
        public Rect Rect { get; set; }
    }
    private List<Block> blockList = new List<Block>();
    private Vector barPosition = new Vector(-100, -100);
    private Vector ballPosition = new Vector(-100, -100);
    private Vector ballSpeed = new Vector(0, 0);
    private double speedMax = 10.0;
    private double speedRate = 1.5;
    private int score = 0;
    private int level = 0;
    private Dictionary<int, BitmapImage> imageList = new Dictionary<int, BitmapImage>();
    private SoundPlayer soundPlayer = new SoundPlayer() { SoundLocation = @"C:\00-Shared\SampleSound.wav" };

    /// <summary>
    /// コンストラクタ
    /// </summary>
    public MainWindow()
    {
        InitializeComponent();

        // 画像データの生成(事前に画像ファイルを用意しておくこと。ファイル名は2桁のゼロパディングのものを用意する。)
        foreach (var number in Enumerable.Range(0, 40))
        {
            imageList[number] = new BitmapImage(new Uri($"Image/{number.ToString("D2")}.JPG", UriKind.Relative));
        }
    }

    /// <summary>
    /// Window->ContentRenderedイベント
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void Window_ContentRendered(object sender, EventArgs e)
    {
        // ブロック生成
        CreateBlockList(count: 48);

        // ゲーム開始
        var timer = new DispatcherTimer() { Interval = new TimeSpan(0, 0, 0, 0, 1) };
        timer.Tick += (senderTick, eTick) =>
        {
            // 領域サイズの取得
            var areaWidth = this.CanvasArea.ActualWidth;
            var areaHeight = this.CanvasArea.ActualHeight;

            // 領域判定(左)
            if (ballPosition.X < 0)
            {
                MoveBall(0, ballPosition.Y);
                ballSpeed.X *= -1;
            }

            // 領域判定(右)
            if (areaWidth < ballPosition.X + this.EllipseBall.ActualWidth)
            {
                MoveBall(areaWidth - this.EllipseBall.ActualWidth, ballPosition.Y);
                ballSpeed.X *= -1;
            }

            // 領域判定(上)
            if (ballPosition.Y < 0)
            {
                MoveBall(ballPosition.X, 0);
                ballSpeed.Y *= -1;
            }

            // 領域判定(下)
            if (areaHeight < ballPosition.Y + this.EllipseBall.ActualHeight)
            {
                if (this.CheckBoxEndless.IsChecked.Value)
                {
                    // エンドレスモード
                    MoveBall(ballPosition.X, areaHeight - this.EllipseBall.ActualHeight);
                    ballSpeed.Y *= -1;
                }
                else
                {
                    // 点数表示
                    if ((ballSpeed.X == 0) && (ballSpeed.Y == 0)) return;
                    ballSpeed.X = 0;
                    ballSpeed.Y = 0;
                    MessageBox.Show($"てんすう:{score} {GetScoreMessage(score)}", "ブロック崩し", MessageBoxButton.OK, MessageBoxImage.Information);
                }
            }

            // バー衝突判定
            var barRect = new Rect(barPosition.X, barPosition.Y, this.RectangleBar.ActualWidth, this.RectangleBar.ActualHeight);
            if (barRect.Contains(ballPosition.X, ballPosition.Y + this.EllipseBall.ActualHeight))
            {
                if (0 < ballSpeed.Y) ballSpeed.Y *= -1;
            }

            // ブロック衝突判定
            foreach (var block in blockList)
            {
                if (block.Rect.Contains(ballPosition.X, ballPosition.Y)) CloseBlock(block.Image);
            }

            // ボール移動
            var movePosition = ballPosition + ballSpeed;
            MoveBall(movePosition.X, movePosition.Y);

            // ブロック再生成(すべて非表示の場合)
            if (!blockList.Any(x => x.Image.Visibility == Visibility.Visible))
            {
                // タイマー停止
                timer.Stop();

                // ボール初期化
                InitializeBall();

                // 最大スピードアップ
                speedMax += 3.0;
                speedRate += 0.1;

                // レベルアップ
                level++;

                // 再表示
                foreach (var block in blockList)
                {
                    block.Image.Visibility = Visibility.Visible;
                    block.Image.Source = GetBitmap(block.Number, level);
                }

                // タイマー開始
                timer.Start();
            }
        };
        timer.Start();
    }

    /// <summary>
    /// ブロックを生成する
    /// </summary>
    /// <param name="count">生成数</param>
    private void CreateBlockList(int count)
    {
        var row = 1;
        var column = 1;
        foreach (var number in Enumerable.Range(1, count))
        {
            // 画像生成
            var image = new Image()
            {
                Name = $"Image{number}",
                Source = GetBitmap(number, level: 0),
                Width = 100,
                Height = 100,
            };
            this.CanvasArea.Children.Add(image);

            // 座標設定
            if (IsOverArea(blockList.LastOrDefault()))
            {
                column = 1;
                row++;
            }
            var positionX = column * image.Width;
            var positionY = row * image.Height;
            column++;

            // 画像配置
            Canvas.SetLeft(image, positionX);
            Canvas.SetTop(image, positionY);

            // ブロック情報の保持
            var rect = new Rect(positionX, positionY, image.Width, image.Height);
            blockList.Add(new Block()
            {
                Number = number,
                Image = image,
                Rect = rect,
            });
        }
    }

    /// <summary>
    /// ブロックが表示領域を超えているか判定する
    /// </summary>
    /// <param name="block">ブロック</param>
    /// <returns>true:超えた、false:超えていない</returns>
    private bool IsOverArea(Block block)
    {
        if (block == null) return false;
        var margin = 100;
        var areaWidth = this.CanvasArea.ActualWidth;
        return (areaWidth < block.Rect.X + block.Rect.Width + margin);
    }

    /// <summary>
    /// ブロックを非表示にする
    /// </summary>
    /// <param name="image">イメージ</param>
    private void CloseBlock(Image image)
    {
        // ブロック非表示
        if (image.Visibility != Visibility.Visible) return;
        image.Visibility = Visibility.Collapsed;

        // サウンド再生
        //SystemSounds.Beep.Play();
        soundPlayer.Play();

        // スコアアップ
        SetScore(score + 1);

        // スピードアップ
        if (speedMax < Math.Abs(ballSpeed.X)) return;
        if (speedMax < Math.Abs(ballSpeed.Y)) return;
        ballSpeed *= speedRate;
    }

    /// <summary>
    /// イメージを取得する
    /// </summary>
    /// <param name="seed">シード</param>
    /// <param name="level">レベル</param>
    /// <returns>イメージ</returns>
    private BitmapImage GetBitmap(int seed, int level)
    {
        // 乱数生成
        var number = 0;
        switch (level)
        {
            case 0:
                // 0~9
                number = new Random(seed).Next(0, 10);
                break;
            case 1:
                // 10~19
                number = new Random(seed).Next(10, 20);
                break;
            case 2:
                // 20~29
                number = new Random(seed).Next(20, 30);
                break;
            case 3:
                // 30~39
                number = new Random(seed).Next(30, 40);
                break;
            default:
                // 0~29
                number = new Random(seed).Next(0, 40);
                break;
        }

        // 画像取得
        return imageList[number];
    }

    /// <summary>
    /// スコアを設定する
    /// </summary>
    /// <param name="value">スコア</param>
    private void SetScore(int value)
    {
        score = value;
        this.RunScore.Text = value.ToString();
    }

    /// <summary>
    /// スコアメッセージを取得する
    /// </summary>
    /// <param name="score">スコア</param>
    /// <returns>メッセージ</returns>
    private string GetScoreMessage(double score)
    {
        if (200 <= score) return "ごめんなさい。もう許してください。(;´Д`)";
        if (180 <= score) return "参りました。ごめんなさい。(;´Д`)";
        if (160 <= score) return "すごい!すごいよ! ( ゚Д゚)";
        if (140 <= score) return "もう少しで 150てん だね。 ( ゚Д゚)";
        if (120 <= score) return "すごい!すごいよ! ( ゚Д゚)";
        if (100 <= score) return "100てん こえたね。 (*^▽^*)";
        if (80 <= score) return "もう少しで 100てん だね。 ('◇')ゞ";
        if (60 <= score) return "すごい、じょうずだね。( `ー´)ノ";
        if (40 <= score) return "じょうずだね。(^^♪";
        if (20 <= score) return "ちょっと、がんばったね。(; ・`д・´)";
        return "がんばりましょう。('ω')ノ";
    }

    /// <summary>
    /// バーを移動する
    /// </summary>
    /// <param name="positionX">X座標</param>
    /// <param name="positionY">Y座標</param>
    private void MoveBar(double positionX, double positionY)
    {
        const double positionMinY = 100;
        if (positionY < positionMinY) positionY = positionMinY;
        Canvas.SetLeft(this.RectangleBar, positionX);
        Canvas.SetTop(this.RectangleBar, positionY);
        barPosition.X = positionX;
        barPosition.Y = positionY;
    }

    /// <summary>
    /// ボールを移動する
    /// </summary>
    /// <param name="positionX">X座標</param>
    /// <param name="positionY">Y座標</param>
    private void MoveBall(double positionX, double positionY)
    {
        Canvas.SetLeft(this.EllipseBall, positionX);
        Canvas.SetTop(this.EllipseBall, positionY);
        ballPosition.X = positionX;
        ballPosition.Y = positionY;
    }

    /// <summary>
    /// ボールを初期化する
    /// </summary>
    private void InitializeBall()
    {
        // ボール速度設定
        ballSpeed.X = 1;
        ballSpeed.Y = 2;

        // ボール移動
        MoveBall(0, 0);
    }

    /// <summary>
    /// Window->MouseMoveイベント
    /// </summary>
    private void Window_MouseMove(object sender, MouseEventArgs e)
    {
        const double offset = -50.0;
        var position = e.GetPosition(this);
        MoveBar(position.X + offset, position.Y + offset);
    }

    /// <summary>
    /// Window->KeyDownイベント
    /// </summary>
    private void Window_KeyDown(object sender, KeyEventArgs e)
    {
        switch (e.Key)
        {
            case Key.Right:
                if (ballSpeed.X < 0) ballSpeed.X *= -1;
                break;
            case Key.Left:
                if (0 < ballSpeed.X) ballSpeed.X *= -1;
                break;
            case Key.Down:
                if (ballSpeed.Y < 0) ballSpeed.Y *= -1;
                break;
            case Key.Up:
                if (0 < ballSpeed.Y) ballSpeed.Y *= -1;
                break;
            default:
                break;
        }
    }

    /// <summary>
    /// ButtonBall->Clickイベント
    /// </summary>
    private void ButtonBall_Click(object sender, RoutedEventArgs e)
    {
        // パラメータ初期化
        level = 0;
        speedMax = 10.0;
        speedRate = 1.5;

        // スコア初期化
        SetScore(0);

        // ブロック表示
        foreach (var block in blockList) block.Image.Visibility = Visibility.Visible;

        // ボール初期化
        InitializeBall();
    }
}

以上