/// <summary> /// Estimates the future position of target. /// </summary> /// <param name="player">The player on which the future position estimate is to be performed.</param> /// <param name="target">The target position of the player.</param> /// <param name="max">The maximum estimated time.</param> /// <returns>The future position of the player.</returns> public static Vector2 FuturePosition(IPointMass player, IPointMass target, float max) { Contract.Requires<ArgumentException>(player != null); Contract.Requires<ArgumentException>(target != null); Contract.Requires<ArgumentException>(max >= 0); var estimatedTime = max; var invSpeed = 1/player.Velocity.Length(); if (!float.IsNaN(invSpeed)) { int p, f; var offset = target.Position - player.Position; var parallelness = Vector2.Dot(Vector2.Normalize(player.Velocity), Vector2.Normalize(target.Velocity)); if (float.IsNaN(parallelness)) p = 1; else p = parallelness < -0.707f ? 2 : (parallelness > 0.707f ? 0 : 1); var forwardness = Vector2.Dot(Vector2.Normalize(player.Velocity), Vector2.Normalize(offset)); if (float.IsNaN(forwardness)) f = 3; else f = forwardness < -0.707f ? 6 : (forwardness > 0.707f ? 0 : 3); estimatedTime = Math.Min(offset.Length()*invSpeed*TimeFactorTable[p + f], max); } return target.Position + target.Velocity*estimatedTime; }
/// <summary> /// Move toward a specified position. /// </summary> /// <param name="player">The player that should seek the specified position.</param> /// <param name="target">The target position.</param> /// <param name="desiredSpeed">The desired speed at which the player should move towards the target.</param> /// <returns>The force vector that should be applied to the player.</returns> public static Vector2 Seek(IPointMass player, Vector2 target, float desiredSpeed) { Contract.Requires<ArgumentException>(player != null); Contract.Requires<ArgumentException>(desiredSpeed >= 0); return Vector2.Normalize(target - player.Position)*desiredSpeed - player.Velocity; }
public Spring(IPointMass end1, IPointMass end2, float stiffness, float damping) : base() { End1 = end1; End2 = end2; Stiffness = stiffness; Damping = damping; TargetLength = Vector3.Distance(end1.Position, end2.Position) * 0.95f; }
/// <summary> /// </summary> /// <param name="player"></param> /// <param name="target"></param> /// <param name="otherPlayers"></param> /// <param name="passingLaneWidth"></param> /// <returns></returns> public static bool IsPlayerOpenForPass( IPointMass player, IPointMass target, IEnumerable<IPointMass> otherPlayers, float passingLaneWidth) { var angle = Math.PI/2 - Math.Atan2(target.Position.Y - player.Position.Y, target.Position.X - player.Position.X); var v = passingLaneWidth/2*new Vector2((float) Math.Cos(angle), (float) Math.Sin(angle)); return otherPlayers.All( p => !PointInTriangle(p.Position, player.Position, v + target.Position, target.Position - v)); }
private static void DrawPointMass(IPointMass pointMass, Pen pen, Brush brush, Graphics g) { var triangle = new[] { new PointF(1, 0), new PointF(-0.7f, 0.7f), new PointF(-0.7f, -0.7f) }; using (var m = new Matrix()) { m.Translate(pointMass.Position.X, pointMass.Position.Y); m.Rotate((float)(Math.Atan2(pointMass.Velocity.Y, pointMass.Velocity.X) * 180 / Math.PI)); m.Scale(pointMass.Radius*0.9f, pointMass.Radius*0.9f); m.TransformPoints(triangle); } g.FillPolygon(brush, triangle); g.DrawCircle(pen, pointMass.Position, pointMass.Radius); }
/// <summary> /// Move to and stop at a specified position. /// </summary> /// <param name="player">The player that should arrive at the specified position.</param> /// <param name="target">The target position.</param> /// <param name="maxSpeed">The maximum speed at which the player should move towards the target position.</param> /// <param name="slowingRadius">The radius in which the player should begin to slow down.</param> /// <returns>The force vector that should be applied to the player.</returns> public static Vector2 Arrive(IPointMass player, Vector2 target, float maxSpeed, float slowingRadius) { Contract.Requires<ArgumentException>(player != null); Contract.Requires<ArgumentException>(maxSpeed >= 0); Contract.Requires<ArgumentException>(slowingRadius >= 0); var targetOffset = target - player.Position; var distance = targetOffset.Length(); var rampedSpeed = maxSpeed*(distance/slowingRadius); var clippedSpeed = Math.Min(rampedSpeed, player.MaxSpeed); var direction = targetOffset/distance; var desiredVelocity = clippedSpeed*direction; return desiredVelocity - player.Velocity; }
/// <summary> /// </summary> /// <param name="player"></param> /// <param name="target"></param> /// <param name="ball"></param> /// <returns></returns> // TODO: Finish this public static Kick PassToPlayer(IPointMass player, IPointMass target, IPointMass ball) { var desiredDirection = target.Position - player.Position; if (ball.Velocity.LengthSquared() < 0.1) { return new Kick(player, desiredDirection * 100000); } else { var projection = ball.Velocity.Projection(desiredDirection); var rejection = ball.Velocity.Rejection(desiredDirection); var difference = ball.Velocity - desiredDirection; var force = desiredDirection - projection - rejection; //var angle = Math.Atan2(difference.Y, difference.X); //var force = ball.Velocity - rejection * 2; //if (Math.Abs(angle) > Math.PI / 2) // force -= projection * 2; return new Kick(player, force * 100000); } }
private static void DrawDebugInfo(IPointMass pointMass, Brush brush, Font font, Graphics g) { }
private static IPointMass ClosestPlayerToPoint(IEnumerable<IPointMass> players, IPointMass target, float max, Vector2 goalMiddle) { return players.OrderBy(p => (p.Position - SteeringStrategies.FuturePosition(p, target, max)).LengthSquared() + DistanceBetween(p.Position, goalMiddle)).First(); }
/// <summary> /// Creates a new instance of the <see cref="Kick" /> class with the specified player and force. /// </summary> /// <param name="player">The player to kick the ball.</param> /// <param name="force">The desired force on the ball to be exerted by the player.</param> public Kick(IPointMass player, Vector2 force) { Contract.Requires<ArgumentNullException>(player != null); Player = player; Force = force; }
/// <summary> /// </summary> /// <param name="players"></param> /// <param name="target"></param> /// <param name="max"></param> /// <returns></returns> public static IPointMass ClosestPlayerToPoint(IEnumerable<IPointMass> players, IPointMass target, float max) => players.OrderBy(p => (p.Position - SteeringStrategies.FuturePosition(p, target, max)).LengthSquared()).First();
private bool IsKickValid(IPointMass player, ISimulation simulation) => Players.Any(p => p == player) && (player.Position - simulation.Ball.Position).Length() < player.Radius + simulation.Ball.Radius;
/// <summary> /// </summary> /// <param name="player"></param> /// <returns></returns> public static float GetSlowingRadius(IPointMass player) { Contract.Requires<ArgumentException>(player != null); return player.MaxSpeed*player.MaxSpeed/(2*player.MaxForce/player.Mass); }
/// <summary> /// Move toward a target's future position. This method assumes the player will move at their maximum speed. It also /// ignores friction on the target. /// </summary> /// <param name="player">The player that should pursue the specified location.</param> /// <param name="target">The target position that the player should pursue.</param> /// <param name="maxEstimatedTime">The maximum estimated time.</param> /// <returns>The force vector that shoud be applied to the player.</returns> public static Vector2 Pursue(IPointMass player, IPointMass target, float maxEstimatedTime) => Seek(player, FuturePosition(player, target, maxEstimatedTime), player.MaxSpeed);