0
\$\begingroup\$

I'm making a 3D game, and I'm stuck on a little experiment.

I currently have a flat plane with a free camera running around, and a sphere (really, a light approximated as a sphere) that rests above the surface of the plane at, say, 50 units up. I'm trying to move the sphere, and I can do this easily by simply adding to the position in the X and Z direction. However, I got a bit messed up because when I move the camera, pressing the left arrow might move the sphere to the right, because of the coordinates. So I'm trying to move the sphere along the X and Z dependant on where my camera is looking.

For example, if I was looking at it from behind, down on an angle, I expect the sphere to move to the camera's left along the plane, or move down towards the camera, still along the plane.

I have seemed to get left and right working with a.Position -= Vector3.Cross(((FreeCamera)camera).Forward, ((FreeCamera)camera).Up); but trying to move the sphere using just the camera's forward causes the sphere to move into or out of the plane, depending on the angle of the camera (this makes sense, because the camera's forward may be pointing into the plane).

My question is, how do I move an object left or right along a plane (remaining 50 units above the Y axis) in accordance to the camera's direction? For example, viewing the plane from its left facing towards the center, moving left should make it move towards the top of the plane if viewed from the back or to the camera's local left.

Here's two screenshots showing what I mean:

When viewing the red light from here: Starting position Pressing right should give me this. Notice how the red sphere has moved (disregard the fact the camera angle changed slightly): enter image description here

\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

Your Camera class generates a View matrix right? Multiplying a position in world space by the view matrix results in that same position being transformed into view space. Conversely, if you multiply a position in view space by the inverse of your view matrix, you'll get your position back in world space. On to your question.

Part 1) Moving light in relation to the camera

So you'd like to move the light position according to the camera's current orientation. An easy way to do this is simply to apply the transformation in view space (as opposed to world space). As an example:

Vector3 viewLightPosition = Vector3.Transform(light.Position, camera.ViewMatrix);
if(keystate.IsKeyDown(Keys.Right)) viewLightPosition += Vector3.Right * moveSpeed * dt;
light.Position = Vector3.Transform(viewLightPosition, Matrix.Invert(camera.ViewMatrix));

That takes care of the problem where movement should always be relative to the camera.

Part 2) Clamping the light back onto the correct plane

In step 1 the light was moved according to the camera, but disregarding the plane. Now it's time to fix that, i.e. you need to validate the light position to ensure that it remains at the same distance from the plane. The first thing that occurs to me would be:

  1. Project your light position into the plane.
  2. Then move it out of the plane by the desired amount along the plane's normal.

I'm not sure but I think XNA's Matrix.CreateShadow could be leveraged to simplify the first step. Once again, example:

Matrix projectionMatrix = Matrix.CreateShadow(plane.Normal, plane);
light.Position = Vector3.Transform(light.Position, projectionMatrix);
light.Position += plane.Normal * distance;

Note) Calculating the correct plane for your wall

Of course part 2 only works if you're using the correct plane. Since the Plane is usually stored as a Normal and Distance from the origin, it might be hard to come up with the correct values to fit a specific object in the world.

Luckily the Plane class also has a constructor that makes this task easy - you only need to pass it three points that lie on the plane. So to create a plane for your wall, all you'd have to do is:

Plane plane = new Plane(wall.Vertices[0], wall.Vertices[1], wall.Vertices[2]);
\$\endgroup\$
12
  • \$\begingroup\$ Thanks for your answer. On Part 2 of your answer, it does not seem to work as given (the first CreateShadow() causes it to disappear, which I assume means it fell through; the negated version gives a weird flickering) but I still have to check to make sure my plane is defined correctly (as my plane is right now a mesh). Also, the idea about moving the light out along the normal sounds like a good idea, so I'll take a look at it again a bit later. Thanks! \$\endgroup\$
    – DMan
    Dec 29, 2011 at 3:18
  • \$\begingroup\$ Let me know how it turns out, I've never used CreateShadow in this kind of situation, but logic tells me it should work, since it's just a matrix that projects points into a plane along some specified direction. And by the way, don't do this every frame. Only do it when there's user input and the position was changed. :) \$\endgroup\$ Dec 29, 2011 at 3:36
  • \$\begingroup\$ Unfortunately I could not get this to work. One issue was that it would always flicker (I tracked the position and seemed that X would invert) and after transforming it back to the plane, it wouldn't move at all. \$\endgroup\$
    – DMan
    Dec 29, 2011 at 4:56
  • \$\begingroup\$ Could you post the relevant portion of the code to pastebin or something? And is that flickering related to part 1 or part 2? I've used the procedure described in part 1 before with no problems so there shouldn't be any problem there. As for part 2, I'm really not seeing why it might fail either.. \$\endgroup\$ Dec 29, 2011 at 5:06
  • \$\begingroup\$ I'm running some tests now, will get back to you soon. Part 1 is working well. Part 2 is indeed giving the behavior you describe. Looking into it. \$\endgroup\$ Dec 29, 2011 at 5:18
0
\$\begingroup\$

Figure out the camera's rotation around the Y axis and use that with trig functions to calculate the X and Z distances:

x = cos(angle) * speed

z = sin(angle) * speed

btw an answer I gave to a different question described the same math for a slightly different situation https://gamedev.stackexchange.com/a/18395/6588

\$\endgroup\$
4
  • \$\begingroup\$ Excuse my midway-through-highschool math understanding, but I have my camera's rotation around the Y (the Yaw, in radians). Atan2 accepts (y,x). So am I trying to solve for x? If so, would Yaw=Atan2(y,x) and what does y stand for then? \$\endgroup\$
    – DMan
    Dec 28, 2011 at 3:50
  • \$\begingroup\$ oops I got my trig functions confused, that should be sine and cosine not arc-tangent. edited \$\endgroup\$
    – jhocking
    Dec 28, 2011 at 13:24
  • \$\begingroup\$ I think I'm missing something here; say if I passed something.Position += new Vector3((float)(Math.Cos(((FreeCamera)camera).Yaw)), 0, (float)(Math.Sin(((FreeCamera)camera).Yaw))) * timeConstant;, this won't take in account to the direction I pressed, even if I add/subtract it. Left/right movement seems to be working if I negate the Cos and Sin respectively, but I can't figure it out for up/down. \$\endgroup\$
    – DMan
    Dec 28, 2011 at 17:39
  • \$\begingroup\$ I still cannot get this implementation to work; at best, it moves left and right in the correct direction, but it will never move up/down no matter how I switch the signs. \$\endgroup\$
    – DMan
    Dec 29, 2011 at 4:30

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .