public MovingPlatform(Shape aPlatform, Vector2D start, Vector2D end, float aSpeed, float dampen) { point1 = start; point2 = end; destSpeed = aSpeed; Vector2D displacement = point2 - point1; direction = displacement.Normalised(); timeToTravel = displacement.Length() / destSpeed; timeSoFar = 0.0f; currentSpeed = 0; //if the dampen time is longer than half the total travel time then cut it down to half the travel time if (dampen * 2.0f > timeToTravel) { dampen = timeToTravel / 2.0f; } dampenTime = dampen; acceleration = SuvatEquations.AfromUTV(0, dampenTime, destSpeed); platform = new RigidBody(); platform.Shape = aPlatform; platform.LinearVelocity = direction * currentSpeed; platform.moveable = true; platform.obeysGravity = false; platform.reactToForce = false; platform.SetPosition(point1); }
public float GetTotalJumpDuration(Vector2D source, Vector2D destination, Vector2D gravity) { //jump time is equal to the time it takes to traverse the required vertical displacement Vector2D displacement = destination - source; return(SuvatEquations.TfromSUA(displacement.Y, jumpInitialVelocity, gravity.Y).Item1); }
//given the velocity, acceleration, duration, and coordinate of one axis, calculate the coordinate of the other axis from it's velocity and acceleration static private Tuple <float, float> GetOtherCoordFromCurve(float initialVelocity1, float initialVelocity2, float acceleration1, float acceleration2, float maxDuration, float coord1) { //time axis 1 takes to reach coord1 Tuple <float, float> time = SuvatEquations.TfromSUA(coord1, initialVelocity1, acceleration1); float result1 = time.Item1; float result2 = time.Item2; //if either result is invalid, mark is as such if (result1 >= maxDuration) { result1 = float.NaN; } if (result2 >= maxDuration) { result2 = float.NaN; } if (result1 <= 0) { result1 = float.NaN; } if (result2 <= 0) { result2 = float.NaN; } //calculate the two possible positions of coord2 float otherCoord1 = SuvatEquations.SfromUAT(initialVelocity2, acceleration2, result1); float otherCoord2 = SuvatEquations.SfromUAT(initialVelocity2, acceleration2, result2); return(new Tuple <float, float>(otherCoord1, otherCoord2)); }
//determine if the current player could jump from a given coordinate to a given coordinate public bool CanJumpToFrom(Vector2D source, Vector2D destination, Vector2D gravity) { Vector2D relativeDestination = destination - source; //calculate the time to reach the apex of the jump, and the height of the apex float timeToApex = SuvatEquations.TfromUAV(jumpInitialVelocity, gravity.Y, 0.0f); float maxYRange = SuvatEquations.SfromUAT(jumpInitialVelocity, gravity.Y, timeToApex); //positions above the apex cannot be jumped to if (relativeDestination.Y < maxYRange) { return(false); } //calculate the time it takes to reach the correct X coord travelling horizontally float horizontalAcceleration = GetHorizontalAcceleration() * relativeDestination.X / Math.Abs(relativeDestination.X); Tuple <float, float> timeToReach = SuvatEquations.TfromSUA(relativeDestination.X, playerBody.LinearVelocity.X, horizontalAcceleration); if (float.IsNaN(timeToReach.Item1)) { return(false); } float largestTime = timeToReach.Item1; if (!float.IsNaN(timeToReach.Item2) && timeToReach.Item2 > timeToReach.Item1) { largestTime = timeToReach.Item2; } //If the point's X is reached before the furthest apex' X then it must be within range (Note: this is only true as long as the player's horizontal velocity isn't too large) if (largestTime <= timeToApex) { return(true); } //Find the Y position of the jump at the destination X float timeFromApex = largestTime - timeToApex; float YAtDestX = maxYRange + SuvatEquations.SfromUAT(0, gravity.Y, timeFromApex); //If the destination Y is above the jump arc then it is not reachable if (YAtDestX > relativeDestination.Y) { return(false); } return(true); }
//Get the coordinates of the specified jump arc where it lines up with one of the edges of a given rigidbody public Tuple <Tuple <Vector2D, Vector2D, Vector2D, Vector2D>, Tuple <Vector2D, Vector2D, Vector2D, Vector2D> > GetBoxCollisionPoints(RigidBody RB, Vector2D source, float accelerationTime, float totalJumpTime, Vector2D gravity) { if (float.IsNaN(accelerationTime)) { return(null); } //force acceleration time to be positive (negative times are not valid) int sign = (int)(accelerationTime / (float)Math.Abs(accelerationTime)); accelerationTime *= sign; //calculate the possible collision locations during acceleration Vector2D acceleration = new Vector2D(GetHorizontalAcceleration() * sign, gravity.Y); Vector2D velocity = new Vector2D(playerBody.LinearVelocity.X, jumpInitialVelocity); Vector2D initialPosition = new Vector2D(source); Tuple <Vector2D, Vector2D, Vector2D, Vector2D> resultsDuringAcc = UsefulMathsFunctions.GetArcPosAtBoxEdges(RB, initialPosition, velocity, acceleration, accelerationTime); //calculate the possible collision locations after acceleration float YatAccEnd = SuvatEquations.SfromUAT(velocity.Y, acceleration.Y, accelerationTime); float XatAccEnd = SuvatEquations.SfromUAT(velocity.X, acceleration.X, accelerationTime); initialPosition.Y += YatAccEnd; initialPosition.X += XatAccEnd; velocity.X = SuvatEquations.VfromUAT(velocity.X, acceleration.X, accelerationTime); velocity.Y = SuvatEquations.VfromUAT(velocity.Y, acceleration.Y, accelerationTime); acceleration.X = 0; Tuple <Vector2D, Vector2D, Vector2D, Vector2D> resultsAftergAcc = UsefulMathsFunctions.GetArcPosAtBoxEdges(RB, initialPosition, velocity, acceleration, totalJumpTime - accelerationTime); //return the two sets of coordinates return(new Tuple <Tuple <Vector2D, Vector2D, Vector2D, Vector2D>, Tuple <Vector2D, Vector2D, Vector2D, Vector2D> >(resultsDuringAcc, resultsAftergAcc)); }
//determine if the player can fall from a given coordinate to a given coordinate public bool CanFallToFrom(Vector2D source, Vector2D destination, Vector2D gravity) { Vector2D relativeDestination = destination - source; //cannot fall to a destination that is above the source if (relativeDestination.Y < 0) { return(false); } //Calculate the time it takes to travel the horizontal difference float horizontalAcceleration = GetHorizontalAcceleration() * relativeDestination.X / Math.Abs(relativeDestination.X); Tuple <float, float> timeToReach = SuvatEquations.TfromSUA(relativeDestination.X, playerBody.LinearVelocity.X, horizontalAcceleration); if (float.IsNaN(timeToReach.Item1)) { return(false); } float largestTime = timeToReach.Item1; if (!float.IsNaN(timeToReach.Item2) && timeToReach.Item2 > timeToReach.Item1) { largestTime = timeToReach.Item2; } //If the Y position after that time is below the destination's Y position then the destination cannot be fallen to float YAtDestX = SuvatEquations.SfromUAT(0, gravity.Y, largestTime); if (YAtDestX > relativeDestination.Y) { return(false); } return(true); }
//Get the Y coordinates of an input jump at the specified X coordinate public Tuple <Vector2D, Vector2D> GetJumpYFromX(Vector2D source, float accelerationTime, float totalJumpTime, Vector2D gravity, float destX) { if (float.IsNaN(accelerationTime)) { return(new Tuple <Vector2D, Vector2D>(null, null)); } //setup the required variables int sign = (int)(accelerationTime / (float)Math.Abs(accelerationTime)); accelerationTime *= sign; Vector2D acceleration = new Vector2D(GetHorizontalAcceleration() * sign, gravity.Y); Vector2D velocity = new Vector2D(playerBody.LinearVelocity.X, jumpInitialVelocity); Vector2D initialPosition = new Vector2D(source); //get the XY coordinates from the curve during acceleration Tuple <Vector2D, Vector2D> possiblePositions1 = UsefulMathsFunctions.GetWorldspacePointAlongCurve(source, velocity, acceleration, accelerationTime, destX, float.NaN); //update the variables to the second half of the jump (post acceleration) float YatAccEnd = SuvatEquations.SfromUAT(velocity.Y, acceleration.Y, accelerationTime); float XatAccEnd = SuvatEquations.SfromUAT(velocity.X, acceleration.X, accelerationTime); initialPosition.Y += YatAccEnd; initialPosition.X += XatAccEnd; velocity.X = SuvatEquations.VfromUAT(velocity.X, acceleration.X, accelerationTime); velocity.Y = SuvatEquations.VfromUAT(velocity.Y, acceleration.Y, accelerationTime); acceleration.X = 0; //get the XY coordinates from the curve after acceleration Tuple <Vector2D, Vector2D> possiblePositions2 = UsefulMathsFunctions.GetWorldspacePointAlongCurve(initialPosition, velocity, acceleration, totalJumpTime - accelerationTime, destX, float.NaN); //Finally, select which results are to be returned (max of 2 valid results are assumed) if (possiblePositions1.Item1 != null && possiblePositions1.Item2 != null && possiblePositions1.Item1 != possiblePositions1.Item2) { return(possiblePositions1); } if (possiblePositions2.Item1 != null && possiblePositions2.Item2 != null && possiblePositions2.Item1 != possiblePositions2.Item2) { return(possiblePositions2); } if (possiblePositions1.Item1 != null && possiblePositions2.Item1 != null) { return(new Tuple <Vector2D, Vector2D>(possiblePositions1.Item1, possiblePositions2.Item1)); } if (possiblePositions1.Item2 != null && possiblePositions2.Item2 != null) { return(new Tuple <Vector2D, Vector2D>(possiblePositions1.Item2, possiblePositions2.Item2)); } if (possiblePositions1.Item1 != null && possiblePositions2.Item2 != null) { return(new Tuple <Vector2D, Vector2D>(possiblePositions1.Item1, possiblePositions2.Item2)); } if (possiblePositions1.Item2 != null && possiblePositions2.Item1 != null) { return(new Tuple <Vector2D, Vector2D>(possiblePositions1.Item2, possiblePositions2.Item1)); } if (possiblePositions1.Item1 != null) { return(new Tuple <Vector2D, Vector2D>(possiblePositions1.Item1, possiblePositions1.Item1)); } if (possiblePositions1.Item2 != null) { return(new Tuple <Vector2D, Vector2D>(possiblePositions1.Item2, possiblePositions2.Item2)); } if (possiblePositions2.Item1 != null) { return(new Tuple <Vector2D, Vector2D>(possiblePositions2.Item1, possiblePositions2.Item1)); } if (possiblePositions2.Item2 != null) { return(new Tuple <Vector2D, Vector2D>(possiblePositions2.Item2, possiblePositions2.Item2)); } return(new Tuple <Vector2D, Vector2D>(null, null)); }
//Checks to see if a fall from an input coordinate TO an input coordinate would collide with a given rigidbody public bool FallCollidesWithRB(RigidBody RB, Vector2D source, Vector2D destination, Vector2D gravity) { //Return false if the rigid body is outside the widest possible fall range if (source.Y - playerRadius > RB.Shape.ComputeAABB().MAX.Y + RB.Position.Y) { return(false); } if (Math.Max(source.Y, destination.Y) + playerRadius < RB.Position.Y + RB.Shape.ComputeAABB().MIN.Y) { return(false); } if (RB.Shape.ComputeAABB().MAX.X + RB.Position.X < Math.Min(source.X, destination.X) - playerRadius) { return(false); } if (Math.Max(source.X, destination.X) + playerRadius < RB.Position.X + RB.Shape.ComputeAABB().MIN.X) { return(false); } Vector2D displacement = destination - source; //calculate the fall time (return no collision if the fall time was invalid) Tuple <float, float> timeToFall = SuvatEquations.TfromSUA(displacement.Y, 0.0f, gravity.Y); if (float.IsNaN(timeToFall.Item1) || timeToFall.Item1 < 0) { return(false); } //calculate the average horizontal acceleration needed to fall to the destination float acceleration = SuvatEquations.AfromSUT(displacement.X, 0.0f, Math.Max(timeToFall.Item1, timeToFall.Item2)); //calculate the four points where the path defined by acceleration could potentially collide with the rigidbody Tuple <float, float> timeToReach; //obtain the X positions of the sides of the rigidbody float leftmostX = (RB.Position.X + RB.Shape.ComputeAABB().MIN.X) - source.X; float righttmostX = (RB.Position.X + RB.Shape.ComputeAABB().MAX.X) - source.X; //obtain the Y positions of the top and bottom of the rigidbody float topY = (RB.Position.Y + RB.Shape.ComputeAABB().MIN.Y) - source.Y; float bottomY = (RB.Position.Y + RB.Shape.ComputeAABB().MAX.Y) - source.Y; //these coords will be estimated from the above coords float leftmostY; float righttmostY; float topX; float bottomX; Shape temp = new Circle(playerRadius); //calculate the time to reach the left side of the rigidbody timeToReach = Physics.SuvatEquations.TfromSUA(leftmostX, 0.0f, acceleration); //calculate the first Y position, check it for collision, calculate the second, check for collision leftmostY = Physics.SuvatEquations.SfromUAT(0.0f, 98, timeToReach.Item1); if (!float.IsNaN(leftmostY) && RB.Shape.IsCollided(temp, RB.Position, new Vector2D(leftmostX, leftmostY) + source)) { return(true); } leftmostY = Physics.SuvatEquations.SfromUAT(0.0f, 98, timeToReach.Item2); if (!float.IsNaN(leftmostY) && RB.Shape.IsCollided(temp, RB.Position, new Vector2D(leftmostX, leftmostY) + source)) { return(true); } //calculate the time to reach the right side of the rigidbody timeToReach = Physics.SuvatEquations.TfromSUA(righttmostX, 0.0f, acceleration); //calculate the first Y position, check it for collision, calculate the second, check for collision righttmostY = Physics.SuvatEquations.SfromUAT(0.0f, 98, timeToReach.Item1); if (!float.IsNaN(righttmostY) && RB.Shape.IsCollided(temp, RB.Position, new Vector2D(righttmostX, righttmostY) + source)) { return(true); } righttmostY = Physics.SuvatEquations.SfromUAT(0.0f, 98, timeToReach.Item2); if (!float.IsNaN(righttmostY) && RB.Shape.IsCollided(temp, RB.Position, new Vector2D(righttmostX, righttmostY) + source)) { return(true); } //calculate the time to reach the top of the rigidbody timeToReach = Physics.SuvatEquations.TfromSUA(topY, 0.0f, 98); //calculate the first X position, check it for collision, calculate the second, check for collision topX = Physics.SuvatEquations.SfromUAT(0.0f, acceleration, timeToReach.Item1); if (!float.IsNaN(topX) && RB.Shape.IsCollided(temp, RB.Position, new Vector2D(topX, topY) + source)) { return(true); } topX = Physics.SuvatEquations.SfromUAT(0.0f, acceleration, timeToReach.Item2); if (!float.IsNaN(topX) && RB.Shape.IsCollided(temp, RB.Position, new Vector2D(topX, topY) + source)) { return(true); } //calculate the time to reach the bottom of the rigidbody timeToReach = Physics.SuvatEquations.TfromSUA(bottomY, 0.0f, 98); //calculate the first X position, check it for collision, calculate the second, check for collision bottomX = Physics.SuvatEquations.SfromUAT(0.0f, acceleration, timeToReach.Item1); if (!float.IsNaN(bottomX) && RB.Shape.IsCollided(temp, RB.Position, new Vector2D(bottomX, bottomY) + source)) { return(true); } bottomX = Physics.SuvatEquations.SfromUAT(0.0f, acceleration, timeToReach.Item2); if (!float.IsNaN(bottomX) && RB.Shape.IsCollided(temp, RB.Position, new Vector2D(bottomX, bottomY) + source)) { return(true); } return(false); }
//Checks to see if a jump from source to destination would collid with the provided rigidbody public bool JumpCollidesWithRB(RigidBody RB, Vector2D source, Vector2D destination, Vector2D gravity) { //Get the max Y displacement float maxYRange = SuvatEquations.SFromUVA(jumpInitialVelocity, 0.0f, gravity.Y); //Return false if the rigid body is outside the widest possible jump range if (maxYRange + source.Y - playerRadius > RB.Shape.ComputeAABB().MAX.Y + RB.Position.Y) { return(false); } if (Math.Max(source.Y, destination.Y) + playerRadius < RB.Position.Y + RB.Shape.ComputeAABB().MIN.Y) { return(false); } if (RB.Shape.ComputeAABB().MAX.X + RB.Position.X < Math.Min(source.X, destination.X) - playerRadius) { return(false); } if (Math.Max(source.X, destination.X) + playerRadius < RB.Position.X + RB.Shape.ComputeAABB().MIN.X) { return(false); } Vector2D displacement = destination - source; //calculate the jump's acceleration duration, and total duration float accelerationTime = GetJumpFromSourceToDest(source, destination, gravity); float totalJumpTime = GetTotalJumpDuration(source, destination, gravity); //if the jump is not valid, return that it does not collide with the rigidbody if (float.IsNaN(totalJumpTime) || totalJumpTime < 0) { return(false); } //calculate the 8 points that could potentially intersect with the box (1 before and 1 after the jump's apex) Tuple <Tuple <Vector2D, Vector2D, Vector2D, Vector2D>, Tuple <Vector2D, Vector2D, Vector2D, Vector2D> > boxCollisionPoints = GetBoxCollisionPoints(RB, source, accelerationTime, totalJumpTime, gravity); if (boxCollisionPoints == null) { return(true); } //check to see if the player would collide with the rigidbody at any of the calculated positions Shape temp = new Circle(playerRadius); if (RB.Shape.IsCollided(temp, RB.Position, boxCollisionPoints.Item1.Item1)) { return(true); } if (RB.Shape.IsCollided(temp, RB.Position, boxCollisionPoints.Item1.Item2)) { return(true); } if (RB.Shape.IsCollided(temp, RB.Position, boxCollisionPoints.Item1.Item3)) { return(true); } if (RB.Shape.IsCollided(temp, RB.Position, boxCollisionPoints.Item1.Item4)) { return(true); } if (RB.Shape.IsCollided(temp, RB.Position, boxCollisionPoints.Item2.Item1)) { return(true); } if (RB.Shape.IsCollided(temp, RB.Position, boxCollisionPoints.Item2.Item2)) { return(true); } if (RB.Shape.IsCollided(temp, RB.Position, boxCollisionPoints.Item2.Item3)) { return(true); } if (RB.Shape.IsCollided(temp, RB.Position, boxCollisionPoints.Item2.Item4)) { return(true); } return(false); }
//// Each jump is an initial jump impulse, followed by a period of acceleration, then a period without acceleration //// This method calculates the length of time the player must accelerate for in order for a jump from "source" to reach "destination" //// The returned value is positive for acceleration to the right, and negative for acceleration to the left public float GetJumpFromSourceToDest(Vector2D source, Vector2D destination, Vector2D gravity) { Vector2D displacement = destination - source; float horizontalVelocity = playerBody.LinearVelocity.X; float horizontalAcceleration = GetHorizontalAcceleration(); float direction = 1; //if the destination is to the left of the source, reverse the velocity, displacement, and direction //This is intended to reduce the equation to a question of accelerate vs decelerate if (displacement.X < 0) { displacement.X *= -1; horizontalVelocity *= -1; direction *= -1; } float timeToReachDest = GetTotalJumpDuration(source, destination, gravity); //If the player is currently travelling in the correct direction if (horizontalVelocity > 0) { //if no acceleration is applied, will the player overshoot the mark? float naturalDistance = SuvatEquations.SfromUAT(horizontalVelocity, 0.0f, timeToReachDest); if (naturalDistance > displacement.X) { //if yes, the player will need to decelerate direction *= -1; horizontalAcceleration *= -1; //If decelerating for the full duration of the jump still results in overshooting, then the jump cannot be made float minimumDistance = SuvatEquations.SfromUAT(horizontalVelocity, horizontalAcceleration, timeToReachDest); if (minimumDistance > displacement.X) { return(float.NaN); } //calculate the amount that will be overshot float simulatedDisplacement = naturalDistance - displacement.X; //find the distance undershot from maximum deceleration float undershoot = SuvatEquations.SfromUAT(0.0f, horizontalAcceleration * -1, timeToReachDest) - simulatedDisplacement; //find the time it takes to travel that distance float timeSpentUndershooting = SuvatEquations.TfromSUA(undershoot, 0.0f, horizontalAcceleration * -1).Item1; //the time spent decelerating is equal to the total jump time minus the undershoot time return((timeToReachDest - timeSpentUndershooting) * direction); } } //get the distance overshot at maximum acceleration float overshoot = SuvatEquations.SfromUAT(horizontalVelocity, horizontalAcceleration, timeToReachDest) - displacement.X; //calculate the time it takes to travel that distance float timeSpentOvershooting = SuvatEquations.TfromSUA(overshoot, 0.0f, horizontalAcceleration).Item1; //the time spent accelerating is equal to the total jump time minus the overshoot time return((timeToReachDest - timeSpentOvershooting) * direction); }