I use Android Studio with Java, LibGDX and Box2D.
I need to visualize the predictions of the movement of the balls, at least until the next two collisions.
Now the code works but the projections are not precise, as you can see in the image where they move while the ball is reaching the first collision point.
So, how do you make them as accurate as possible?
Thanks
public static void debugBallNextCollisions(MyGame game, World world, Viewport viewport, Body ball, float maxPredictionDistance, int qtyRecursions) {
Array<Vector2[]> raysVector2 = getNextCollisionsRay(world, ball, maxPredictionDistance, qtyRecursions);
game.shapeRenderer.end();
game.shapeRenderer.setProjectionMatrix(viewport.getCamera().combined);
Gdx.gl.glEnable(GL20.GL_BLEND);
game.shapeRenderer.begin(ShapeRenderer.ShapeType.Line);
for(Vector2[] vector2s : new Array.ArrayIterator<>(raysVector2)) {
game.shapeRenderer.line(vector2s[0].x, vector2s[0].y, vector2s[1].x, vector2s[1].y);
}
game.shapeRenderer.end();
}
public static Array<Vector2[]> getNextCollisionsRay(World world, Body ball, float maxPredictionDistance, int qtyRecursions) {
Array<Vector2[]> collisionRays = new Array<>();
Vector2 velocity = ball.getLinearVelocity();
if (!velocity.isZero()) {
Vector2 vector2start = new Vector2(ball.getPosition());
Vector2 vector2End = new Vector2(vector2start).add(velocity.nor().scl(maxPredictionDistance));
getNextCollisionsRayRecursive(world, vector2start, vector2End, velocity, maxPredictionDistance, String.valueOf(ball.getUserData()), collisionRays, qtyRecursions);
}
return collisionRays;
}
private static void getNextCollisionsRayRecursive(World world, Vector2 startPos, Vector2 endPos, Vector2 velocity, float maxPredictionDistance, String bodyId, Array<Vector2[]> collisionRays, int qtyRecursions) {
DetailedRayCastCallback callback2 = new DetailedRayCastCallback(startPos, endPos, bodyId);
Vector2 vector2start2 = new Vector2(startPos);
Vector2 vector2End2 = new Vector2(vector2start2).add(velocity.nor().scl(maxPredictionDistance));
world.rayCast(callback2, vector2start2, vector2End2);
if (callback2.closestHit != null) {
CollisionInfo hit2 = callback2.closestHit;
collisionRays.add(new Vector2[]{new Vector2(vector2start2.x, vector2start2.y), new Vector2(hit2.collisionPoint.x, hit2.collisionPoint.y)});
if(--qtyRecursions > 0) {
Vector2 endPos2 = new Vector2(hit2.reflectedVector).scl(maxPredictionDistance).add(hit2.collisionPoint);
getNextCollisionsRayRecursive(world, hit2.collisionPoint, endPos2, hit2.reflectedVector, maxPredictionDistance, bodyId, collisionRays, qtyRecursions);
}
}
else {
Vector2 velocityEnd2 = new Vector2(vector2start2).add(velocity);
collisionRays.add(new Vector2[]{new Vector2(vector2start2.x, vector2start2.y), new Vector2(velocityEnd2.x, velocityEnd2.y)});
}
}
private static class DetailedRayCastCallback implements RayCastCallback {
public CollisionInfo closestHit;
private Vector2 position;
private Vector2 velocity;
private String bodyId;
public DetailedRayCastCallback(Vector2 position, Vector2 velocity, String bodyId) {
this.position = new Vector2(position);
this.velocity = new Vector2(velocity);
this.bodyId = bodyId;
}
@Override
public float reportRayFixture(Fixture fixture, Vector2 point, Vector2 normal, float fraction) {
if (fixture.getBody().getUserData() == bodyId) return -1;
Vector2 reflectedVelocity = calculateReflection(velocity, normal);
closestHit = new CollisionInfo(position, point, normal, reflectedVelocity, fixture);
return fraction;
}
}
private static class CollisionInfo {
public Vector2 initialPoint;
public Vector2 collisionPoint;
public Vector2 surfaceNormal;
public Vector2 reflectedVector;
public CollisionInfo(Vector2 initial, Vector2 collision, Vector2 normal, Vector2 reflected, Fixture hitFixture) {
this.initialPoint = new Vector2(initial);
this.collisionPoint = new Vector2(collision);
this.surfaceNormal = new Vector2(normal);
this.reflectedVector = new Vector2(reflected);
}
}
private static Vector2 calculateReflection(Vector2 incomingVector, Vector2 surfaceNormal) {
float dotProduct = incomingVector.dot(surfaceNormal);
Vector2 reflection = new Vector2(incomingVector).sub(surfaceNormal.scl(2 * dotProduct));
return reflection;
}
debugBallNextCollisions(game, world, viewport, ball.body, WORLD_HEIGHT, 5);