Sunday, May 3, 2015

linear algebra - Trying to use 3D vectors for a 2D ping pong game


I am trying to setup a virtual ping pong table, and am getting a little stuck in trying to understand how to do 3D vector math and render it in 2D.


So far, my plan is for the ball: X = up/down, Y = left/right, and Z = forward/backwards


Image of the desired perspective


So as a simple start, I am just trying to move the ball forward:


ball.vel.x = 0;
ball.vel.y = 0;

ball.vel.z = 1;

in my update function:


ball.position.x += (ball.vel.x / ball.vel.z) * delta;
ball.position.y += (ball.vel.y / ball.vel.z) * delta;

Which obviously makes no sense since 0 / anything is 0.. my ball will never move forward... So I am fundamentally missing something here. What do I need to do so that my Z velocity will actually move the ball's position?



Answer



First, I'd change up your coordinate system, because it doesn't match common game conventions and that's likely to cause friction when you try to learn from other answers/tutorials out there.


So, I'd propose the following left-handed scheme:




  • x+ is the lateral axis, pointing rightward on your screen

  • y+ is the vertical axis, pointing upward on your screen

  • z+ is the depth axis, pointing diagonally "into" your screen, away from the viewer


Next, we'll track the ball's position AND velocity in 3D (in what we call worldspace), using all 3 axes.


eg.


ball.position.x += ball.velocty.x * deltaTime;
ball.position.y += ball.velocty.y * deltaTime;
ball.position.z += ball.velocty.z * deltaTime;


or if we define vector addition and scalar multiplication on our vector types, we can write this more concisely as ball.position += ball.velocity * deltaTime


This makes it much easier to check things like "did the ball hit the table?" - let's say our table spans -10 to +10 on the x axis, and -5 to 5 on the z axis, at a height of y = 0. Then our bounce check can look like...


if(ball.position.y < table.height /*0*/ ) {
if(Abs(ball.position.x) <= table.length /*10*/
&& Abs(ball.position.z) <= table.width /*5*/) {
// In bounds! Let's bounce the ball,
// reflecting its position & velocity across the table's plane.
ball.position.y = 2 * table.height - ball.position.y;
ball.velocity.y *= -1;

} else {
// Ball fell out of bounds. Score a point for someone.
OutOfBounds();
}
}

A more accurate collision method would check the line between the previous & current position of the ball, to check if we clipped the table on our way past the edge, but this shows how simple the math can be in worldspace rather than trying to infer depth & test against a parallellogram in screenspace.


When we want to convert a 3D worldspace position into 2D screenspace for display, we use a projection. Here's an example of a simple projection you could use:


Vector2 ProjectToScreenPixels(Vector3 worldspace) {
Vector2 screenSpace = worldSpace.x * screenX

+ worldSpace.y * screenY
+ worldSpace.z * screenZ;

screenSpace += originOffset;

/* equivalent to:
screenSpace.x = worldSpace.x * screenX.x
+ worldSpace.y * screenY.x
+ worldSpace.z * screenZ.x
+ originOffset.x;


screenSpace.y = worldSpace.x * screenX.y
+ worldSpace.y * screenY.y
+ worldSpace.z * screenZ.y
+ originOffset.y;
*/

return screenSpace;
}


We use a few configuration parameters to control the projection. These three decide which way on the screen each axis of our 3D world should point, and how it should be scaled or foreshortened:


Vector2 screenX = new Vector2(5, 0);  // +1 unit of x = 5 pixels right on-screen
Vector2 screenY = new Vector2(0, 5); // +1 unit of y = 5 pixels up on-screen
Vector2 screenZ = new Vector2(2, 2); // +1 unit of z = ~3 pixels diagonally

This is roughly a Cabinet projection: we keep the x & y axes perpendicular & scaled equally, and foreshorten the depth axis that lays diagonal to them. I chose it as a roughly eyeballed match for the image included in your original question.


You can use an additional originOffset vector to control where the position (0,0,0) should get drawn on your screen, which effectively pans the whole scene around to help you frame it the way you want.


No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...