Это все содержимое файла WorldModel.cs
- Сделайте так, чтобы шар двигался с постоянной скоростью, пока не долетает до края окна. Скорость должно быть возможно задавать на старте (в коде), в том числе горизонтальную составляющую.
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);
}
}
- Шар должен отскакивал от краёв окна по законам идеального геометрического бильярда.
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;
}
}
}
}
- На шар действует сила сопротивления, пропорциональная модулю скорости и направленная против скорости.
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;
}
}
}
}
- Наведите порядок в коде. Сделайте код красивым, понятным и лаконичным. После вашего рефакторинга, новые возможности должно быть возможно добавлять в код, не переусложняя его. Старайтесь, чтобы ни один метод не был длиннее 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;
}
}
}
- На шар действует ещё одна сила — сила притяжения, константная по модулю и направленная вниз.
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;
}
}
}