OpenGL first person realistic keyboard movement - c++

So I'm making this FPS game where I want to move forward, backward, left and right with keyboard input and look around with the camera like in a real FPS-game like Battlefield. The camera movement in combination with the keyboard input works great, but now my camera can fly around. And I just want to be able to stay on the "ground" and move forward, backward, left and right while looking up or down like in a game like battlefield without flying around (like no-clip does). Now If I look down or up and press forward on my keyboard I can "fly", but I don't want this to happen.
A friend of my suggested to use something like this to go forward.
Position += glm::vec3(glm::cos(Yaw), glm::sin(Yaw), 0) * velocity
instead of:
Position += Front * velocity;
But I don't fully understand how this would work?
This is the current keyboard input code:
void Camera::ProcessKeyboard(Camera_Movement direction, float deltaTime)
{
float velocity = MovementSpeed * deltaTime;
if (direction == FORWARD)
Position += Front * velocity;
if (direction == BACKWARD)
Position -= Front * velocity;
if (direction == LEFT)
Position -= Right * velocity;
if (direction == RIGHT)
Position += Right * velocity;
}
Tips or help would be appreciated!
Yes this code comes from LearnOpenGL.com
What we are doing here is trying to simulate movement by moving all objects in the scene in the reverse direction, giving the illusion that we are moving.

To make the camera stay on the ground, simply don't move it up and down.
Yeah, the part where the camera moves up and down? You're the one who wrote that code. Delete it.
You didn't say where this Front vector comes from. It comes from a place that is taking pitch and yaw into account. Delete the pitch part.
Your friend's suggestion glm::vec3(glm::cos(Yaw), glm::sin(Yaw), 0) is a way to calculate Front using only yaw.
As your game gets more advanced you may also implement jumping and gravity, so the player can go up and down to a limited extent.

I want to thank everyone for helping me out! I understand better how everything works now. I want to move in the x-z plane and y must be zero (yes I chose the Y-axis as "up" or "the sky"), because I don't want the camera to move up or down. So when I go forward I only want to change the x and z parameter of the glm vector!
void Camera::ProcessKeyboard(Camera_Movement direction, float deltaTime)
{
float velocity = MovementSpeed * deltaTime;
if (direction == FORWARD) {
// glm::vec3(X,Y,Z)!!! we only want to change the X-Z position
Position += glm::vec3(glm::cos(glm::radians(Yaw)), 0, glm::sin(glm::radians(Yaw))) * velocity; //Y is not affected, Y is looking up
}
if (direction == BACKWARD) {
// glm::vec3(X,Y,Z)!!! we only want to change the X-Z position
Position -= glm::vec3(glm::cos(glm::radians(Yaw)), 0, glm::sin(glm::radians(Yaw))) * velocity; //Y is not affected, Y is looking up
}
if (direction == LEFT) {
Position -= Right * velocity;
}
if (direction == RIGHT) {
Position += Right * velocity;
}
}
Now everything works great!

Related

2d tilemap collision for SFML

