Greetings.
I am currently working on a game in which I will use trajectory prediction on 2D gameObjects using 2D Circle Colliders, in order to get an AI to intercept a projectile.
I’ve made a pretty simple trajectory prediction algorithm, based on looping this pseudocode
// FixedUpdate simulation : Apply projectile curve effect
// Physics simulation :
// Check for collision
// |- If collision :
// | |- Set position to collision point (remember travelled distance)
// | |- Set new velocity vector according to projectile parameters (custom bounce)
// | |- Set new curve effect according to projectile parameters
// | '- Travel along new velocity vector (remaining travel distance for this tick)
// '- Else (no collision) :
// '- Travel along current velocity vector
Actual code can be found at the bottom of this post as it is quite long.
The problem I encountered is that my prediction algorithm is based on simulating position, velocity and curve of the projectiles, and as such, does not take their collider radius into account as is.
When using it to predict trajectory, even though it allows me to visualize the custom behaviour of my projectiles, the collision point is wrong, as a larger radius projectile will collide “sooner” than the prediction and thus make it inexact.
Example : http://puu.sh/l0LEJ/58280476d4.gif
I’ve been trying a few different ways to take the collider radius into account, be it during the raycast, or during the repositionning relative to collision point, but didn’t manage to achieve satisfactory results.
I would be very grateful if you could assist me in solving this problem, or point me to something I might have missed or overlooked in my implementation.
List<Vector2> PredictTrajectory(ProjectileScript projectileScript, int cycles)
{
List<Vector2> projectileInterceptPoints = new List<Vector2>();
Vector2 projectilePosition = projectileScript.GetPosition();
Vector2 projectileVelocity = projectileScript.GetVelocity();
float projectileCurve = projectileScript.GetCurve();
ProjectileInfo projectileInfo = projectileScript.GetProjectileInfo();
for (int i = 0; i < cycles; i++)
{
// FixedUpdate simulation : Apply projectile curve effect
if (projectileCurve != 0)
projectileVelocity = AddAngleToVector2(projectileVelocity, projectileCurve);
// Physics simulation :
// Check for collision
// |- If collision :
// | |- Set position to collision point (remember travelled distance)
// | |- Set new velocity according to projectile parameters (custom bounce)
// | |- Set new curve according to projectile parameters
// | '- Travel along new velocity vector (remaining travel distance for this tick)
// '- Else (no collision) :
// '- Travel along current velocity vector
RaycastHit2D hit;
hit = Physics2D.Raycast(projectilePosition, projectileVelocity.normalized, (projectileVelocity.magnitude * Time.fixedDeltaTime), 1 << LayerMask.NameToLayer("Walls"));
if (hit.collider != null)
{
// Get distance to collision point and remaining distance
// TODO take into account circle collider radius
float distanceToCollisionPoint = Vector2.Distance(projectilePosition, hit.point);
float remainingTravelDistance = (projectileVelocity.magnitude * Time.fixedDeltaTime) - distanceToCollisionPoint;
// Set position to collision point
projectilePosition = hit.point;
// Set new velocity according to projectile parameters
if (hit.transform.tag == "BottomWall") // UNITY ANSWERS : Other cases omitted
projectileVelocity = projectileVelocity.magnitude * (GetVector2FromAngle(projectileInfo.customBounceAngles.BottomWall));
// Set new curve according to projectile parameters
if (hit.transform.tag == "BottomWall")
projectileCurve = projectileInfo.customBounceCurves.BottomWall; // UNITY ANSWERS : 0° in this case
// Travel along new velocity vector (remaining travel distance for this tick since collision)
projectilePosition += projectileVelocity.normalized * remainingTravelDistance;
}
else
{
// Travel along current velocity vector
projectilePosition += projectileVelocity * Time.fixedDeltaTime;
}
projectileInterceptPoints.Add(projectilePosition);
}
return projectileInterceptPoints;
}