Skip to content

Instantly share code, notes, and snippets.

@MzHmO
Last active November 5, 2023 15:05
Show Gist options
  • Save MzHmO/a40214398fd78456422bf161c4c5229a to your computer and use it in GitHub Desktop.
Save MzHmO/a40214398fd78456422bf161c4c5229a to your computer and use it in GitHub Desktop.

Это все содержимое файла WorldModel.cs

Первое задание

  1. Сделайте так, чтобы шар двигался с постоянной скоростью, пока не долетает до края окна. Скорость должно быть возможно задавать на старте (в коде), в том числе горизонтальную составляющую.
using System;

namespace GravityBalls;

public class WorldModel
{
    public double BallX;
    public double BallY;
    public double BallRadius;
    public double WorldWidth;
    public double WorldHeight;
    public double SpeedX = 80;
    public double SpeedY = 80;

    public void SimulateTimeframe(double elapsedTime)
    {
        BallY = Math.Min(BallY + SpeedY * elapsedTime, WorldHeight - BallRadius);
        BallX = Math.Min(BallX + SpeedX * elapsedTime, WorldWidth - BallRadius);
    }
}

Второе задание

  1. Шар должен отскакивал от краёв окна по законам идеального геометрического бильярда.
using System;

namespace GravityBalls
{
    public class WorldModel
    {
        public double BallX;
        public double BallY;
        public double BallRadius;
        public double WorldWidth;
        public double WorldHeight;
        public double SpeedX = 80;
        public double SpeedY = 80;

        public void SimulateTimeframe(double elapsedTime)
        {
            BallY = Math.Min(BallY + SpeedY * elapsedTime, WorldHeight - BallRadius);
            BallX = Math.Min(BallX + SpeedX * elapsedTime, WorldWidth - BallRadius);
            if (BallX <= BallRadius || BallX >= (WorldWidth - BallRadius))
            {
                SpeedX = -SpeedX;
            }

            if (BallY <= BallRadius || BallY >= (WorldHeight - BallRadius))
            {
                SpeedY = -SpeedY;
            }
        }
    }
}

Третье задание

  1. На шар действует сила сопротивления, пропорциональная модулю скорости и направленная против скорости.
using System;

namespace GravityBalls
{
    public class WorldModel
    {
        public double BallX;
        public double BallY;
        public double BallRadius;
        public double WorldWidth;
        public double WorldHeight;
        public double SpeedX = 200;
        public double SpeedY = 200;
        public double ResistenceCoefficient = 1000;
        public void SimulateTimeframe(double elapsedTime)
        {
            SpeedY += ResistenceCoefficient * elapsedTime;
            //чтобы сила действовала и по оХ
            //speedX += resistenceCoefficient * elapsedTime;
            BallY = Math.Min(BallY + SpeedY * elapsedTime, WorldHeight - BallRadius);
            BallX = Math.Min(BallX + SpeedX * elapsedTime, WorldWidth - BallRadius);
            if (BallX <= BallRadius || BallX >= (WorldWidth - BallRadius))
            {
                SpeedX = -SpeedX;
            }

            if (BallY <= BallRadius || BallY >= (WorldHeight - BallRadius))
            {
                SpeedY = -SpeedY;
            }
        }
    }
}

Четвертое задание

  1. Наведите порядок в коде. Сделайте код красивым, понятным и лаконичным. После вашего рефакторинга, новые возможности должно быть возможно добавлять в код, не переусложняя его. Старайтесь, чтобы ни один метод не был длиннее 10 строк кода.
using System;

namespace GravityBalls
{
    public class WorldModel
    {
        public double BallX;
        public double BallY;
        public double BallRadius;
        public double WorldWidth;
        public double WorldHeight;
        public double SpeedX = 200;
        public double SpeedY = 200;
        public double ResistanceCoefficient = 1000;
        public void SimulateTimeframe(double elapsedTime)
        {
            ApplyResistY(elapsedTime);
            ApplyResistX(elapsedTime);
            SetBallPositions(elapsedTime); 
            UpdateSpeedWithWallCollision();
        }

