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;
}
Related
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 using this code in cocos2d-x for shooting in specific directions but at the edge of screen my sprite(i.e. my shooting bubbles) used to get lost. I need help in making it to change its angle when it collides at screen edges and change direction to go up.
Code in c++ using cocos2dx:-
CCSize winSize = CCDirector::sharedDirector()->getVisibleSize();
CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin();
// Determinie offset of location to projectile
float offX = flocationX - m_pOwner->getPosition().x;
float offY = flocationY - m_pOwner->getPosition().y;
// Bail out if we are shooting down or backwards
if (offY <= 0) return;
// Ok to add now - we've double checked position
// Determine where we wish to shoot the projectile to
//float realX = origin.x + winSize.width + (m_pOwner->getPosition().x);
float realY = origin.y + winSize.height + (m_pOwner->getPosition().y);
// float ratio = offY / offX;
float ratio = offX / offY;
//float realY = (realX * ratio) + m_pOwner->getPosition().y;
float realX = (realY * ratio) + m_pOwner->getPosition().x;
CCPoint realDest = ccp(realX, realY);
// Determine the length of how far we're shooting
float offRealX = realX - m_pOwner->getPosition().x;
float offRealY = realY - m_pOwner->getPosition().y;
float length = sqrtf((offRealX * offRealX) + (offRealY*offRealY));
float velocity = 480/1; // 480pixels/1sec
float realMoveDuration = length/velocity;
// Move projectile to actual endpoint
m_pOwner->runAction( CCSequence::create(
CCMoveTo::create(realMoveDuration, realDest),
CCCallFuncN::create(getOwner()->getParent()->getComponent("SceneController"),
callfuncN_selector(SceneController::spriteMoveFinished)),
NULL) );
Alright, so I'm trying to click and drag to rotate around an object using C++ and OpenGL. The way I have it is to use gluLookAt centered at the origin and I'm getting coordinates for the eye by using parametric equations for a sphere (eyex = 2* cos(theta) * sin(phi); eyey = 2* sin(theta) * sin(phi); eyez = 2* cos(phi);). This works mostly, as I can click and rotate horizontally, but when I try to rotate vertically it makes tight circles instead of rotating vertically. I'm trying to get the up vector by using the position of the camera and a vecter at a 90 degree angle along the x-z plane and taking the cross product of that.
The code I have is as follows:
double dotProduct(double v1[], double v2[]) {
return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
}
void mouseDown(int button, int state, int x, int y) {
if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN ) {
xpos = x;
ypos = y;
}
}
void mouseMovement(int x, int y) {
diffx = x - xpos;
diffy = y - ypos;
xpos = x;
ypos = y;
}
void camera (void) {
theta += 2*PI * (-diffy/glutGet(GLUT_SCREEN_HEIGHT));
phi += PI * (-diffx/glutGet(GLUT_WINDOW_WIDTH));
eyex = 2* cos(theta) * sin(phi);
eyey = 2* sin(theta) * sin(phi);
eyez = 2* cos(phi);
double rightv[3], rightt[3], eyes[3];
rightv[0] = 2* cos(theta + 2/PI) * sin(phi);
rightv[1] = 0;
rightv[2] = 2* cos(phi);
rightt[0] = rightv[0];
rightt[1] = rightv[1];
rightt[2] = rightv[2];
rightv[0] = rightv[0] / sqrt(dotProduct(rightt, rightt));
rightv[1] = rightv[1] / sqrt(dotProduct(rightt, rightt));
rightv[2] = rightv[2] / sqrt(dotProduct(rightt, rightt));
eyes[0] = eyex;
eyes[1] = eyey;
eyes[2] = eyez;
upx = (eyey/sqrt(dotProduct(eyes,eyes)))*rightv[2] + (eyez/sqrt(dotProduct(eyes,eyes)))*rightv[1];
upy = (eyez/sqrt(dotProduct(eyes,eyes)))*rightv[0] + (eyex/sqrt(dotProduct(eyes,eyes)))*rightv[2];
upz = (eyex/sqrt(dotProduct(eyes,eyes)))*rightv[1] + (eyey/sqrt(dotProduct(eyes,eyes)))*rightv[0];
diffx = 0;
diffy = 0;
}
I am somewhat basing things off of this but it doesn't work, so I tried my way instead.
This isn't exactly a solution for the way you are doing it but I did something similar the other day. I did it by using DX's D3DXMatrixRotationAxis and D3DXVec3TransformCoord The math behind the D3DXMatrixRotationAxis method can be found at the bottom of the following page: D3DXMatrixRotationAxis Math use this if you are unable to use DX. This will allow you to rotate around any axis you pass in. In my object code I keep track of a direction and up vector and I simply rotate each of these around the axis of movement(in your case the yaw and pitch).
To implement the fixed distance camera like this I would simply do the dot product of the current camera location and the origin location (if this never changes then you can simply do it once.) and then move the camera to the origin rotate it the amount you need then move it back with its new direction and up values.
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));