Move first-person camera side to side

Started by
7 comments, last by EddieK 6 years ago

Hi, I'm working on this little 3D demo game, and I have come across a problem.

I have implemented the rotation of the camera correctly, but now I want to make it so that when the virtual joystick is moved left or right, the camera would translate appropriately (think A and D buttons in FPS games on PC).

The way I did it was by rotating the camera's directional vector 90 degrees and then translating the camera's position by that directional vector multiplied by joystick's value and movement speed.

It works as expected when the player is facing forward, but if it's facing up or down, the movement becomes slowed down.

Here's the bit of code I'm talking about


        // this is the part that doesn't work correctly
        cameraDirection.rotate(0,90,0);
        cameraPosition.x += cameraDirection.x * game.joystickSystem.getX1() * CAMERA_SPEED*deltaTime;
        cameraPosition.z += cameraDirection.z * game.joystickSystem.getX1() * CAMERA_SPEED*deltaTime;

        // rotate the camera
        cameraDirection.set(0,0,-1);
        cameraDirection.rotate(cameraRotation.x, cameraRotation.y, 0);

        cameraRotation.x += game.touchDelta.y;
        cameraRotation.y += game.touchDelta.x;
        // keep camera up/down rotation between -85 and 85 degrees
        if(cameraRotation.x > 85)
            cameraRotation.x = 85;
        if(cameraRotation.x < -85)
            cameraRotation.x = -85;

        // move camera in its direction with joystick
        cameraPosition.x += cameraDirection.x * -game.joystickSystem.getY1()*CAMERA_SPEED*deltaTime;
        cameraPosition.y += cameraDirection.y * -game.joystickSystem.getY1()*CAMERA_SPEED*deltaTime;
        cameraPosition.z += cameraDirection.z * -game.joystickSystem.getY1()*CAMERA_SPEED*deltaTime;

        camera.setCenter(
                cameraPosition.x,
                cameraPosition.y,
                cameraPosition.z
        );
        // make camera look at the direction it's facing
        camera.lookAt(
                cameraPosition.x + cameraDirection.x,
                cameraPosition.y + cameraDirection.y,
                cameraPosition.z + cameraDirection.z
        );

Any ideas on how to fix this?

Thanks in advance :)

Advertisement

I think you should replace cameraDirection vector with a cameraOrientation matrix, then you have right, up and front directions already available.

Here's some code i'm using in a tool where i fly around in a first person like manner.

The main difference is that mouse movement alters euler angles, and the matrix is build from those angles instead using a lookAt.


struct Camera
{
	glm::mat4 world;
	glm::mat4 worldInverse;
	glm::mat4 projection;
	glm::vec3 euler;

	float fov;
	float firstPersonSpeed;
	float firstPersonSensibility;
	float nearClip;
	float farClip;
	float aspect;
	

	void Init ()
	{
		fov = 60.0f;
		firstPersonSpeed = 2.0f;
		firstPersonSensibility = 0.1f;
		aspect = 1.0f;
		nearClip = 0.1f;
		farClip = 1000.0f;
		
		world = glm::mat4();
		worldInverse = glm::mat4(); 
		euler = glm::vec3();
		UpdateProjection ();
	}
	
	void UpdateWorld (const glm::vec3 position, const glm::vec3 orientation)
	{
		glm::vec3 pos = position;
		euler = orientation;
		world  = glm::eulerAngleZ(orientation[2]);
		world *= glm::eulerAngleY(orientation[1]);
		world *= glm::eulerAngleX(orientation[0]);
		world[3] = glm::vec4 (pos, 1.0f);

		worldInverse = glm::inverse (world);
	}

	void MoveFirstPerson (const glm::vec3 translation, glm::vec3 rotation)
	{
		UpdateWorld (
			glm::vec3(world[3] + world * glm::vec4(translation * firstPersonSpeed, 0.0f)), 
			euler + rotation * firstPersonSensibility * 0.01f);
	}

	void UpdateProjection ()
	{
		float fovY = fov / 180.0f * float(M_PI);
		if (fov > 0.0f)
			projection = glm::perspective (fovY, aspect, nearClip, farClip);
		else
			projection = glm::ortho (fov*aspect, -fov*aspect, fov, -fov, -nearClip, farClip);
		projection[1] *= -1.f;
	}
};