I am currently working on a 2D rpg game with SFML and I am trying to make the player collide with certain tiles. I have gotten to the point where the player can collide with the tiles that I want as well as slide along a wall with the following code:
sf::Vector2f movement(0.f, 0.f);
if (isMovingUp)
movement.y -= 1.f;
if (isMovingDown)
movement.y += 1.f;
if (isMovingLeft)
movement.x -= 1.f;
if (isMovingRight)
movement.x += 1.f;
sf::Vector2f oldpos = Player.getPosition();
sf::Vector2f newpos = Player.getPosition() + movement*speed*dt.asSeconds();
Player.setPosition(newpos); // the player is an sf::sprite that is 16*24px
for (int i = 0; i < tiler.solids.size(); i++) // each solid is an sf::FloatRect that is 16*16px
{
if (Player.getPosition().x + 16 >= tiler.solids[i].left && Player.getPosition().x <= tiler.solids[i].left + tiler.solids[i].width && Player.getPosition().y + 24 >= tiler.solids[i].top && Player.getPosition().y <= tiler.solids[i].top + tiler.solids[i].height) // if the player is touching the tile
{
Player.setPosition(oldpos);
}
}
What it basically does is iterate over every solid and test if the player is intersecting any of them. If there is an intersection, the player will return to their previous position when there was no collision.
The main problem that I have is that when I press two directions at once (for example colliding with a wall to the right while moving right and up) the player gets stuck because it is resetting both the x and y coordinates. Most of the code is based off of This question. The person who asked the question had the same problem. The last comment to an answer suggested that they try separating the vertical from the horizontal movement. I tried that in my code but that didn't work because it then it was basically testing if there was x-collision OR y-collision and would reset the player's position even if the player was in line with a solid.
So I guess my main question is: How can I separate the vertical from the horizontal collision in a better way OR Is there another way of doing tile collisions that allows the player to slide along a wall while holding two directions?
What you want to do is test the collision on both axis seperately. That means you safe the current position of the player and then calculate two bounding boxes. One of which is the players's bounding box with the horizontal offset added and one with the vertical offset added.
sf::FloatRect oldBounds = Player.getPosition();
sf::FloatRect newBoundsH = (oldBounds.left + movement.x *speed*dt.asSeconds(), oldBounds.top, oldBounds.width, oldBounds.height);
sf::FloatRect newBoundsV = (oldBounds.left, oldBounds.top + movement.y *speed*dt.asSeconds(), oldBounds.width, oldBounds.height);
So you can write:
// Test horizontal collision.
if(tiler.intersects(newBoundsH)){
Player.setPosition(oldpos.x, Player.getPositions().y);
}
// Test vertical collision.
if(tiler.intersects(newBoundsV)){
Player.setPosition(Player.getPositions().x, oldpos.y);
}
This method absolutely has its flaws but I think it is easy to understand.

First Person Camera movement issues

I'm implementing a first person camera using the GLM library that provides me with some useful functions that calculate perspective and 'lookAt' matrices. I'm also using OpenGL but that shouldn't make a difference in this code.
Basically, what I'm experiencing is that I can look around, much like in a regular FPS, and move around. But the movement is constrained to the three axes in a way that if I rotate the camera, I would still move in the same direction as if I had not rotated it... Let me illustrate (in 2D, to simplify things).
In this image, you can see four camera positions.
Those marked with a one are before movement, those marked with a two are after movement.
The red triangles represent a camera that is oriented straight forward along the z axis. The blue triangles represent a camera that hasbeen rotated to look backward along the x axis (to the left).
When I press the 'forward movement key', the camera moves forward along the z axis in both cases, disregarding the camera orientation.
What I want is a more FPS-like behaviour, where pressing forward moves me in the direction the camera is facing. I thought that with the arguments I pass to glm::lookAt, this would be achieved. Apparently not.
What is wrong with my calculations?
// Calculate the camera's orientation
float angleHori = -mMouseSpeed * Mouse::x; // Note that (0, 0) is the center of the screen
float angleVert = -mMouseSpeed * Mouse::y;
glm::vec3 dir(
cos(angleVert) * sin(angleHori),
sin(angleVert),
cos(angleVert) * cos(angleHori)
);
glm::vec3 right(
sin(angleHori - M_PI / 2.0f),
0.0f,
cos(angleHori - M_PI / 2.0f)
);
glm::vec3 up = glm::cross(right, dir);
// Calculate projection and view matrix
glm::mat4 projMatrix = glm::perspective(mFOV, mViewPortSizeX / (float)mViewPortSizeY, mZNear, mZFar);
glm::mat4 viewMatrix = glm::lookAt(mPosition, mPosition + dir, up);
gluLookAt takes 3 parameters: eye, centre and up. The first two are positions while the last is a vector. If you're planning on using this function it's better that you maintain only these three parameters consistently.
Coming to the issue with the calculation. I see that the position variable is unchanged throughout the code. All that changes is the look at point I.e. centre only. The right thing to do is to first do position += dir, which will move the camera (position) along the direction pointed to by dir. Now to update the centre, the second parameter can be left as-is: position + dir; this will work since the position was already updated to the new position and from there we've a point farther in dir direction to look at.
The issue was actually in another method. When moving the camera, I needed to do this:
void Camera::moveX(char s)
{
mPosition += s * mSpeed * mRight;
}
void Camera::moveY(char s)
{
mPosition += s * mSpeed * mUp;
}
void Camera::moveZ(chars)
{
mPosition += s * mSpeed * mDirection;
}
To make the camera move across the correct axes.

