3
\$\begingroup\$

In a 2d game, doing collisions with the edges of your screen is easy. Your textures are a 1:1 mapping of your screen resolution because your verities are too.. if your x position is > than ( screen width / 2 ) then you have hit the edge of your screen and shouldn't be able to escape it.

How do you solve the same problem once your game is in 3d coordinates?

I've tried the following but I'm missing something, the output is far too large. I'm expecting a number like 6.0 instead I'm getting 41.0 because I'm multiplying 25 or so by 1.5

float Window::width = 1920.0f;
float Window::height = 1080.0f;

float PlayingFrame::cameraPosition = -1.0f;
float PlayingFrame::fov = 45;
float PlayingFrame::defaultPlayDistance = 25.0f; // how far back my player is pushed into the frustrum... mainly for scaling.
float PlayingFrame::defaultFrameWidth = 0.0f; // I want to know how far my scaled player can go left / right until he hits the edge of the viewable screen
float PlayingFrame::defaultFrameHeight = 0.0f; // same as above but with top / bottom
float PlayingFrame::nearFrustrum = 1.0f;

float PlayingFrame::GetWorldSpaceHeight( float distance )
{
    float a = PlayingFrame::nearFrustrum + abs(PlayingFrame::cameraPosition) + distance;
    float o = 1.0f + tan(PlayingFrame::fov * 0.5);
    return a * o;
}

float PlayingFrame::GetWorldSpaceWidth(float distance)
{
    float a = PlayingFrame::nearFrustrum + abs(PlayingFrame::cameraPosition) + distance;
    float o = 1.0f + tan(PlayingFrame::fov * 0.5);
    return a * o;
}

Fyi, distance coming in as a parameter is actually set to 25.0f at the moment, I was trying to think ahead

enter image description here

It may help to see the actual problem I'm having... I basically want 2d characters in a 3d world so I can do cool rotation things.. but I cannot figure out how to know when my character has hit the edge of the viewable screen:

https://www.youtube.com/watch?v=A-jcnFfLu6c

enter image description here


I'm going to tell this story more thoroughly:

I originally made a 2D game. I had vertices for my background filling up 1920 x 1200:

const VERTEX World::vertices[ 4 ] = {
    { -960.0f, -600.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f },    // side 1 screen coordinates centred
    { 960.0f, -600.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f },
    { -960.0f, 600.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f },
    { 960.0f, 960.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f }
};

I applied Orthographic projections to my vertices and obviously it came out lovely on my screen! my 1920 x 1200 texture mapped perfect to my full screen app. beautiful.

Same goes with all of my players and enemies etc, I had vertices like:

const VERTEX Player::vertices[ 4 ] = {
    { -64.0f, -32.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f },    // side 1 screen coordinates centred
    { 64.0f, -32.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f },
    { -64.0f, 32.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f },
    { 64.0f, 32.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f }
};

this image was 128 x 64... and so was my texture, so it mapped perfectly... it fit on the screen as if it were 1:1 mapped... spot on!

The problem

I wanted to make my game 3D... so I now applied a perspective fov left hand projection and a camera... rather than the orthographic projection which allowed it to be 2D:

I set up a camera like so:

XMVECTOR vecCamPosition = XMVectorSet( 0.0f, 0.0f, PlayingFrame::cameraPosition, 0 );
XMVECTOR vecCamLookAt = XMVectorSet( 0.0f, 0.0f, 0.0f, 0.0f );
XMVECTOR vecCamUp = XMVectorSet( 0.0f, 1.0f, 0.0f, 0.0f );
matView3D = XMMatrixLookAtLH( vecCamPosition, vecCamLookAt, vecCamUp );

I set up a projection like so:

matProjection = XMMatrixPerspectiveFovLH(
    XMConvertToRadians( PlayingFrame::fov ),                              // the field of view
    Window::width / Window::height,  // aspect ratio
    PlayingFrame::nearFrustrum,                                                           // the near view-plane
    100.0f );                                                        // the far view-plan

I did not change my vertices but I added a z of 1.0f to my objects with just a simple translation... The issue (and I guess you already realise this) was that my textures / vertices were HUGE... I either had to scale all of my objects by some roughly correct size with a scaling transformation, or change my vertices (and this is what I did in the end) to something more like this:

const VERTEX Player::vertices[ 4 ] = {
    { -1.0f, -0.5f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f },    // side 1 screen coordinates centred
    { 1.0f, -0.5f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f },
    { -1.0f, 0.5f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f },
    { 1.0f, 0.5f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f }
}

Both choices are killing me. Choice 1 meant I had to scale all of my collisions aswell, and both choice 1 and 2 meant I had to estimate how scaled they should be. so much so that my original 1920 x 1200 background I put back into orthographic projection coz I could not get it to scale well...

The last and most frustrating issue is that I have NO IDEA how to calculate where the borders of my screen are...

What is the usual approach to this?

\$\endgroup\$
  • \$\begingroup\$ Width and height of the screen don't change , if I understand you correctly. \$\endgroup\$ – concept3d Jan 29 '14 at 12:29
  • \$\begingroup\$ Not literally, but if my character has been pushed 25.0f units into z.. and my camera is -1.0f units back from the near frustrum that is 1.0f unit forward into z... and I have a 45 degree field of view, I'm going to see more on the left and right of the screen than just 1920 (normalised to -1.0 to 1.0) and 1080 ( normalized to -1.0 to 1.0 )... it's going to have this triangle added to the side (ill add a pic above in relation to this comment) \$\endgroup\$ – Jimmyt1988 Jan 29 '14 at 12:31
  • \$\begingroup\$ I have added an image to the end, hope that makes sense.. it may also be wrong, but it's how I'm assuming it to be. Once you add projection into the mix it seems to normalise the coordinates... thus 1920 wide would be massively off of the screen because at 0,0,0 left is now -1.0 and right is now 1.0 \$\endgroup\$ – Jimmyt1988 Jan 29 '14 at 12:40
  • \$\begingroup\$ p.s I just called it framewidth.. I'm sure it has a proper name, but its. After projection and moving player 25.0f back, how far can the player go before hitting the "side" of the "viewable screen" \$\endgroup\$ – Jimmyt1988 Jan 29 '14 at 12:43
  • \$\begingroup\$ Are you making a 2D game with depth ? if so, and you want to perform 2D collisions in 3D I suggest you use world coordinates and set the Z as constant for each object depending on its width. \$\endgroup\$ – concept3d Jan 29 '14 at 12:45
1
\$\begingroup\$

you can try to project the screen borders back into the scene. In rendering projection matrices are invertable, because depth isn't thrown away. so you can use (matProjection*matCamera)⁻¹*v to project a point from screen coordinates back to world coordinates. the vertices [1 -1 0 1] [1 1 0 1] [1 1 1 1] [1 -1 1 1] are the quad that is on the right border of the screen. used in the formula above and you a a quad in world coordinates that you can use for collision.

\$\endgroup\$
  • \$\begingroup\$ I'm not sure I understand what you mean, can you see my OP I added a whole chunk of annoying text which may help you to understand my issue. #virtualbeers all around \$\endgroup\$ – Jimmyt1988 Jan 31 '14 at 12:37
  • \$\begingroup\$ as I mentioned, projection is invertable and [1 1 depth]' is the bottom right pixel in your screen (' is transpose), depth you have to define. In extended coordinate system you get [1 1 depth 1]'. multiplying (matProjection*matCamera)⁻¹ to that coordinate will give your [x y z w]', divide by w and you get [x/w y/w z/w 1]. This is now a world coordinate. You can use that for collision. \$\endgroup\$ – Arne Jan 31 '14 at 16:34
  • \$\begingroup\$ Thanks for your ongoing help. I'm going to strip everything I have out so I can understand each step I must take here. Will keep you updated. \$\endgroup\$ – Jimmyt1988 Jan 31 '14 at 18:46
1
\$\begingroup\$

Use the projection matrix like that:

D3DXMatrixPerspectiveFovLH( &matProjPerPlayer, D3DXToRadian( (FOV) ), 
                     CanvasX / CanvasY , Near, Far );

And it will get the vector to the border, if camera looks at the z coordinate

D3DXVECTOR3 v;
v.x = 1 / matProjPerPlayer._11 ;
v.y = 1 / matProjPerPlayer._22 ;
v.z = 1 / matProjPerPlayer._33 ;

I think that you can multiply with camera object matrix to calculate the width of field of view of the camera. (And Multiply with your distance.)

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.