void UserInput (float deltaTime)
	{
		glm::vec3 move (0,0,0);
		glm::vec3 look (0,0,0);

		if (application->KeyDown('W') || application->KeyDown(Application::KEY_UP))		move += glm::vec3 (0.0f, 0.0f, -1.0f);
		if (application->KeyDown('S') || application->KeyDown(Application::KEY_DOWN))	move += glm::vec3 (0.0f, 0.0f,  1.0f);
		if (application->KeyDown('A') || application->KeyDown(Application::KEY_LEFT))	move += glm::vec3 (-1.0f, 0.0f, 0.0f);
		if (application->KeyDown('D') || application->KeyDown(Application::KEY_RIGHT))	move += glm::vec3 ( 1.0f, 0.0f, 0.0f);
		if (application->KeyDown('E') || application->KeyDown(Application::KEY_PGUP))	move += glm::vec3 (0.0f, -1.0f, 0.0f);
		if (application->KeyDown('C') || application->KeyDown(Application::KEY_PGDOWN)) move += glm::vec3 (0.0f,  1.0f, 0.0f);

		if (application->KeyDown(Application::KEY_RBUTTON) || application->forceMouseCapture) 
		{
			look[1] = -float(application->mousePosX - application->mousePrevX);
			look[0] = -float(application->mousePosY - application->mousePrevY);
		}
		//ImGui::Text("look[0] %f", look[0]);
		if (application->KeyDown(Application::KEY_CTRL)) move *= 0.1f;
		if (application->KeyDown(Application::KEY_SHIFT)) move *= 10.0f;

		
		camera.MoveFirstPerson (move * deltaTime, look);
	}

 

1 hour ago, EddieK said:

// this is the part that doesn't work correctly cameraDirection.rotate(0,90,0); cameraPosition.x += cameraDirection.x * game.joystickSystem.getX1() * CAMERA_SPEED*deltaTime; cameraPosition.z += cameraDirection.z * game.joystickSystem.getX1() * CAMERA_SPEED*deltaTime;

To fix it as is, something like this should work:

vector up (0,1,1);

vector side  = up.Cross(cameraDirection); // perpendicular to up and direction

side.Normalize(); // make unit length so speed is not affected as you currently have

cameraPosition += side * game.joystickSystem.getX1() * CAMERA_SPEED*deltaTime;

1 hour ago, JoeJ said:

To fix it as is, something like this should work:

vector up (0,1,1);

vector side  = up.Cross(cameraDirection); // perpendicular to up and direction

side.Normalize(); // make unit length so speed is not affected as you currently have

cameraPosition += side * game.joystickSystem.getX1() * CAMERA_SPEED*deltaTime;

I tried doing is way, and I'm getting exactly same results...

I implemented it like this:


        Vector3 side = new Vector3(0,1,0); // I think you meant up vector be be (0,1,0), not (0,1,1)
        side.cross(cameraDirection); // performs cross product and puts result into "side" vector
        side.normalize();
        cameraPosition.x += side.x * game.joystickSystem.getX1() * CAMERA_SPEED*deltaTime;
        cameraPosition.z += side.z * game.joystickSystem.getX1() * CAMERA_SPEED*deltaTime;

Camera side movements still get slowed down when looking up or down.

Any other ideas? Or did I implemented it wrong?

Hmmm - can you be sure it really slows down? Maybe it's some form of optical illusion making you think so?

Haha, I am really sure it slows down. The amount of slowing down is determined by camera rotation vector's Y value. If value is -90 degrees or +90 degrees, it completely stops moving to the sides. And if I let the camera position be translated by direction vector's value, it even starts moving up and down, that's why I ignore it in the calculation. 

I don't know if any one this made any sense, but yeah... I'm stuck.

28 minutes ago, EddieK said:

And if I let the camera position be translated by direction vector's value, it even starts moving up and down, that's why I ignore it in the calculation. 

Ok, let's work this out before taking care for strafing.

I assume cameraDirection points in the direction the camera looks at, and Y is up.

So if you project it to the direction to the XZ plane and normalize, it should move always forth or back on that horizontal plane, no matter if you look up or down:

vector front = cameraDirection;

front.y = 0;

front.Normalize();

cameraPosition += front * deltaTime * 0.01; // moving at constant speed to rule out joystick

 

from that we could calculate side vector more easily:

vector side = front.Cross(vector(0,1,0));

 

If it still won't work, you should start logging numbers for front vector and it's length, cameraPosition etc. (maybe there is a serious bug in math lib, or in setCenter()... - who knows)

EDIT: wrote front.z = 0; initially - seems i have a YZ problem today :)

Never mind, I fixed it with


        cameraPosition.x += Math.cos(Math.toRadians(cameraRotation.y)) * game.joystickSystem.getX1() * CAMERA_SPEED * deltaTime;
        cameraPosition.z += Math.sin(Math.toRadians(cameraRotation.y)) * game.joystickSystem.getX1() * CAMERA_SPEED * deltaTime;

Could've sworn that I have tried it before, maybe I just forgot to convert to radians. 

Anyways, thanks for the help :)

This topic is closed to new replies.

Advertisement