DirectX C++ Xbox Controller Input Issue

So I've started using DirectX C++ and have a base game with is a bunch of asteroids which you can shoot using the mouse. It also has a drawn crosshair sprite which follows the mouses location.
I have decided that I would like to change input to support Xbox 360 controllers. I have managed to program the shoot button to the A button on the gamepad however I am having some issues changing to movement of the crosshair from mouse to the analog sticks on the controller.
The following is the code which is used for determining the mouses location and getting its position for use:
virtual void Update()
{
POINT mousePosition;
D3DXVECTOR3 position, currentPosition;
DirectInput::Get().ProcessMouse();
mousePosition = DirectInput::Get().GetCurrentMousePosition();
position.x = (float) mousePosition.x;
position.y = (float) mousePosition.y;
position.z = 0.0f;
currentPosition = m_pCrossHairs->GetSpritePosition();
position.x -= currentPosition.x;
position.y -= currentPosition.y;
position.x = position.x/2.0f;
position.y = position.y/2.0f;
m_pCrossHairs->SetTranslationMatrix(position);
m_pCrossHairs->CheckBoundary();
m_pCrossHairs->Update();
}
And here is what I've changed it to for use with an Xbox Controller Instead:
virtual void Update()
{
POINT crosshairPosition;
D3DXVECTOR3 position, currentPosition;
crosshairPosition.x = XBox360Controller::Get().RightJoyStickX();
crosshairPosition.y =- XBox360Controller::Get().RightJoyStickY();
position.x = crosshairPosition.x;
position.y = crosshairPosition.y;
position.z = 0.0f;
currentPosition = m_pCrossHairs->GetSpritePosition();
position.x -= currentPosition.x;
position.y -= currentPosition.y;
position.x = position.x/2.0f;
position.y = position.y/2.0f;
m_pCrossHairs->SetTranslationMatrix(position);
m_pCrossHairs->CheckBoundary();
m_pCrossHairs->Update();
}
On a positive note what I have done kind of works. The Crosshair does move when I move the analog stick, however it is stuck in the top right hand corner and can only move within a range of about 1 square inch on the screen. I'm a bit stuck as to why this is and therefore would appreciate any kind of input. Sorry if I'm lacking any important info that I may have missed, a bit of a late post however would really love to get it working!
On a side note - I'm getting an error on the following giving me a warning about conversion from long to float or something (should I be worried about this)?
crosshairPosition.x = XBox360Controller::Get().RightJoyStickX();
crosshairPosition.y =- XBox360Controller::Get().RightJoyStickY();
Once again thank you in advance.
Billy
Edit:
Code for XBox360Controller::Get().
XBox360Controller& XBox360Controller::Get()
{
if (s_pXBox360Controller == NULL)
s_pXBox360Controller = new XBox360Controller();
return *s_pXBox360Controller;
}
Code for Right analog stick X-Axis
float XBox360Controller::RightJoyStickX()
{
float joyStickX = GetState().Gamepad.sThumbRX;
// Make sure there has been movement, as joystick does return different values even if no movement.
if ( joyStickX< XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE &&
joyStickX > -XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE)
return 0;
else
return joyStickX/DynamicRange;
}
Code for Right analog stick Y-Axis
float XBox360Controller::RightJoyStickY()
{
float joyStickY = GetState().Gamepad.sThumbRY;
// Make sure there has been movement, as joystick does return different values even if no movement.
if ( joyStickY< XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE &&
joyStickY > -XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE)
return 0;
else
return joyStickY/DynamicRange;
}
EDIT SOLVED:
Added a new translation matrix and it's fixed the issue, thanks for the help.

