private static bool isOutsideOfField(PointMass player, RectangleF field) { if (player.Position.Y > field.Top && player.Position.Y < field.Bottom && player.Position.X > field.Left && player.Position.X < field.Right) return false; else return true; }
/// <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"></param> /// <param name="target"></param> /// <returns></returns> public static Vector2 Pursue(PointMass player, PointMass target) { // Calculate the time till intersection. // We want to move the perpendicular distance between the player and the target in the same amount of time to move the parallel distance to the estimated intersection point. // Formulas: // t = perpDist / Vy // Vx * t = initialParallelDist + Ball.ParallelVelocity * t (ignores friction) // => Vx * perpDist / Vy = initialParallelDist + Ball.ParallelVelocity * perpDist / Vy // => Vx * perpDist = initialParallelDist * Vy + Ball.ParallelVelocity * perpDist // => Vx = initialParallelDist * Vy / perpDist + Ball.ParallelVelocity // player.MaxSpeed ^2 = Vx^2 + Vy^2 // => player.MaxSpeed ^2 = (initialParallelDist * Vy / perpDist + Ball.ParallelVelocity)^2 + Vy^2 // => lolno (keep these equations for future reference) // We can solve for Vy/Vx together, this is equal to tan(theta) // The above boils down to: // (1/Ball.ParallelVelocity)^2 = (initialParallelDist/(perpDist*Ball.ParallelVelocity))^2 * t^2 - 2*initialParallelDist/(perpDist * Ball.ParallelVelocity^2) = 1/maxSpeed^2 + t^2/maxSpeed^2 // NOTE t is not time, it is Vy/Vx // Just solve this for t // https://www.wolframalpha.com/input/?i=t%5E2(d%5E2%2F(y%5E2*b%5E2)+-+1%2Fm%5E2)+-+2*d*t%2F(y*b%5E2)+%2B+1%2Fb%5E2+-+1%2Fm%5E2+%3D+0+solve+for+t // => t = (sqrt(b^2 y^2 (y^2 (m^2-b^2)+d^2 m^2))+d m^2 y)/(d^2 m^2-b^2 y^2) if d^2 m^2!=b^2 y^2 // => t = (sqrt(b^2 y^2 (y^2 (m^2-b^2)+d^2 m^2))-d m^2 y)/(b^2 y^2-d^2 m^2) if d^2 m^2!=b^2 y^2 // NOTE t is not time, it is Vy/Vx // The final angle we want to travel towards is: // tan-1(t) return Vector2.Zero; }
public static Simulation Create2V2Simulation() { const float w = 1000; const float h = 500; const float goalW = w / 20; const float goalH = h / 4; const float mass = 1; const float radius = 7.5f; const float maxForce = 100; const float maxSpeed = 100; var team1Players = new PointMass[5]; var team2Players = new PointMass[5]; for (var j = 0; j < 5; j++) { team1Players[j] = new PointMass(mass, radius, maxForce, maxSpeed, new Vector2(-w / 4 + w / 2 + (j % 2 != 0 ? 100 : 0), -h / 4 + j * h / 8), Vector2.Zero); team2Players[j] = new PointMass(mass, radius, maxForce, maxSpeed, new Vector2(-w / 4 + (j % 2 != 0 ? -100 : 0), -h / 4 + j * h / 8), Vector2.Zero); team1Players[j].id = "A" + j; team2Players[j].id = "B" + j; } var pitch = new RectangleF(-w / 2, -h / 2, w, h); var team1Goal = new RectangleF(-w / 2 - goalW, -goalH / 2, goalW, goalH); var team2Goal = new RectangleF(w / 2, -goalH / 2, goalW, goalH); var team1 = new KeepawayTeam(new ReadOnlyCollection<PointMass>(team1Players), team1Goal); var team2 = new KeepawayTeam(new ReadOnlyCollection<PointMass>(team2Players), team2Goal); var ball = new PointMass(1, 2.5f, 100000, 100, new Vector2(0, 0), Vector2.Zero); return new Simulation(new ReadOnlyCollection<Team>(new Team[] { team1, team2 }), ball, pitch, 10); }
/// <summary> /// </summary> /// <param name="player"></param> /// <param name="team"></param> /// <param name="otherTeam"></param> /// <param name="simulation"></param> /// <param name="passingLaneWidth"></param> /// <returns></returns> public static IEnumerable<IPointMass> GetOpenPlayers( PointMass player, ITeam team, ITeam otherTeam, ISimulation simulation, float passingLaneWidth) => from p in team.Players where p != player && IsPlayerOpenForPass(player, p, otherTeam.Players, passingLaneWidth) select p;
public override Kick Execute(ISimulation simulation) { var ballChaser = FootballStrategies.ClosestPlayerToPoint(this.Players, simulation.Ball, 0); var kick = Kick.None; foreach (var player in Players) { if (player == ballChaser) { //messages[player] = "Chaser"; var playersExceptSelf = Players.ToList(); playersExceptSelf.Remove(player); player.Force = SteeringStrategies.Pursue(player, simulation.Ball, 1); if ((player.Position - simulation.Ball.Position).Length() < player.Radius + simulation.Ball.Radius) { var isLeftTeam = this.GoalBounds.Left > 0 ? true : false; PointMass[] arr = new PointMass[7]; playersExceptSelf.CopyTo(arr, 0); arr[4] = new PointMass(1, 1, 1, 1, new Vector2(isLeftTeam ? this.GoalBounds.Left : this.GoalBounds.Right, this.GoalBounds.Top + (0.2f) * this.GoalBounds.Height), Vector2.Zero); arr[5] = new PointMass(1, 1, 1, 1, new Vector2(isLeftTeam ? this.GoalBounds.Left : this.GoalBounds.Right, this.GoalBounds.Top + (0.5f) * this.GoalBounds.Height), Vector2.Zero); arr[6] = new PointMass(1, 1, 1, 1, new Vector2(isLeftTeam ? this.GoalBounds.Left : this.GoalBounds.Right, this.GoalBounds.Top + (0.8f) * this.GoalBounds.Height), Vector2.Zero); arr[4].id = "GT"; arr[5].id = "GM"; arr[6].id = "GB"; ReadOnlyCollection<PointMass> roc = new ReadOnlyCollection<PointMass>(arr); Vector2 middleOfGoal = new Vector2(isLeftTeam ? this.GoalBounds.Left : this.GoalBounds.Right, this.GoalBounds.Top - (0.5f) * this.GoalBounds.Height); IPointMass kickTarget = ClosestPlayerToPoint(roc, player, 1, middleOfGoal); messages[player] = "Chaser T: " + kickTarget.id; k = kick = FootballStrategies.PassToPlayer(player, kickTarget, simulation.Ball); } else k = kick = Kick.None; } else { messages[player] = isOutsideOfField(player, simulation.PitchBounds) ? "Outside" : "inside"; var allPlayers = simulation.Teams[0].Players.Concat(simulation.Teams[1].Players); if (isOutsideOfField(player, simulation.PitchBounds)) player.Force = SteeringStrategies.Seek(player, Vector2.Zero, player.MaxSpeed); else FootballStrategies.SpreadOut(player, allPlayers, simulation.PitchBounds, 150, 100); } } return kick; }
/// <summary> /// Initializes a new instance of the <see cref="Simulation" /> class. /// </summary> /// <param name="teams">The teams to be played against each other.</param> /// <param name="ball">The ball.</param> /// <param name="pitchBounds">The pitch boundaries.</param> /// <param name="friction">The friction coefficient.</param> public Simulation(ReadOnlyCollection<Team> teams, PointMass ball, RectangleF pitchBounds, float friction) { Contract.Requires<ArgumentNullException>(teams != null); Contract.Requires<ArgumentNullException>(ball != null); Contract.Requires<ArgumentException>(friction >= 0); Contract.Requires<ArgumentException>(pitchBounds.Width > 0 && pitchBounds.Height > 0); Contract.Requires<ArgumentException>(Contract.ForAll(teams, t => t != null && t.IsValid(pitchBounds))); Contract.Requires<ArgumentException>(pitchBounds.Contains(ball.Position)); _simulate = SimulatePlaying; _teams = teams; _startingPositions = from t in teams select t.PlayerPositions; _ball = ball; _ballStartingPosition = ball.Position; PitchBounds = pitchBounds; Friction = friction; }
/// <summary> /// Initializes a new instance of the <see cref="Simulation" /> class. /// </summary> /// <param name="teams">The teams to be played against each other.</param> /// <param name="ball">The ball.</param> /// <param name="pitchBounds">The pitch boundaries.</param> /// <param name="friction">The friction coefficient.</param> public Simulation(ReadOnlyCollection<Team> teams, PointMass ball, RectangleF pitchBounds, float friction) { Contract.Requires<ArgumentNullException>(teams != null); Contract.Requires<ArgumentNullException>(ball != null); Contract.Requires<ArgumentException>(friction >= 0); Contract.Requires<ArgumentException>(pitchBounds.Width > 0 && pitchBounds.Height > 0); Contract.Requires<ArgumentException>(Contract.ForAll(teams, t => t != null && pitchBounds.IntersectsOrBorders(t.GoalBounds) && t.Players.All(p => pitchBounds.Contains(p.Position)))); Contract.Requires<ArgumentException>(pitchBounds.Contains(ball.Position)); _simulate = SimulatePlaying; _teams = teams; _startingPositions = (from t in teams select (from p in t.Players select p.Position).ToList().AsReadOnly()).ToList().AsReadOnly(); _ball = ball; _ballStartingPosition = ball.Position; PitchBounds = pitchBounds; Friction = friction; }
/// <summary> /// </summary> /// <param name="player"></param> /// <param name="target"></param> /// <param name="desiredSpeed"></param> /// <returns></returns> public static Vector2 FleeNormalized(PointMass player, Vector2 target, float desiredSpeed) => Vector2.Normalize(Flee(player, target, desiredSpeed));
/// <summary> /// Move away from a specified position. /// </summary> /// <param name="player"></param> /// <param name="target"></param> /// <param name="desiredSpeed"></param> /// <returns></returns> public static Vector2 Flee(PointMass player, Vector2 target, float desiredSpeed) { var desiredVelocity = Vector2.Normalize(target - player.Position).ClampMagnitude(desiredSpeed); return desiredVelocity - player.Velocity; }
/// <summary> /// Move away from a target's future position. /// </summary> /// <param name="player"></param> /// <param name="target"></param> /// <returns></returns> public static Vector2 Evade(PointMass player, PointMass target) => Vector2.Zero;
/// <summary> /// </summary> /// <param name="player"></param> /// <param name="allPlayers"></param> /// <param name="pitchBounds"></param> /// <param name="playerOverlapRadius"></param> /// <param name="edgeOverlapRadius"></param> public static void SpreadOut( PointMass player, IEnumerable<IPointMass> allPlayers, RectangleF pitchBounds, float playerOverlapRadius, float edgeOverlapRadius) { var v = (from otherPlayer in allPlayers where player != otherPlayer select player.Position - otherPlayer.Position into between where between.Length() < playerOverlapRadius select between).Aggregate(Vector2.Zero, (current, between) => current + Vector2.Normalize(between)); var force = Vector2.Normalize(v)*10; var stoppingForce = -player.Velocity; force = v.Length() > 0 ? force : stoppingForce; if (Math.Abs(player.Position.X - pitchBounds.X) < edgeOverlapRadius && player.Velocity.X < 0) force = new Vector2(-player.Velocity.X, force.Y); if (Math.Abs(player.Position.X - pitchBounds.Right) < edgeOverlapRadius && player.Velocity.X > 0) force = new Vector2(-player.Velocity.X, force.Y); if (Math.Abs(player.Position.Y - pitchBounds.Y) < edgeOverlapRadius && player.Velocity.Y < 0) force = new Vector2(force.X, -player.Velocity.Y); if (Math.Abs(player.Position.Y - pitchBounds.Bottom) < edgeOverlapRadius && player.Velocity.Y > 0) force = new Vector2(force.X, -player.Velocity.Y); player.Force = force; }