if(wIsPressed){
movement.x += sin((player.getRotation() * 3.141592654)/ 180) * .5; //accelerates ship at a rate of 0.5 ms^2
movement.y -= cos((player.getRotation() * 3.141592654)/ 180) * .5; //accelerates ship at a rate of 0.5 ms^2
}
else if(abs(movement.x) > 0 || abs(movement.y) > 0){
double angle = (atan2(movement.x, movement.y) * 3.141592654) / 180; //finds angle of current movement vector and converts fro radians to degrees
movement.x -= sin((angle)) * 0.5; //slows down ship by 0.5 using current vector angle
movement.y += cos((angle)) * 0.5; //slows down ship by 0.5 using current vector angle
}
basically, what happens after using this code is that my ship is pulled directly down to the bottom of the screen, and acts like the ground has gravity, and i dont understand what i am doing incorrectly
To elaborate on my comment:
You're not converting your angle to degrees properly. It should be:
double angle = atan2(movement.x, movement.y) * 180 / 3.141592654;
However, you're using this angle in another trig calculation, and the C++ trig functions expect radians, so you really shouldn't be converting this to degrees in the first place. Your else if statement could also be causing problems, because you're checking for an absolute value greater than 0. Try something like this:
float angle = atan2(movement.x, movement.y);
const float EPSILON = 0.01f;
if(!wIsPressed && abs(movement.x) > EPSILON) {
movement.x -= sin(angle) * 0.5;
}
else {
movement.x = 0;
}
if(!wIsPressed && abs(movement.y) > EPSILON) {
movement.y += cos(angle) * 0.5;
}
else {
movement.y = 0;
}
Related
I'm currently building a game where the played drives a semi truck and is followed and attacked by enemy cars by attempted slamming. I got some help from one of my teachers on how to get the enemy to choose which direction to go in order to follow and attack the player. Upon implementing what she gave me I got weird behavior and feel like I'm missing something.
When I place an enemy car in game near the player and pass the player's position into the function the enemy car simply spins in circles. If I add velocity to it i drives in large circles. Generally it never chooses a direction to drive straight in.
After debugging it seems like my if statement doesn't ever resolve and upon every update it keeps trying to get back towards 0 but for some reason it can't.
I am not sure if the coordinates of the player are creating the issue or if my math calculations or going wonky.
void EnemySpeedy::playerTracking(float posX, float posY)
{
//Direction choosing
dir.x = posX - pos.x;
dir.y = posY - pos.y;
//plus maybe this?
goalAngle = atan2f(dir.y, dir.x);
//I think this is the problem code?//
if (angle < goalAngle) angle -= sfw::getDeltaTime() * angularSpeed;
else angle += sfw::getDeltaTime() * angularSpeed;
//AI Movement alla adding velocity
acc = speed;
vel = vel + (acc - dragVel) * sfw::getDeltaTime();
vel = std::fmaxf(0, vel);
vel = std::fminf(vel, maxVel);
pos = { pos.x + vel * cosf(angle * PI / 180) * sfw::getDeltaTime(),
pos.y + vel * sinf(angle * PI / 180) * sfw::getDeltaTime() };
}
atan2f returns radians, so your goalAngle is in the range [-Pi,Pi].
I don't know if your angle and the angularSpeed use the same metric, but when you calculate the sinf and cosf you are converting angle from degrees to radians.
I suggest to keep all your angles in radians and check them:
#include <cmath>
inline float normAngle ( float ang ) {
return ang < -M_PI ? ang + 2.0*M_PI : ( ang > M_PI ? ang - 2.0*M_PI : ang);
}
inline float limitValue ( float x, float min, float max ) {
return x < min ? min : ( x > max ? max : x );
}
Then, you can try this logic:
void EnemySpeedy::playerTracking(float posX, float posY)
{
//Direction choosing, pos is a member of EnemySpeedy
float dirX = posX - pos.x;
float dirY = posY - pos.y;
//Angle choosing; angle, angularSpeed and angularSpeedMax are members of EnemySpeedy
float goalAngle = atan2(dirY, dirX);
float difAngle = normAngle(angle - goalAngle);
angularSpeed = limitValue(-difAngle,-angularSpeedMax,angularSpeedMax);
float dt = sfw::getDeltaTime();
angle = normAngle(angle + dt * angularSpeed);
// Update speed; acc, vel, etc. are members of EnemySpeedy class
// acc = speed; // it seems odd to me...
// vel = limitValue(vel + (acc - dragVel) * dt, 0.0, maxVel);
// what about:
acc = (difAngle > 1.5 || difAngle < -1.5) ? -maxAcc/2.0 : maxAcc*(maxVel - vel)/maxVel;
// brake if direction is wrong, go to limit velocity otherwise
acc = limitValue(acc, -maxAcc, maxAcc);
vel = limitValue(vel + acc * dt, 0.0, maxVel);
// Update position
pos.x += vel * cos(angle) * dt;
pos.y += vel * sin(angle) * dt;
}
I am trying to do the equivalent of multiplying the velocity by the time between frames. I would imagine that doing this for quaternions would be done by raising them to a power. I have code to rotate an object based on my mouse movements. It has a main loop running at one frame rate and a physics loop running at a fixed frame rate. Here is the relevant part of the main loop:
glfwPollEvents();
Input::update();
window.clear(0,0,0,1);
rigidBody.angularVelocity *= glm::angleAxis(0.001f * Input::deltaMouse().x, glm::vec3(0,1,0));
rigidBody.angularVelocity *= glm::angleAxis(0.001f * Input::deltaMouse().y, glm::vec3(1,0,0));
if(Input::getKey(Input::KEY_A))
{
rigidBody.velocity -= float(Time::getDelta()) * glm::vec3(1,0,0);
}
if(Input::getKey(Input::KEY_D))
{
rigidBody.velocity += float(Time::getDelta()) * glm::vec3(1,0,0);
}
if(Input::getKey(Input::KEY_W))
{
rigidBody.velocity -= float(Time::getDelta()) * glm::vec3(0,0,1);
}
if(Input::getKey(Input::KEY_S))
{
rigidBody.velocity += float(Time::getDelta()) * glm::vec3(0,0,1);
}
if(Input::getKey(Input::KEY_LCONTROL))
{
rigidBody.velocity -= float(Time::getDelta()) * glm::vec3(0,1,0);
}
if(Input::getKey(Input::KEY_LSHIFT))
{
rigidBody.velocity += float(Time::getDelta()) * glm::vec3(0,1,0);
}
Here is the relevant part of the physics loop:
for(int i = 0; i < *numRigidBodies; i++)
{
rigidBodies[i].transform->getPos() += rigidBodies[i].velocity;
rigidBodies[i].transform->getRot() *= rigidBodies[i].angularVelocity;
}
rigidBodies[0].angularVelocity = glm::quat();
rigidBodies[0].velocity = glm::vec3();
This works fine, but when I try raising angular velocity to a power with glm::pow, the object rotates randomly and does not follow my mouse. I realize I could do this with a line of code like
rigidBodies[i].transform->getRot() *= glm::angleAxis((float)Time::getFixedDelta() * glm::angle(rigidBodies[i].angularVelocity), glm::axis(rigidBodies[i].angularVelocity));
but this seems needlessly complicated for the task. What is causing this issue, and how can I fix it?
Not sure exactly how to do it with the API you're using, but basically, you would use Quaternion::Slerp(). Slerp means "spherical linear interpolation".
Something like this(pseudocode) should work:
auto& rot = rigidBodies[i].transform->getRot();
auto goal = rigidBodies[i].angularVelocity * rot;
rot = rot.slerp(rot, goal, Time::deltaTime);
Edit:
I should note that this is not how I would approach this problem. I would just store the rotation around the X and Y axis as scalars and construct a new quaternion from them each frame.
Please excuse the sloppy pseudo code:
// previous x and y positions, could probably be set in MouseDown event
float lastX = ...;
float lastY = ...;
float xRotation = 0;
float yRotation = 0;
float rotationSpeed = 1.0;
void OnMouseMove(float x, float y) {
float dx = x - lastX;
float dy = y - lastY;
lastX = x;
lastY = y;
xRotation += dy * rotationSpeed * Time::deltaTime;
yRotation += dx * rotationSpeed * Time::deltaTime;
rigidBodies[i].transform->getRot() = eulerQuat(xRotation, yRotation, 0);
}
Turns out angular velocity is usually represented as a 3d vector where the direction is the axis and the magnitude is the angular speed. Replace this line of code:
rigidBodies[i].transform->getRot() *= rigidBodies[i].angularVelocity;
with this:
if(rigidBodies[i].angularVelocity != glm::vec3())
rigidBodies[i].transform->getRot() *= glm::quat(rigidBodies[i].angularVelocity * float(Time::getFixedDelta()));
and the physics system works as expected. The if check makes sure that angular speed is not 0.
I've asked this question over at GameDev but got not response so far and this question is a bit time sensitive unfortunately.
I'm pretty sure this is just me doing something stupid or not understanding something that I should but I cannot figure out what is wrong here.
I'm having a problem bouncing a projectile off a sprite, we've been asked to move the projectile using the equations of motions which makes things a little more difficult but as far as I can see what I have should work.
What I'm trying to do is change the angle of the collided projectile depending on which direction it is coming from.
Here is a video that is hopefully not too laggy for you to see what is happening:
Link
When the projectile collides with the left or right hand side of the sprite everything works as expected, it just switches X direction.
When it hit's the top or bottom of the sprite however it doesn't change, it just sort of rolls along the top and the shoots off.
Here is the movement code:
float nX = get_x() + cos(nGetAngle() * 3.14 / 180) * getU() * getT();
float nY = get_y() - sin(nGetAngle() * 3.14 / 180) * getU() * getT() + 0.5 * 9.8 * getT() * getT();
set_world_position(nX, nY);
Where U is initial velocity, T is time and nGetAngle() is the angle in degrees (which is set to radians whenever the angle is set).
Here is my collision for the top of the player:
//if the projectile is colliding in any way with the player sprite
if (projectiles[currProj]->get_y() < player->get_y()) // top of player
{
float vx = cos(projectiles[currProj]->nGetAngle());
float vy = sin(projectiles[currProj]->nGetAngle());
float newAngle = atan2(-vy, vx) * 180 / 3.14;
projectiles[currProj]->nSetAngle(newAngle);
projectiles[currProj]->set_world_position_y(player->get_y() - projectiles[currProj]->get_height() - 1);
}
and here is my collision for the left of the player:
else if (projectiles[currProj]->get_x() < player->get_x()) // left of player
{
projectiles[currProj]->set_world_position_x(player->get_x() - projectiles[currProj]->get_width());
float vx = cos(projectiles[currProj]->nGetAngle());
float vy = sin(projectiles[currProj]->nGetAngle());
float newAngle = atan2(vy, -vx) * 180 / 3.14;
projectiles[currProj]->nSetAngle(newAngle);
}
The left side collision works, the top does not and I have no idea why.
If necessary I can post the entire project somewhere.
Full collision code for player:
void Game::playerCollision()
{
if (projectiles[currProj]->bb_collision(player))
{
if (projectiles[currProj]->get_y() < player->get_y()) // top of player
{
float vx = cos(projectiles[currProj]->nGetAngle());
float vy = sin(projectiles[currProj]->nGetAngle());
float newAngle = atan2(-vy, vx) * 180 / 3.14;
projectiles[currProj]->nSetAngle(newAngle);
projectiles[currProj]->set_world_position_y(player->get_y() - projectiles[currProj]->get_height() - 1);
}
else if (projectiles[currProj]->get_y() + projectiles[currProj]->get_height() > player->get_y() + player->get_height() + 1) // bottom of player
{
projectiles[currProj]->set_world_position_y(player->get_y() + player->get_height());
float vx = cos(projectiles[currProj]->nGetAngle());
float vy = sin(projectiles[currProj]->nGetAngle());
float newAngle = atan2(-vy, vx) * 180 / 3.14;
projectiles[currProj]->nSetAngle(newAngle);
}
else if (projectiles[currProj]->get_x() < player->get_x()) // left of player
{
projectiles[currProj]->set_world_position_x(player->get_x() - projectiles[currProj]->get_width());
float vx = cos(projectiles[currProj]->nGetAngle());
float vy = sin(projectiles[currProj]->nGetAngle());
float newAngle = atan2(vy, -vx) * 180 / 3.14;
projectiles[currProj]->nSetAngle(newAngle);
}
else if (projectiles[currProj]->get_x() > player->get_x()) // right of player
{
projectiles[currProj]->set_world_position_x(player->get_x() + player->get_width());
float vx = cos(projectiles[currProj]->nGetAngle());
float vy = sin(projectiles[currProj]->nGetAngle());
float newAngle = atan2(vy, -vx) * 180 / 3.14;
projectiles[currProj]->nSetAngle(newAngle);
}
}
}
I think your collision detection is not sufficient. without knowing your representation in detail
you do not check where the projectile (pr) came from. a collision top left within the player (pl) might have entered through the top or from the left
you do not bounce the pr immediately, you just alter the direction. depending on the entry depth it might not be able to exit with the next iteration. this happens especially on the top where the pr accelerates downwards but slows down upwards.
so you must
detect the entry surface (determines angle)
and most important rebounce immediately
I am trying to rotate opengl scene using track ball. The problem i am having is i am getting rotations opposite to direction of my swipe on screen. Here is the snippet of code.
prevPoint.y = viewPortHeight - prevPoint.y;
currentPoint.y = viewPortHeight - currentPoint.y;
prevPoint.x = prevPoint.x - centerx;
prevPoint.y = prevPoint.y - centery;
currentPoint.x = currentPoint.x - centerx;
currentPoint.y = currentPoint.y - centery;
double angle=0;
if (prevPoint.x == currentPoint.x && prevPoint.y == currentPoint.y) {
return;
}
double d, z, radius = viewPortHeight * 0.5;
if(viewPortWidth > viewPortHeight) {
radius = viewPortHeight * 0.5f;
} else {
radius = viewPortWidth * 0.5f;
}
d = (prevPoint.x * prevPoint.x + prevPoint.y * prevPoint.y);
if (d <= radius * radius * 0.5 ) { /* Inside sphere */
z = sqrt(radius*radius - d);
} else { /* On hyperbola */
z = (radius * radius * 0.5) / sqrt(d);
}
Vector refVector1(prevPoint.x,prevPoint.y,z);
refVector1.normalize();
d = (currentPoint.x * currentPoint.x + currentPoint.y * currentPoint.y);
if (d <= radius * radius * 0.5 ) { /* Inside sphere */
z = sqrt(radius*radius - d);
} else { /* On hyperbola */
z = (radius * radius * 0.5) / sqrt(d);
}
Vector refVector2(currentPoint.x,currentPoint.y,z);
refVector2.normalize();
Vector axisOfRotation = refVector1.cross(refVector2);
axisOfRotation.normalize();
angle = acos(refVector1*refVector2);
I recommend artificially setting prevPoint and currentPoint to (0,0) (0,1) and then stepping through the code (with a debugger or with your eyes) to see if each part makes sense to you, and the angle of rotation and axis at the end of the block are what you expect.
If they are what you expect, then I'm guessing the error is in the logic that occurs after that. i.e. you then take the angle and axis and convert them to a matrix which gets multiplied to move the model. A number of convention choices happen in this pipeline --which if swapped can lead to the type of bug you're having:
Whether the formula assumes the angle is winding left or right handedly around the axis.
Whether the transformation is meant to rotate an object in the world or meant to rotate the camera.
Whether the matrix is meant to operate by multiplication on the left or right.
Whether rows or columns of matrices are contiguous in memory.
Here is what I'm trying to do. I'm trying to make a bullet out of the center of the screen. I have an x and y rotation angle. The problem is the Y (which is modified by rotation on the x) is really not working as intended. Here is what I have.
float yrotrad, xrotrad;
yrotrad = (Camera.roty / 180.0f * 3.141592654f);
xrotrad = (Camera.rotx / 180.0f * 3.141592654f);
Vertex3f Pos;
// get camera position
pls.x = Camera.x;
pls.y = Camera.y;
pls.z = Camera.z;
for(float i = 0; i < 60; i++)
{
//add the rotation vector
pls.x += float(sin(yrotrad)) ;
pls.z -= float(cos(yrotrad)) ;
pls.y += float(sin(twopi - xrotrad));
//translate camera coords to cube coords
Pos.x = ceil(pls.x / 3);
Pos.y = ceil((pls.y) / 3);
Pos.z = ceil(pls.z / 3);
if(!CubeIsEmpty(Pos.x,Pos.y,Pos.z)) //remove first cube that made contact
{
delete GetCube(Pos.x,Pos.y,Pos.z);
SetCube(0,Pos.x,Pos.y,Pos.z);
return;
}
}
This is almost identical to how I move the player, I add the directional vector to the camera then find which cube the player is on. If I remove the pls.y += float(sin(twopi - xrotrad)); then I clearly see that on the X and Z, everything is pointing as it should. When I add pls.y += float(sin(twopi - xrotrad)); then it almost works, but not quite, what I observed from rendering out spheres of the trajector is that the furthur up or down I look, the more offset it becomes rather than stay alligned to the camera's center. What am I doing wrong?
Thanks
What basically happens is very difficult to explain, I'd expect the bullet at time 0 to always be at the center of the screen, but it behaves oddly. If i'm looking straight at the horizon to +- 20 degrees upward its fine but then it starts not following any more.
I set up my matrix like this:
void CCubeGame::SetCameraMatrix()
{
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glRotatef(Camera.rotx,1,0,0);
glRotatef(Camera.roty,0,1,0);
glRotatef(Camera.rotz,0,0,1);
glTranslatef(-Camera.x , -Camera.y,-Camera.z );
}
and change the angle like this:
void CCubeGame::MouseMove(int x, int y)
{
if(!isTrapped)
return;
int diffx = x-lastMouse.x;
int diffy = y-lastMouse.y;
lastMouse.x = x;
lastMouse.y = y;
Camera.rotx += (float) diffy * 0.2;
Camera.roty += (float) diffx * 0.2;
if(Camera.rotx > 90)
{
Camera.rotx = 90;
}
if(Camera.rotx < -90)
{
Camera.rotx = -90;
}
if(isTrapped)
if (fabs(ScreenDimensions.x/2 - x) > 1 || fabs(ScreenDimensions.y/2 - y) > 1) {
resetPointer();
}
}
You need to scale X and Z by cos(xradrot). (In other words, multiply by cos(xradrot)).
Imagine you're pointing straight down the Z axis but looking straight up. You don't want the bullet to shoot down the Z axis at all, this is why you need to scale it. (It's basically the same thing that you're doing between X and Z, but now doing it on the XZ vector and Y.)
pls.x += float(sin(yrotrad)*cos(xrotrad)) ;
pls.z -= float(cos(yrotrad)*cos(xrotrad)) ;
pls.y += float(sin(twopi - xrotrad));