Make an object follow the camera in OpenGL

I'm making a racecar game in OpenGL (just a small project) and I'm having problems trying to make my car follow the camera view.
It wasn't hard to make it follow the camera as the camera moves forward and/or backward, but as I rotate the camera (look right or left) the car remais still. I mean, it still moves forward and backward but it's not in front of the camera (it's on the side).
Here is my code (the part where I try to implement it):
void updateCam() {
gluLookAt(posX,posY + 0.025 * std::abs(sin(headPosAux*PI/180)),posZ,
posX + sin(roty*PI/180),posY + 0.025 * std::abs(sin(headPosAux*PI/180)) + cos(rotx*PI/180),posZ -cos(roty*PI/180),
0.0,1.0,0.0);
listenerPos[0] = posX;
listenerPos[1] = posY;
listenerPos[2] = posZ;
source0Pos[0] = posX;
source0Pos[1] = posY;
source0Pos[2] = posZ;
GLfloat distD;
distD = posZ - 3.3;
//makes the car "follow" the camera
modelPOR.Translate(posX,posY,distD);
}
I think the problem is that you change the center location in gluLookAt and not in the modelPOR.Translate
the 3 middle parameters of gluLookAt set the center of the view, so the car should be at the exact same position but you modify the center without modifying the car's position.
gluLookAt(posX,posY + 0.025 * std::abs(sin(headPosAux*PI/180)),posZ,
// center x
posX + sin(roty*PI/180),
// center y
posY + 0.025 * std::abs(sin(headPosAux*PI/180)) + cos(rotx*PI/180),
// center z
posZ -cos(roty*PI/180),
0.0,1.0,0.0);
you should probably translate the car by those same values. then it will be centered.
another problem that seems to be in that code is that you rotate the camera and not the car, so the car probably will not stay pointing in the same direction as the camera. (is that desired or do you do it elswhere?)
but are you really sure you want to make the car follow the camera? it would probably be better the other way around.

How do I make projectiles?

I am totally stumped on this one. I'm using C++ and SFML 1.6 for a game I'm developing, and I have no bloody idea. How do I make projectiles (like bullets)? I just don't understand it. It could be my lack of sleep but I don't know.
So my question is how do I create a Sprite that moves in a definite direction based on where the mouse is? (Think of a top down shooter with mouse aiming)
Easiest solution:
If the mouse is at Mx,My and the ship is at Sx,Sy then calculate the direction from the ship to the mouse:
Dx=Sx-Mx
Dy=Sy-My
Now normalise D (this means scale it so that it's length is one):
DLen=sqrt(Dx*Dx + Dy*Dy)
Dx/=DLen;
Dy/=DLen;
Now Dx is the distance you want to move the bullet on the x axis in order to get bullet speed of 1.
Thus each frame you move the bullet like so (position of bullet: Bx,By Speed of bullet: Bs [in pixels per millisec] Frame time Ft[in millisec])
Bx=Bx+Dx*Bs*Ft
By=By+Dy*Bs*Ft
This give you a bullet that moves towards the mouse position at a speed independent of the direction of the mouse or framerate of the game.
EDIT: As #MSalters says you need to check for the DLen==0 case when the mouse is directly above the ship to avoid division by zero errors on the normalise
One way to do it is to make the bullet face the mouse and then move it across the x and y axis by using trigonometry to find the hypotinuse from the angle. I don't think i explained this very well, so here the code to make a sprite move from its rotation:
void sMove(sf::Sprite& input,float toMove, int rotation){
bool negY = false;
bool negX = false;
int newRot = rotation;
if (rotation <= 90){
negY = true;
}
else if (rotation <= 180){
//newRot -= 90;
negY = true;
}
else if (rotation <= 360){
newRot -= 270;
negY = true;
negX = true;
}
float y = toMove*cos(newRot*PI/180);
float x = toMove*sin(newRot*PI/180);
if (negY){
y = y*-1;
}
if (negX){
x = x*-1
}
input.move(x, y);
}