/// <summary> /// As above, but avoiding sqrt /// </summary> /// <param name="A"></param> /// <param name="B"></param> /// <param name="P"></param> /// <returns></returns> public static double DistToLineSegmentSq(Vector2D A, Vector2D B, Vector2D P) { //if the angle is obtuse between PA and AB is obtuse then the closest //vertex must be A double dotA = (P.X - A.X) * (B.X - A.X) + (P.Y - A.Y) * (B.Y - A.Y); if (dotA <= 0) { return(Vector2D.Vec2DDistanceSq(A, P)); } //if the angle is obtuse between PB and AB is obtuse then the closest //vertex must be B double dotB = (P.X - B.X) * (A.X - B.X) + (P.Y - B.Y) * (A.Y - B.Y); if (dotB <= 0) { return(Vector2D.Vec2DDistanceSq(B, P)); } //calculate the point along AB that is the closest to P Vector2D Point = A + ((B - A) * dotA) / (dotA + dotB); //calculate the distance P-Point return(Vector2D.Vec2DDistanceSq(P, Point)); }
/// <summary> /// calculate the closest player to the SupportSpot /// /// </summary> /// <returns></returns> public PlayerBase DetermineBestSupportingAttacker() { double ClosestSoFar = double.MaxValue; PlayerBase BestPlayer = null; for (int playerIndex = 0; playerIndex < _players.Count; playerIndex++) { //only attackers utilize the BestSupportingSpot if ((_players[playerIndex].PlayerRole == PlayerBase.PlayerRoles.Attacker) && (_players[playerIndex] != _controllingPlayer)) { //calculate the dist. Use the squared value to avoid sqrt double dist = Vector2D.Vec2DDistanceSq(_players[playerIndex].Position, _supportSpotCalculator.GetBestSupportingSpot()); //if the distance is the closest so far and the player is not a //goalkeeper and the player is not the one currently controlling //the ball, keep a record of this player if ((dist < ClosestSoFar)) { ClosestSoFar = dist; BestPlayer = _players[playerIndex]; } } } return(BestPlayer); }
/// <summary> /// returns true if an opposing player is within the radius of the position /// given as a parameter /// /// </summary> /// <param name="pos"></param> /// <param name="rad"></param> /// <returns></returns> public bool IsOpponentWithinRadius(Vector2D pos, double rad) { foreach (PlayerBase opponent in OpposingTeam.Players) { if (Vector2D.Vec2DDistanceSq(pos, opponent.Position) < rad * rad) { return(true); } } return(false); }
/// <summary> /// The best pass is considered to be the pass that cannot be intercepted /// by an opponent and that is as far forward of the receiver as possible /// /// </summary> /// <param name="passer"></param> /// <param name="receiver"></param> /// <param name="PassTarget"></param> /// <param name="power"></param> /// <param name="MinPassingDistance"></param> /// <returns></returns> public bool FindPass(PlayerBase passer, out PlayerBase receiver, out Vector2D PassTarget, double power, double MinPassingDistance) { receiver = null; PassTarget = null; double ClosestToGoalSoFar = double.MaxValue; Vector2D Target = new Vector2D(); //iterate through all this player's team members and calculate which //one is in a position to be passed the ball foreach (PlayerBase currentPlayer in _players) { //make sure the potential receiver being examined is not this player //and that it is further away than the minimum pass distance if ((currentPlayer != passer) && (Vector2D.Vec2DDistanceSq(passer.Position, currentPlayer.Position) > MinPassingDistance * MinPassingDistance)) { if (GetBestPassToReceiver(passer, currentPlayer, ref Target, power)) { //if the pass target is the closest to the opponent's goal line found // so far, keep a record of it double Dist2Goal = Math.Abs(Target.X - OpponentsGoal.GoalLineCenter.X); if (Dist2Goal < ClosestToGoalSoFar) { ClosestToGoalSoFar = Dist2Goal; //keep a record of this player receiver = currentPlayer; //and the target PassTarget = Target; } } } }//next team member if (receiver != null) { return(true); } else { return(false); } }
//------------------------- IsThreatened --------------------------------- // // returns true if there is an opponent within this player's // comfort zone //------------------------------------------------------------------------ public bool IsThreatened() { for (int opponentIndex = 0; opponentIndex < Team.OpposingTeam.Players.Count; opponentIndex++) { //calculate distance to the player. if dist is less than our //comfort zone, and the opponent is infront of the player, return true if (IsPositionInFrontOfPlayer(Team.OpposingTeam.Players[opponentIndex].Position) && (Vector2D.Vec2DDistanceSq(Position, Team.OpposingTeam.Players[opponentIndex].Position) < ParameterManager.Instance.PlayerComfortZoneSq)) { return(true); } }// next opp return(false); }
/// <summary> /// given a line segment AB and a circle position and radius, this function /// determines if there is an intersection and stores the position of the /// closest intersection in the reference IntersectionPoint /// /// returns false if no intersection point is found /// </summary> /// <param name="A"></param> /// <param name="B"></param> /// <param name="pos"></param> /// <param name="radius"></param> /// <param name="IntersectionPoint"></param> /// <returns></returns> public static bool GetLineSegmentCircleClosestIntersectionPoint(Vector2D A, Vector2D B, Vector2D pos, double radius, ref Vector2D IntersectionPoint) { Vector2D toBNorm = Vector2D.Vec2DNormalize(B - A); //move the circle into the local space defined by the vector B-A with origin //at A Vector2D LocalPos = Transformations.PointToLocalSpace(pos, toBNorm, toBNorm.Perp, A); bool ipFound = false; //if the local position + the radius is negative then the circle lays behind //point A so there is no intersection possible. If the local x pos minus the //radius is greater than length A-B then the circle cannot intersect the //line segment if ((LocalPos.X + radius >= 0) && ((LocalPos.X - radius) * (LocalPos.X - radius) <= Vector2D.Vec2DDistanceSq(B, A))) { //if the distance from the x axis to the object's position is less //than its radius then there is a potential intersection. if (Math.Sqrt(LocalPos.Y) < radius) { //now to do a line/circle intersection test. The center of the //circle is represented by A, B. The intersection points are //given by the formulae x = A +/-sqrt(r^2-B^2), y=0. We only //need to look at the smallest positive value of x. double a = LocalPos.X; double b = LocalPos.Y; double ip = a - Math.Sqrt(radius * radius - b * b); if (ip <= 0) { ip = a + Math.Sqrt(radius * radius - b * b); } ipFound = true; IntersectionPoint = A + toBNorm * ip; } } return(ipFound); }
/// <summary> /// sets _playerClosestToBall to the player closest to the ball /// </summary> public void CalculateClosestPlayerToBall() { double ClosestSoFar = double.MaxValue; for (int playerIndex = 0; playerIndex < _players.Count; playerIndex++) { //calculate the dist. Use the squared value to avoid sqrt double dist = Vector2D.Vec2DDistanceSq(_players[playerIndex].Position, Pitch.Ball.Position); //keep a record of this value for each player _players[playerIndex].DistanceToBallSquared = dist; if (dist < ClosestSoFar) { ClosestSoFar = dist; _playerClosestToBall = _players[playerIndex]; } } _distSqToBallOfClosestPlayer = ClosestSoFar; }
/// <summary> /// Tests to see if the ball has collided with a ball and reflects /// the ball's velocity accordingly /// </summary> /// <param name="walls"></param> public void TestCollisionWithWalls(List <Wall2D> walls) { //test ball against each wall, find out which is closest int closestIndex = -1; Vector2D normalVector = Vector2D.Vec2DNormalize(Velocity); Vector2D intersectionPoint = new Vector2D(), collisionPoint = new Vector2D(); double distToIntersection = double.MaxValue; ////iterate through each wall and calculate if the ball intersects. ////If it does then store the index into the closest intersecting wall for (int wallIndex = 0; wallIndex < walls.Count; wallIndex++) { //assuming a collision if the ball continued on its current heading //calculate the point on the ball that would hit the wall. This is //simply the wall's normal(inversed) multiplied by the ball's radius //and added to the balls center (its position) Vector2D thisCollisionPoint = Position - (walls[wallIndex].VectorNormal * BoundingRadius); // //calculate exactly where the collision point will hit the plane if (Geometry.WhereIsPoint(thisCollisionPoint, walls[wallIndex].VectorFrom, walls[wallIndex].VectorNormal) == Geometry.PlaneLocation.Behind) { double distToWall = Geometry.DistanceToRayPlaneIntersection(thisCollisionPoint, walls[wallIndex].VectorNormal, walls[wallIndex].VectorFrom, walls[wallIndex].VectorNormal); intersectionPoint = thisCollisionPoint + (distToWall * walls[wallIndex].VectorNormal); } else { double distToWall = Geometry.DistanceToRayPlaneIntersection(thisCollisionPoint, normalVector, walls[wallIndex].VectorFrom, walls[wallIndex].VectorNormal); intersectionPoint = thisCollisionPoint + (distToWall * normalVector); } //check to make sure the intersection point is actually on the line //segment bool onLineSegment = false; if (Geometry.LineIntersection2D(walls[wallIndex].VectorFrom, walls[wallIndex].VectorTo, thisCollisionPoint - walls[wallIndex].VectorNormal * 20.0, thisCollisionPoint + walls[wallIndex].VectorNormal * 20.0)) { onLineSegment = true; } //Note, there is no test for collision with the end of a line segment //now check to see if the collision point is within range of the //velocity vector. [work in distance squared to avoid sqrt] and if it //is the closest hit found so far. //If it is that means the ball will collide with the wall sometime //between this time step and the next one. double distSq = Vector2D.Vec2DDistanceSq(thisCollisionPoint, intersectionPoint); if ((distSq <= Velocity.LengthSquared) && (distSq < distToIntersection) && onLineSegment) { distToIntersection = distSq; closestIndex = wallIndex; collisionPoint = intersectionPoint; } } // next wall //to prevent having to calculate the exact time of collision we //can just check if the velocity is opposite to the wall normal //before reflecting it. This prevents the case where there is overshoot //and the ball gets reflected back over the line before it has completely //reentered the playing area. if ((closestIndex >= 0) && normalVector.GetDotProduct(walls[closestIndex].VectorNormal) < 0) { Velocity.Reflect(walls[closestIndex].VectorNormal); } }
/// <summary> /// test if a pass from 'from' to 'to' can be intercepted by an opposing player /// /// </summary> /// <param name="from"></param> /// <param name="target"></param> /// <param name="receiver"></param> /// <param name="opp"></param> /// <param name="PassingForce"></param> /// <returns></returns> public bool IsPassSafeFromOpponent(Vector2D from, Vector2D target, PlayerBase receiver, PlayerBase opp, double PassingForce) { //move the opponent into local space. Vector2D ToTarget = target - from; Vector2D ToTargetNormalized = Vector2D.Vec2DNormalize(ToTarget); Vector2D LocalPosOpp = Transformations.PointToLocalSpace(opp.Position, ToTargetNormalized, ToTargetNormalized.Perp, from); //if opponent is behind the kicker then pass is considered okay(this is //based on the assumption that the ball is going to be kicked with a //velocity greater than the opponent's max velocity) if (LocalPosOpp.X < 0) { return(true); } //if the opponent is further away than the target we need to consider if //the opponent can reach the position before the receiver. if (Vector2D.Vec2DDistanceSq(from, target) < Vector2D.Vec2DDistanceSq(opp.Position, from)) { if (receiver != null) { if (Vector2D.Vec2DDistanceSq(target, opp.Position) > Vector2D.Vec2DDistanceSq(target, receiver.Position)) { return(true); } else { return(false); } } else { return(true); } } //calculate how long it takes the ball to cover the distance to the //position orthogonal to the opponents position double TimeForBall = Pitch.Ball.CalucateTimeToCoverDistance(new Vector2D(0, 0), new Vector2D(LocalPosOpp.X, 0), PassingForce); //now calculate how far the opponent can run in this time double reach = opp.MaxSpeed * TimeForBall + Pitch.Ball.BoundingRadius + opp.BoundingRadius; //if the distance to the opponent's y position is less than his running //range plus the radius of the ball and the opponents radius then the //ball can be intercepted if (Math.Abs(LocalPosOpp.Y) < reach) { return(false); } return(true); }
public bool TooFarFromGoalMouth() { return(Vector2D.Vec2DDistanceSq(Position, GetRearInterposeTarget()) > ParameterManager.Instance.GoalKeeperInterceptRangeSq); }
public bool BallWithinRangeForIntercept() { return(Vector2D.Vec2DDistanceSq(Team.HomeGoal.GoalLineCenter, Ball.Position) <= ParameterManager.Instance.GoalKeeperInterceptRangeSq); }