I'm using opengl and trying to create a first person camera. All examples use GLUT and I need to get the mouse differential in cocoa. But I'm running into issues which appers to be tied with mouseMoved being called as soon as the mouse is moved (which is to be expected). Is there a way to make this mroe accurate? Or a simialer function like GLUTS glutMouseFunc?
Current attempt:
-(void)mouseMoved:(NSEvent *)event{
static bool wrap = false;
if(!wrap){
NSPoint eventLocation = [event locationInWindow];
float centerX = self.frame.size.width/2 + [self window].frame.origin.x;
float centerY = self.frame.size.height/2 + [self window].frame.origin.y;
CGPoint mousePointCenter = CGPointMake(centerX, centerY);
CGWarpMouseCursorPosition(mousePointCenter);
int dx = eventLocation.x - self.frame.size.width/2 ;
int dy = eventLocation.y - self.frame.size.height/2 ;
const float mousespeed = 0.001;
angles.x += dx * mousespeed;
angles.y += dy * mousespeed;
if(angles.x < -M_PI)
angles.x += M_PI * 2;
else if(angles.x > M_PI)
angles.x -= M_PI * 2;
if(angles.y < -M_PI / 2)
angles.y = -M_PI / 2;
if(angles.y > M_PI / 2)
angles.y = M_PI / 2;
lookat.x = sinf(angles.x) * cosf(angles.y);
lookat.y = sinf(angles.y);
lookat.z = cosf(angles.x) * cosf(angles.y);
CGWarpMouseCursorPosition(mousePointCenter);
[self setNeedsDisplay:YES];
}
else{
wrap = true;
}
}
I'm not sure I followed what your code is supposed to be doing, but repeatedly warping the mouse cursor to a center point is rarely the right thing to do.
First, you can use the deltaX and deltaY values of the NSEvent.
Perhaps you want to do CGAssociateMouseAndMouseCursorPosition(false) to disassociate the mouse from the cursor position. When you do that, the on-screen cursor no longer moves with the mouse. You can hide it or reposition it (yes, you'd warp it once in this case). Also, events no longer have changes in their absolute position. But they do still carry delta movement values which reflect the mouse movements.
Related
void update(RenderWindow& window)
{
if (Keyboard::isKeyPressed(Keyboard::W))
{
dy = -0.3;
}
if (Keyboard::isKeyPressed(Keyboard::A))
{
dx = -0.3;
}
if (Keyboard::isKeyPressed(Keyboard::S))
{
dy = 0.3;
}
if (Keyboard::isKeyPressed(Keyboard::D))
{
dx = 0.3;
}
x += dx;
y += dy;
dx = dy = 0;
EntitySprite.setPosition(x, y);
window.draw(EntitySprite);
}
When the movement is described in such a code, the player moves angularly: either to the left, or up, or to the right, or down, or diagonally at an angle of 45 degrees by pressing two buttons together, for example S and D. Can this angle be made smoother so that the movement itself was carried out not only to the left, right, diagonally, etc.? My knowledge of geometry is not enough here, so I ask for your help).
In your code, the user input directly modifies the player position. This is probably the reason why the player movement looks so abrupt to you. Technically, in your code, the user input is what is determining the player velocity at any given moment.
A more realistic approach would be to let the player have a velocity property instead –
that represents the player position's rate of change – and then have the player position updated only through this velocity, not directly from the user input. Instead, the velocity would be what is directly modified by the input, but not entirely determined by the current input as it will also depend on its previous value.
Following this approach, the user input is used to calculate the player acceleration in each call to update(). This acceleration – the rate of change of the velocity – is used to update the player velocity directly. Finally, the player velocity is, in turn, used to update the player position.
The following code implements this approach by introducing the velocity_ data member and the acceleration local variable:
void update(RenderWindow& window) {
sf::Vector2f acceleration;
// adjust this at will
const float dAcc = 0.3f;
// set acceleration
if (Keyboard::isKeyPressed(Keyboard::W))
acceleration.y -= dAcc;
if (Keyboard::isKeyPressed(Keyboard::A))
acceleration.x -= dAcc;
if (Keyboard::isKeyPressed(Keyboard::S))
acceleration.y += dAcc;
if (Keyboard::isKeyPressed(Keyboard::D))
acceleration.x += dAcc;
// update velocity through acceleration
velocity_ += acceleration;
// update position through velocity
x += velocity_.x;
y += velocity_.y;
// apply damping to the velocity
velocity_ = 0.99f * velocity_;
EntitySprite.setPosition(x, y);
window.draw(EntitySprite);
};
This way, the player possesses some kind of inertia, and its movement looks smoother.
Note that you may want to have some damping for the velocity as in:
velocity_ = 0.99f * velocity_;
This will resemble the effect of drag forces.
with help of that man I did it like this, but some better)
void update(RenderWindow& window)
{
float decceleration = 0.3;
if (Keyboard::isKeyPressed(Keyboard::W))
{
accelerationY -= decceleration;
}
if (Keyboard::isKeyPressed(Keyboard::S))
{
accelerationY += decceleration;
}
if (Keyboard::isKeyPressed(Keyboard::A))
{
accelerationX -= decceleration;
}
if (Keyboard::isKeyPressed(Keyboard::D))
{
accelerationX += decceleration;
}
dx += accelerationX;
dy += accelerationY;
speed = sqrt(dx * dx + dy * dy);
if (speed > maxSpeed)
{
dx *= maxSpeed / speed;
dy *= maxSpeed / speed;
}
x += dx;
y += dy;
dx *= 0.9;
dy *= 0.9;
accelerationX = 0;
accelerationY = 0;
EntitySprite.setPosition(x, y);
window.draw(EntitySprite);
}
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'm working on some code to place isometric CCTMXTiledMap onto a CCLayerPanZoom control and then convert a touch location into ISO tilemap co-ordinates. This all works perfectly well for me, so long as the scale of the CClayerPanZoom is 1 (i.e. if I don't zoom in or zoom out). I can pan the map around and still calculate the correct iso tile co-oridinates. However, as soon as I zoom the tiled map in or out the iso cordinates returned by my code are completely wrong. Please see below for my code to calculate the iso co-ordinates from the touch location.
-(CGPoint) tilePosFromLocation:(CGPoint)location tileMap:(CCTMXTiledMap*)thisTileMap panZoom:(CCLayerPanZoom*)thisPanZoom
{
float midScale = (thisPanZoom.minScale + thisPanZoom.maxScale) / 2.0;
float newScale = (thisPanZoom.scale <= midScale) ? thisPanZoom.maxScale : thisPanZoom.minScale;
if (thisPanZoom.scale < 1)
{
newScale = newScale + thisPanZoom.scale;
}
else
{
newScale = newScale - thisPanZoom.scale;
}
CGFloat deltaX = (location.x - thisPanZoom.anchorPoint.x * (thisPanZoom.contentSize.width / CC_CONTENT_SCALE_FACTOR()) ) * (newScale);
CGFloat deltaY = (location.y - thisPanZoom.anchorPoint.y * (thisPanZoom.contentSize.height / CC_CONTENT_SCALE_FACTOR()) ) * (newScale);
CGPoint position = ccp((thisPanZoom.position.x - deltaX) , (thisPanZoom.position.y - deltaY) );
float halfMapWidth = thisTileMap.mapSize.width * 0.5f;
float mapHeight = thisTileMap.mapSize.height;
float tileWidth = thisTileMap.tileSize.width / CC_CONTENT_SCALE_FACTOR() * newScale;
float tileHeight = thisTileMap.tileSize.height / CC_CONTENT_SCALE_FACTOR() * newScale;
CGPoint tilePosDiv = CGPointMake(position.x / tileWidth, position.y / tileHeight );
float inverseTileY = tilePosDiv.y - (mapHeight * CC_CONTENT_SCALE_FACTOR()) * newScale; //mapHeight + tilePosDiv.y;
float posX = (int)(tilePosDiv.y - tilePosDiv.x + halfMapWidth);
float posY = (int)(inverseTileY + tilePosDiv.x - halfMapWidth + mapHeight);
// make sure coordinates are within isomap bounds
posX = MAX(0, posX);
posX = MIN(thisTileMap.mapSize.width - 1, posX);
posY = MAX(0, posY);
posY = MIN(thisTileMap.mapSize.height - 1, posY);
return CGPointMake(posX, posY);
}
Can anyone offer any insight into where I'm going wrong with this?
Thanks,
Alan
In my OpenGL application, I have a camera which is controlled using the keyboard (movement) and mouse (looking around).
Everythings been working perfectly fine up until now, I have noticed that if I move my camera above 300 in the Y axis, it starts to mess up when moving the mouse. For example, if I go to Y =310, and move the mouse up, as it starts to look up it starts turning to the left as well.
I am not sure what the reason for this is. Can anyone help?
Heres the code to work out forward and up position for gluLookAt()
double cosR, cosP, cosY; //temp values for sin/cos from
double sinR, sinP, sinY; //the inputed roll/pitch/yaw
if(Yaw > 359) Yaw = 0;
if(Pitch > 359) Pitch = 0;
if(Yaw < 0) Yaw = 359;
if(Pitch < 0) Pitch = 359;
cosY = cosf(Yaw*3.1415/180);
cosP = cosf(Pitch*3.1415/180);
cosR = cosf(Roll*3.1415/180);
sinY = sinf(Yaw*3.1415/180);
sinP = sinf(Pitch*3.1415/180);
sinR = sinf(Roll*3.1415/180);
//forward position
forwardPos.x = sinY * cosP*360;
forwardPos.y = sinP * 360;
forwardPos.z = cosP * -cosY*360;
//up position
upPos.x = -cosY * sinR - sinY * sinP * cosR;
upPos.y = cosP * cosR;
upPos.z = -sinY * sinR - sinP * cosR * -cosY;
Gimble Lock. It is explained here.
Quaternions are the standard solution to this problem.
Briefly, when two axis angles approach each other in your above calculation, it causes a loss of available movement. You need a 4th degree of freedom to prevent this - and this is what Quaternion math allow.
I seem to be having problems with my OpenGl camera. When I first created it, everything worked fine. However since importing models and creating objects, I've noticed weird things happening.
Firstly, heres my code for movement:
float xRot = (Pitch / 180 * 3.141592654f),
yRot = (Yaw / 180 * 3.141592654f);
float sinX = float(sin(xRot)) * myInput.Sensitivity,
sinY = float(sin(yRot)) * myInput.Sensitivity,
cosY = float(cos(yRot)) * myInput.Sensitivity;
if(myInput.Keys['W']) //Forwards
{
curPos.x += sinY;
curPos.z -= cosY;
curPos.y += sinX;
}
else if(myInput.Keys['S']) //Backwards
{
curPos.x -= sinY;
curPos.z += cosY;
curPos.y -= sinX;
}
if(myInput.Keys['A']) //Left
{
curPos.x -= cosY;
curPos.z -= sinY;
}
else if(myInput.Keys['D']) //Right
{
curPos.x += cosY;
curPos.z += sinY;
}
//Move camera up and down
if(myInput.Keys['Q'])
curPos.y-= myInput.Sensitivity; //up
else if(myInput.Keys['E'])
curPos.y+= myInput.Sensitivity; //down
//Check col
if(curPos.y < 0)
curPos.y = 0;
else if(curPos.y >= 300) //Gimbal lock encountered
curPos.y = 299;
Secondly, heres my movement for calculating gluLookAt:
double cosR, cosP, cosY; //temp values for sin/cos from
double sinR, sinP, sinY; //the inputed roll/pitch/yaw
cosY = cosf(Yaw*3.1415/180);
cosP = cosf(Pitch*3.1415/180);
cosR = cosf(Roll*3.1415/180);
sinY = sinf(Yaw*3.1415/180);
sinP = sinf(Pitch*3.1415/180);
sinR = sinf(Roll*3.1415/180);
//forward position
forwardPos.x = sinY * cosP*360;
forwardPos.y = sinP * 360;
forwardPos.z = cosP * -cosY*360;
//up position
upPos.x = -cosY * sinR - sinY * sinP * cosR;
upPos.y = cosP * cosR;
upPos.z = -sinY * sinR - sinP * cosR * -cosY;
Basically I've noticed that if the camera goes above 300 in the Y-Axis then the view starts rotating, the same thing happens if I move too far in the x/z axis.
Can anyone see whats wrong with my code? Am I working in deg when I should be working in rad?
I haven't checked your code but this type of issue is notorious when using Euler angles for 3D rotations.
It's called gimbal lock (I see you have a comment in your code that shows you're aware of it) and the easiest solution to overcoming it is to switch to using quaternions to calculate your rotation matrices.
There's a really good OpenGL article on the Wiki here.