6
\$\begingroup\$

I am trying to implement a camera which rotates around the world around the player. I have found many solutions online to the task of rotating an object about the origin, or about an arbitrary point. The procedure seems to be to translate the point to be rotated about to the origin, perform the rotation, translate back, then draw. I have gotten this working for rotation around the origin as well as for a fixed point. Rotation of objects around the player works as well, provided the player does not move. However, if the objects are rotated around the player by some non-zero degree, if the player moves after the rotation it causes the rotated objects to move as well. I probably have done a poor job explaining this so here's an image:

enter image description here

And here's the code for the behavior:

renderx = (Ox - Px)*cos(camAngle) - (Oy - Py)*sin(camAngle) + Px;

rendery = (Ox - Px)*sin(camAngle) + (Oy - Py)*cos(camAngle) + Py;

Where (Ox,Oy) is the actual position of the object to be rotated and (Px,Py) is the actual position of the player. Any ideas? I am using C++ with SDL2.0.

\$\endgroup\$
  • \$\begingroup\$ "Rotation of objects around the player works as well" Why do you want to rotate objects in this way? Why don't you just rotate/move the camera in relation to the player as in a "chase camera?" Example: stemkoski.github.io/Three.js/Chase-Camera.html \$\endgroup\$ – Micah Bolen Nov 2 '13 at 14:40
  • \$\begingroup\$ Rotating the camera would cause the player to be rotated and presented upside down and all the objects in the world upside down as well. That works great for top down games but this is not quite top-down. I am not sure that example applies because I think the system in it exists physically in 3 dimensions whereas I am just trying to fake it. \$\endgroup\$ – Boston Nov 2 '13 at 18:36
  • \$\begingroup\$ Sorry, I didn't actually mean to literally rotate the camera. I should have said to orbit the camera. Orbit the camera while keeping it pointed at the player. \$\endgroup\$ – Micah Bolen Nov 2 '13 at 19:08
  • 2
    \$\begingroup\$ Further, correct me if I'm wrong, but I think what you are trying to achieve is in the vein of a 2.5D game. If so, I would recommend a 3D scene and camera even if your game assets (objects/player/background detail) are flat. See also billboarding. \$\endgroup\$ – Micah Bolen Nov 2 '13 at 19:13
  • 1
    \$\begingroup\$ I will give implementing the orbiting camera a shot. Switching to 3D is something I'll do if this problem can't be solved, but to me it seems like adding a dimension of complexity is an overreaction when the only extra functionality I need out of it is having a variable pivot location. \$\endgroup\$ – Boston Nov 2 '13 at 19:49
1
\$\begingroup\$

You've already basically got the answer, your problem is the positioning of all objects is based off of the player, and when the player moves so do the objects in the world.

Instead, you need to change everything to be based off of a camera position. And when rotating the world, figure out how much the character was moved, and move the camera to compensate; this way the character doesn't look like it's moving, and the world is rotating around it.

I've whipped up a sample for you, and here it is: sketchpad.cc. Just hit play. :) In one editor I was experiencing issues in Chrome (Firefox works fine), though in this editor it seems to work okay; was having issues properly tracking the mouse with Processing.js.


Just like your function, I have a GetProjPos method, or Get Camera Space Position.

class GameCamera
{
    float   Rot;// Camera rotation
    PVector Pos;// Camera position

    void GetProjPos(PVector objectPosition)
    {
        return new PVector(
            objectPosition.x * cos(Rot) - objectPosition.y * sin(Rot) + Pos.x,
            objectPosition.x * sin(Rot) + objectPosition.y * cos(Rot) + Pos.y);
    }

All objects are drawn using this function. To handle rotation around the camera we need the player position before and after the rotation happens.

oldMouse = new PVector(pmouseX, pmouseY, 0);
newMouse = new PVector(mouseX, mouseY, 0);

oldMouseFromCenter = PVector.sub(oldMouse, cam.GetProjPos(P));
newMouseFromCenter = PVector.sub(newMouse, cam.GetProjPos(P));

if (newMouseFromCenter != oldMouseFromCenter)
{

Cross product helps us determine which side a vector is to another vector. This is used for determining which way to rotate the world, since angleBetween (which uses acos) returns a positive angle.

    sign = oldMouseFromCenter.cross(newMouseFromCenter).z >= 0 ? 1 : -1;

    oldPlayerPosition = cam.GetProjPos(P);

angleBetween uses the dot product to determine the amount of radians needed to rotate a vector to match another vector. The typical formula is like this: angle = acos(v1.dot(v2)) / (v1.length() * v2.length())

    cam.Rot += sign * PVector.angleBetween(oldMouseFromCenter, newMouseFromCenter);
    newPlayerPosition = cam.GetProjPos(P);

    deltaPlayerPosition = PVector.sub(newPlayerPosition, oldPlayerPosition);

And the amount the player moved in world position, just move the camera back by that amount so it doesn't look like the player moved.

    cam.Pos.sub(deltaPlayerPosition);
}

I'm sorry you didn't get this answer sooner, but I hope you find it useful. I know my example isn't in C++ and SDL, but you should be able to apply this easily enough.

\$\endgroup\$
0
\$\begingroup\$

I know you are using in SDL, but this will help you much, it is how it would be done in opengl.

ok let's start with the basics we have 4 coordinate systems and 3 matrices to translate between those coordinate systems. Coordinate system lower case, matrices upper case.

normalized device coordinates (ndc) Projection (P) camere space (cs) View(V) world space (ws) Model(M) object space (os)

ndc are the coordinates that opengl wants from you. Everythin beween (-1,-1) and (1,1) is visible, everything eles will be cropped out. cs are all coordinates relative to the camera (camera origin is (0,0) here). ws are coordinates in absolute world positions, so (0,0) is the origin of you world that never changes. and os are the vertices of your object, relative to the object origin (as we only use the object itself and do not process meshes, we only use (0,0) as input here). The matrices P,V,M are there to translate between those coordinate systems. They are all invertable.

eg lets take p=[0;0] or for opengl it would be [0;0;0;1] (extended coordinate system) and multiply it with P*V*M*p. What comes out is the position of your player, in screen coordinates [-1;-1;...] is bottom left, [1;1;...] is top right corner. With (P*V*M)^-1 you can invert this process and take screen coordinates and make them relative to your object (if you know the dapth). So each object has it's own M, but V and P are constant for the entire frame

M encodes the position and orientation of your object

V encodes the position and orientation of your camera

P encodes the field of view and wheather you want orthogonal projection or not

if you understand this method, it will much easier for you to find out, that all you have to do is to apply a translation-around-an-axis matrix to your camera coordinates, and then extract V from this new rotated camera, and everything should work as expected.

To use Matrices in C++, you can use either Eigen, or glm, both do their job well.

\$\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.