        private void ApplyResistY(double elapsedTime)
        {
            SpeedY += ResistanceCoefficient * elapsedTime;
        }

        private void ApplyResistX(double elapsedTime)
        {
            SpeedX += ResistanceCoefficient * elapsedTime;
        }

        private void SetBallPositions(double elapsedTime)
        {
            BallY = Math.Min(BallY + SpeedY * elapsedTime, WorldHeight - BallRadius);
            BallX = Math.Min(BallX + SpeedX * elapsedTime, WorldWidth - BallRadius);
        }

        private void UpdateSpeedWithWallCollision()
        {
            if (IsBallTouchingHorizontalWalls())
            {
                SpeedX = -SpeedX;
            }

            if (IsBallTouchingVerticalWalls())
            {
                SpeedY = -SpeedY;
            }

        }

        private bool IsBallTouchingHorizontalWalls()
        {
            return BallX <= BallRadius || BallX >= (WorldWidth - BallRadius);
        }

        private bool IsBallTouchingVerticalWalls()
        {
            return BallY <= BallRadius || BallY >= (WorldHeight - BallRadius);
        }

        private bool IsBallAlmostStopped()
        {
            return Math.Abs(BallY - WorldHeight) < 11;
        }

        private bool AreSpeedsNegative()
        {
            return SpeedY < 0 && SpeedX < 0;
        }
    }
}

Пятое задание

  1. На шар действует ещё одна сила — сила притяжения, константная по модулю и направленная вниз.
using System;

namespace GravityBalls
{
    public class WorldModel
    {
        public double BallX;
        public double BallY;
        public double BallRadius;
        public double WorldWidth;
        public double WorldHeight;
        public double SpeedX = 200;
        public double SpeedY = 200;
        public double ResistanceCoefficient = 1000;
        public double g = 9.8;
        public void SimulateTimeframe(double elapsedTime)
        {
            ApplyGravity();
            ApplyResistY(elapsedTime);
            //ApplyResistX(elapsedTime);
            SetBallPositions(elapsedTime);
            UpdateSpeedWithWallCollision();
        }

        private void ApplyGravity()
        {
            SpeedY += g;
        }
        private void ApplyResistY(double elapsedTime)
        {
            SpeedY += ResistanceCoefficient * elapsedTime;
        }

        private void ApplyResistX(double elapsedTime)
        {
            SpeedX += ResistanceCoefficient * elapsedTime;
        }

        private void SetBallPositions(double elapsedTime)
        {
            BallY = Math.Min(BallY + SpeedY * elapsedTime, WorldHeight - BallRadius);
            BallX = Math.Min(BallX + SpeedX * elapsedTime, WorldWidth - BallRadius);
        }

        private void UpdateSpeedWithWallCollision()
        {
            if (IsBallTouchingHorizontalWalls())
            {
                SpeedX = -SpeedX;
            }

            if (IsBallTouchingVerticalWalls())
            {
                SpeedY = -SpeedY;
            }

            if (IsBallAlmostStopped() && AreSpeedsNegative())
            {
                SpeedY *= (g/12);
                SpeedX *= (g/12);
            }
        }

        private bool IsBallTouchingHorizontalWalls()
        {
            return BallX <= BallRadius || BallX >= (WorldWidth - BallRadius);
        }

        private bool IsBallTouchingVerticalWalls()
        {
            return BallY <= BallRadius || BallY >= (WorldHeight - BallRadius);
        }

        private bool IsBallAlmostStopped()
        {
            return Math.Abs(BallY - WorldHeight) < 11;
        }

        private bool AreSpeedsNegative()
        {
            return SpeedY < 0 && SpeedX < 0;
        }
    }
}

Шестой пункт

В этом коммите шарик вылетает из курсора:

using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;

namespace GravityBalls
{
    public class WorldModel
    {
        [DllImport("user32.dll")]
        public static extern IntPtr GetForegroundWindow();

        [DllImport("user32.dll")]
        public static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);

        public double BallX = 0;
        public double BallY = 0;
        public double BallRadius;
        public double WorldWidth;
        public double WorldHeight;
        public double SpeedX = 70;
        public double SpeedY = 70;
        public double ResistanceCoefficient = 400;
        public double g = 9.8;
        public static int CursorX = 0;
        public static int CursorY = 0;
        public static IntPtr windowHandle;
        public WorldModel()
        {
            Thread thread = new Thread(new ThreadStart(CursorFinder));
            thread.Start();
            ApplyGravity();
            windowHandle = GetForegroundWindow();
        }

        public void SimulateTimeframe(double elapsedTime)
        {
            ApplyResistY(elapsedTime);
            //ApplyResistX(elapsedTime);
            SetBallPositions(elapsedTime);
            UpdateSpeedWithWallCollision();
        }
        private void CursorFinder()
        {
            while (true)
            {
                if (GetMouseCoordinates())
                {
                    var force = 0.2;
                    ApplyForceToBall(force);
                    UpdateSpeedWithWallCollision();
                    CursorX = 0;
                    CursorY = 0;
                }
                Thread.Sleep(10);
            }
        }

        private void ApplyForceToBall(double force)
        {
            // Вычисляем расстояние между шаром и курсором по обеим осям
            var distanceX = CursorX - BallX;
            var distanceY = CursorY - BallY;

            // Вычисляем угол между направлением шара и направлением курсора
            var angle = Math.Atan2(distanceY, distanceX);

            // Вычисляем скорость шара по обеим осям, умножая силу на cos и sin угла
            BallX = CursorX;
            BallY = CursorY;
        }

        private void ApplyGravity()
        {
            SpeedY += g;
        }

        private void ApplyResistY(double elapsedTime)
        {
            SpeedY += ResistanceCoefficient * elapsedTime;
        }

        private void ApplyResistX(double elapsedTime)
        {
            SpeedX += ResistanceCoefficient * elapsedTime;
        }

        private void SetBallPositions(double elapsedTime)
        {
            BallY = Math.Min(BallY + SpeedY * elapsedTime, WorldHeight - BallRadius);
            BallX = Math.Min(BallX + SpeedX * elapsedTime, WorldWidth - BallRadius);
        }

        private void UpdateSpeedWithWallCollision()
        {
            if (IsBallTouchingHorizontalWalls())
            {
                SpeedX = -SpeedX;
            }

            if (IsBallTouchingVerticalWalls())
            {
                SpeedY = -SpeedY;
            }

            if (IsBallAlmostStopped() && AreSpeedsNegative())
            {
                SpeedY *= (g / 12);
                SpeedX *= (g / 12);
            }
        }

        private bool IsBallTouchingHorizontalWalls()
        {
            return BallX <= BallRadius || BallX >= (WorldWidth - BallRadius);
        }

        private bool IsBallTouchingVerticalWalls()
        {
            return BallY <= BallRadius || BallY >= (WorldHeight - BallRadius);
        }

        private bool IsBallAlmostStopped()
        {
            return Math.Abs(BallY - WorldHeight) < 11;
        }

        private bool AreSpeedsNegative()
        {
            return SpeedY < 0 && SpeedX < 0;
        }

        private bool GetMouseCoordinates()
        {
            int chars = 256;
            string pattern = @"[-+]?\d*\.?\d+";
            StringBuilder buff = new StringBuilder(chars);
            if (GetWindowText(windowHandle, buff, chars) > 0)
            {
                string input = buff.ToString();
                MatchCollection matches = Regex.Matches(input, pattern);
                if (matches.Count == 2)
                {
                    foreach (Match match in matches)
                    {
                        if (int.TryParse(match.Value, out int value))
                        {
                            if (CursorX == 0)
                                CursorX = value;
                            else if (CursorY == 0)
                                CursorY = value;
                        }
                    }
                    return true;
                }
            }
            return false;
        }
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment