Hello,
So I've been looking into the kinematic equations for the purposes of projectile motion. I specifically needed to hit a specific point at a specific time, so I am using the formula listed under "lateral speed" on this page. I am solving for the initial velocity and acceleration values.
My game uses a fixed timestep of 60 ticks per second, and I am integrating my physics using semi-implicit Euler. By all accounts this should work fine on its own, yet it does not. If I were to plug in the found values into the kinematic equation x = x0 + vt + 1/2at^2 (I apologize for not using LaTeX; it doesn't show a preview when writing this comment), it would land precisely where I want it to. However, if I evaluate it per frame, I get significantly off. Here's the code for how I launched the projectile, including the code I was using to test where it would be during its flight time:
float t = 15.0f; // t is in frames
float targetHeight = -250.0f;
float yAcceleration = 0.0f;
glm::vec3 diff = targetPosition - initialPosition;
glm::vec3 airDisplacement = glm::dot(diff, WORLD_UP) * WORLD_UP;
glm::vec3 groundDisplacement = diff - airDisplacement;
glm::vec3 xVel = groundDisplacement / t;
//got the following from https://www.forrestthewoods.com/blog/solving_ballistic_trajectories/,
//with slight adjustment to account for a negated y coordinate
float a = glm::dot(initialPosition, WORLD_UP);
float b = a + targetHeight;
float c = glm::dot(targetPosition, WORLD_UP);
yAcceleration = -4 * (a - (2 * b) + c) / (t * t);
float yVal = ((3 * a) - (4 * b) + c) / (t);
glm::vec3 yVel = WORLD_UP * yVal;
glm::vec3 newVelocity = xVel + yVel;
glm::vec3 accelerationVector = WORLD_UP * yAcceleration;
//Testing code to see what the position should be at each frame
//The first loop is how it would look according the equation of motion,
//the second is pretty much how it would work in-game
glm::vec3 priorPrediction = initialPosition;
float testEnd = t;
for (float testT = 1.0f; testT <= testEnd; testT++) {
glm::vec3 predictedPosition = initialPosition + (newVelocity * testT) + (0.5f * accelerationVector) * (testT * testT);
glm::vec3 predictionDiff = predictedPosition - priorPrediction;
printf("Predicted Position in %f frames: (%f, %f, %f)\n", testT, predictedPosition.x, predictedPosition.y, predictedPosition.z);
priorPrediction = predictedPosition;
}
priorPrediction = initialPosition;
glm::vec3 v = newVelocity;
for (float testT = 1.0f; testT <= testEnd; testT++) {
v += accelerationVector;
glm::vec3 predictedPosition = priorPrediction + v;
glm::vec3 predictionDiff = predictedPosition - priorPrediction;
printf("Predicted (Step) Position in %f frames: (%f, %f, %f)\n", testT, predictedPosition.x, predictedPosition.y, predictedPosition.z);
priorPrediction = predictedPosition;
}
When the step integrated solution was evaluated, the ground position was always where it was supposed to be (barring some floating point error, not even all that noticeable). The position in the air, however seemed as though it was too low; specifically as though it was arriving one frame prior to when it should arrive. So, I edited the calculation of yVal above as follows:
float offsetT = t + 1;
yAcceleration = -4 * (a - (2 * b) + c) / (offsetT * offsetT);
float yVal = ((3 * a) - (4 * b) + c) / (offsetT);
And what do you know, now it works exactly how I want. It arrives at the target position in exactly 15 frames as I intended. I should be happy with this, but I am hesitant to accept a solution if I don't understand why it works, especially since it actually doesn't line up in the kinematic equation.
So that's my question; why is it that the y velocity calculation seems to be one frame too fast in this implementation?