C# Winforms Рикошет мяча от блока

Я создаю простую игру используя только C# и Winforms. В игре есть мяч, блоки и другие собираемые мячи (когда игрок их собирает, у него увеличивается количество мячей). Смысл в том, чтобы игрок сперва задал направление мяча так, чтобы мяч попал в блоки, а потом таким образом избавился от всех блоков. У меня не получается реализовать отскок/рикошет мяча от блока.

У меня есть формула с помощью которой я определяю траекторию полета мяча.

private void Background_MouseDown(object sender, MouseEventArgs e)
    {
        if (Если курсор на мяче)
        {
            StartPoint = MousePosition;
            EndPoint = MousePosition;
            isClicked = true;
        }
    }

private void Background_MouseMove(object sender, MouseEventArgs e)
    {
        if (isClicked)
        {
            ControlPaint.DrawReversibleLine(StartPoint, EndPoint, Color.Black);
            EndPoint = MousePosition;
            ControlPaint.DrawReversibleLine(StartPoint, EndPoint, Color.Black);
            trajectory = true;
        }
    }

private void Background_MouseUp(object sender, MouseEventArgs e)
    {
        if (trajectory)
        {
            ControlPaint.DrawReversibleLine(StartPoint, EndPoint, Color.Black);
            EndPoint = e.Location;
            angle = Math.Atan2((Ball.arr[0].rect.X - EndPoint.X), (Ball.arr[0].rect.Y - EndPoint.Y));

            isClicked = false;
            Movement.angle = angle;
            Movement.ClientSize = ClientSize;

            MovingBalls(); // Функция которая запускает движение мячей
        }
    }

Ниже представлен сам код для движения мяча (находится в одном файле).

public void Move()
    {
        while (true)
        {
            if (x - velocityX * Math.Sin(Movement.angle) > Movement.ClientSize.Width - размер мяча || x - velocityX * Math.Sin(Movement.angle) < 0)
            {
                velocityX *= -1;
            }
            if (y - velocityY * Math.Cos(Movement.angle) < 0)
            {
                velocityY *= -1;
            }
            if (y - velocityY * Math.Cos(Movement.angle) >= Movement.defaultPosition.Y)
            {
                return;
            }
            x -= velocityX * Math.Sin(Movement.angle);
            y -= velocityY * Math.Cos(Movement.angle);

            rect.X = (int)x;
            rect.Y = (int)y;

            if (Block.arr != null)
            {
                foreach (Block block in Block.arr)
                {
                    if (rect.IntersectsWith(block.rect))
                    {
                        if (CollisionWithTopOrBottom(block))
                        {
                            velocityY *= -1;
                        }
                        else if (CollisionWithSides(block))
                        {
                            velocityX *= -1;
                        }

                        block.currentHp--;
                        if (block.currentHp == 0)
                        {
                            block.Destroy();
                        }
                    }
                }
            }
        }
    }

public bool CollisionWithTopOrBottom(Block victim)
    {
        bool topBottomIntersection = !((rect.Top + 1 != victim.rect.Bottom) && rect.Bottom - 1 != victim.rect.Top);
        return topBottomIntersection;
    }

public bool CollisionWithSides(Block victim)
    {
        bool sideIntersection = (rect.Bottom >= victim.rect.Top || rect.Top <= victim.rect.Bottom) && (rect.Right > victim.rect.Left || rect.Left < victim.rect.Right);
        return sideIntersection;
    }

Дополнительные сведения:
В каждом блоке написано свое количество жизней (то есть, если написано 5 то игрок должен попасть по блоку 5 раз, чтобы разрушить его).

Скорость полета мяча (то есть velocityX и velocityY) равен одному.

Почему в функции CollisionWithTopOrBottom есть непонятная цифра "+ 1" и "- 1"?
Объясню, когда метод intersectionWith возвращает true (имею в виду, тот случай когда мяч коснулся нижней или верхней части блока), в этот момент координаты мяча и блока отличаются на одну единицу.

Сама проблема:
Представленный код работает когда скорость мяча равен одному, но как только скорость увеличивается мяч уже пролетает сквозь блоков или отскакивает в неправильную сторону. Я попробовал решить проблему с помощью векторов и нормали, но так и не получилось.

Измененный код в методе Move():

if (rect.IntersectsWith(block.rect))
{
    int dot = rect.X * block.rect.X + rect.Y * block.rect.Y;
    Point trajectory = new Point(rect.X - 2 * dot * block.rect.X, rect.Y - 2 * dot * block.rect.Y);

    angle = Math.Atan2((rect.X - trajectory.X), (rect.Y - trajectory.Y));
    x -= velocityX * Math.Sin(angle);
    y -= velocityY * Math.Cos(angle);
    rect.X = (int)x;
    rect.Y = (int)y;

    block.currentHp--;
    if (block.currentHp == 0)
    {
       block.Destroy();
    }
}

Ответы (1 шт):

Автор решения: Andrew_STOP_RU_AGRESSION_IN_UA

Если я правильно понял - то разрабатывается арканоид.

В арканоиде должно быть 2 логики - одна применима на блок (и на стенки уровня), вторая применима на игрока.

  • Блок и стенки уровня отражают мяч с тем же(!!!!) углом с которым мяч летел только в иную сторону:

введите сюда описание изображения

  • А вот с игроком уже интереснее. У игрока угол должен отличатся в зависимости от удаления от центра. Центр должен отбивать мяч ровно вверх. А крайние углы блока должны отбивать на какой-то очень большой максимальный угол:

введите сюда описание изображения

И чем ближе к центру - тем ближе к вертикальнй траэктории должно быть.

Чем ближе к левой стороне - тем более тупой угол должен быть вплоть до, например, 80 градусов относительно вертикали.

Чем ближе к правой стороне - тем более тупой угол в иную сторону.

Кстате, в интернете есть множество реализаций арканоида где ты можешь посмотреть нужные тебе расчеты. Но их уже найдешь сам

→ Ссылка