Direction to angle - c++

I'm not very good at math( and i never was ) and I've a problem: I'm trying to make sprite always rotated to mouse cursor. That's what i have:
// mx is mouse X and my is mouse y
double rotate = (atan2(my, mx) * PI);
// Set rotation takes rotation in degrees
ship.SetRotation( rad2deg(rotate) );
Where deg2rad function is:
double rad2deg(double rad)
{
double deg = 0;
deg = rad * (180/M_PI);
return deg;
}
Unfortunately it is not working. The ship is rotating very weird ( really hard to define that ). And I don't have any idea to solve this problem.
I'm working on SFML and SetRotation takes degrees.
Thanks in advance.

You need to define the rotation relative to some point. Currently you're doing it relative to the corner of the screen, which will only give you 90 degrees and is probably not what you want. You probably want rotation relative to the sprite's location or perhaps relative to the centre of the screen, e.g.
// define centre of screen
const int Y0 = SCREEN_HEIGHT / 2;
const int X0 = SCREEN_WIDTH / 2;
// get rotation angle of mouse location relative to centre of screen
double rotate = (float) (atan2(Y0 - my), ((mx - X0));
[As others have noted, e.g. #duffymo, you may also have the arguments to atan2 transposed, so I've made this change also.]

It seems you have wrong argument order for atan2. And also the values you pass to atan2 should be relative mouse position to the object. So if object position is ox, oy, then you should use atan2(my-oy, mx-ox).

I wonder if you have the arguments to atan2 reversed. The typical order is y first, then x. Are you sure you know what you're passing to that method?
"not working" and "weird" don't help us understand what the problem is, so it's hard to help you.

Related

Drawing a sprite on the circumference of a circle based on the position of other objects

I'm making a sniper shooter arcade style game in Gamemaker Studio 2 and I want the position of targets outside of the viewport to be pointed to by chevrons that move along the circumference of the scope when it moves. I am using trig techniques to determine the coordinates but the chevron is jumping around and doesn't seem to be pointing to the target. I have the code broken into two: the code to determine the coordinates in the step event of the enemies class (the objects that will be pointed to) and a draw event in the same class. Additionally, when I try to rotate the chevron so it also points to the enemy, it doesn't draw at all.
Here's the coordinate algorithm and the code to draw the chevrons, respectively
//determine the angle the target makes with the player
delta_x = abs(ObjectPlayer.x - x); //x axis displacement
delta_y = abs(ObjectPlayer.y - y); //y axis displacement
angle = arctan2(delta_y,delta_x); //angle in radians
angle *= 180/pi //angle in radians
//Determine the direction based on the larger dimension and
largest_distance = max(x,y);
plusOrMinus = (largest_distance == x)?
sign(ObjectPlayer.x-x) : sign(ObjectPlayer.y-y);
//define the chevron coordinates
chevron_x = ObjectPlayer.x + plusOrMinus*(cos(angle) + 20);
chevron_y = ObjectPlayer.y + plusOrMinus*(sign(angle) + 20);
The drawing code
if(object_exists(ObjectEnemy)){
draw_text(ObjectPlayer.x, ObjectPlayer.y-10,string(angle));
draw_sprite(Spr_Chevron,-1,chevron_x,chevron_y);
//sSpr_Chevron.image_angle = angle;
}
Your current code is slightly more complex that it needs to be for this, if you want to draw chevrons pointing towards all enemies, you might as well do that on spot in Draw. And use degree-based functions if you're going to need degrees for drawing anyway
var px = ObjectPlayer.x;
var py = ObjectPlayer.y;
with (ObjectEnemy) {
var angle = point_direction(px, py, x, y);
var chevron_x = px + lengthdir_x(20, angle);
var chevron_y = py + lengthdir_y(20, angle);
draw_sprite_ext(Spr_Chevron, -1, chevron_x, chevron_y, 1, 1, angle, c_white, 1);
}
(also see: an almost-decade old blog post of mine about doing this while clamping to screen edges instead)
Specific problems with your existing code are:
Using a single-axis plusOrMinus with two axes
Adding 20 to sine/cosine instead of multiplying them by it
Trying to apply an angle to sSpr_Chevron (?) instead of using draw_sprite_ext to draw a rotated sprite.
Calculating largest_distance based on executing instance's X/Y instead of delta X/Y.

Rotation Angle of Sprite towards Mouse is only calculated but Sprite does not rotate. But, it works well in Debugger

I am a beginner in C++ and know SFML. I have read a lot of articles about Sprite rotation towards Mouse and have been able to calculate proper angle of rotation of Sprite using this code:
void Player::changeAngle(double Mx, double My) { // This is for making Player point towards Mouse
double dX = x - Mx; // x and y are global Varibales declared outside
double dY = y - My;
double magnitude = sqrt((dX*dX) + (dY*dY));
double normalizedX = dX / magnitude;
double normalizedY = dY / magnitude;
// This part is for Making Player move to Mouse
Xvelocity = normalizedX * speed; // Xvelocity, Yvelocity and speed are global variables
Yvelocity = normalizedY * speed;
// Rotate towards the Mouse
double angle = ( atan2(dY, dX) ) * 180 / M_PI;
entity.setRotation(angle); // entity is a sf::RectangleShape
}
And, this bit actually worked and the Sprite was rotating properly only before a few days. Now the Sprite is just frozen on the screen, nothing happens. I had not made any changes in that code but I had added a View from the main.cpp.
So here is how I call changeAngle() from the main.cpp :
case Event::MouseMoved : {
Vector2f worldPos = window.mapPixelToCoords(Mouse::getPosition(window), view);
double x = worldPos.x;
double y = worldPos.y;
player.changeAngle(x, y);
break;
}
I tried running the debugger and stepped through the code. I found that the angle is properly calculated and my mouse coordinates are also right. And, to my surprise, the sprite was Rotating now !
I could not understand why, but every time its in debugging mode, the Sprite will rotate. But, it won't rotate while running normally.
Can anyone tell me why and how to fix it so it rotates every time ? Tell me if there is any problem with my code.
Thanks !

CDC::Ellipse doesn't draw correctly circles

In a project of mine (VC++2010, MFC), I want to draw a circle using the CDC::Ellipse. I set two points: the first one is the center of the circle, the second one is a point I want it to be on the circumference.
I pass to the CDC::Ellipse( int x1, int y1, int x2, int y2 ) the coordinates of the upper-left corner and lower-right one.
Briefly: with Pitagora Theorem I calculate the distance between the two points ( radius ), then I subtract this value from the coordinates of the center to obtain the upper-left corner and add to obtain the lower-right one.
When I draw the cirlce and the points, and I zoom in, I see that the second one isn't on the circumference as expected, it is slightly inside unless you set it at 0°, 45°, 90° and so on with respect to the absolute sistem of coordinates.
Then I tried to draw the same circle using CDC::Polyline, I gave to this method the points obtained rotating another point around the center, at the distance equal to the radius. In this case the point is on the circumference every where I set it.
The overlap of these two circles has shown that they perfectly overlap at 0°, 45°, 90° and so on, but the gap is maximum at 22.5°, 67.5° and so on.
Has anyone ever noticed a similar behavior?
Thanks to everybody that can help me!
Code snippet:
this is how I calculate the radius given 2 points:
centerPX = vvFPoint( 1380, 845 );
secondPointPX = vvFPoint( 654,654 );
double radiusPX = (sqrt( (secondPointPX.x - centerPX.x) * (secondPointPX.x - centerPX.x) + (secondPointPX.y - centerPX.y) * (secondPointPX.y - centerPX.y) ));
( vvFPoint is a custom type derived from CPoint )
this is how I draw the "circle" with the CDC::Ellipse:
int up = (int)(((double)(m_p1.y-(double)originY - m_radius) / zoom) + 0.5) + offY;
int left = (int)(((double)(m_p1.x-(double)originX - m_radius) / zoom) + 0.5) + offX;
int down = (int)(((double)(m_p1.y-(double)originY + m_radius) / zoom) + 0.5) + offY;
int right = (int)(((double)(m_p1.x-(double)originX + m_radius) / zoom) + 0.5) + offX;
pDC->Ellipse( left, up, right, down);
(m_p1 is the center of the circle, originX/Y is the origin of the image, m_radius is the radius of the circle, zoom is the scale factor, offX/Y is an offset in the client area of my SW)
this is how I draw the circle "manually" (and quite trivial method) using a custom polyline class:
1) create the array of points:
point.x = centerPX.x + radiusPX;
point.y = centerPX.y;
for ( i=0; i < 3600; i++ )
{
pt1.RotateDeg ( centerPX, (double)0.1 );
poly->AddPoint( pt1 );
}
(RotateDeg is a custom method to rotate a point using first argument as a pivot and second argument as angle value in degrees, AddPoint is a custom method to create the array of points, poly is my custom polyline object).
2) draw it:
When I call the Draw( CDC* pDC ) I use the previous array to draw the polyline:
pDC->MoveTo(p);
I hope this can help you to reproduce my weird observations!
code snippet 2:
void vvPoint<Tipo>::RotateDeg(const vvPoint<Tipo> &center, double angle)
{
vvPoint<Tipo> ptB;
angle *= -(M_PI / 180);
*this -= center;
ptB.x = ((this->x * cos(angle)) - (this->y * sin(angle)));
ptB.y = ((this->x * sin(angle)) + (this->y * cos(angle)));
*this = ptB + center;
}
But to let you better understand my observations I would like to add a few images so you can see where my whole question started from... The problem is: I can't add images since I need to have 10 reputation. I uploaded a .zip file on dropbox and if you want I can send you the URL of this file. Let me know if this is the correct (and safe..) way to bypass this problem.
Thanks!
This might be a possible explanation. As MSDN says about CDC::Ellipse (with my emphasis):
The center of the ellipse is the center of the bounding rectangle
specified by x1, y1, x2, and y2, or lpRect. The ellipse is drawn with
the current pen, and its interior is filled with the current brush.
The figure drawn by this function extends up to, but does not include,
the right and bottom coordinates. This means that the height of the
figure is y2 – y1 and the width of the figure is x2 – x1.
The way you described how you calculate the bounding rectangle is not entirely clear (some source code would have helped) but, given the second paragraph quoted above, you possibly need to add 1 to your x2 and y2 values, to make sure you have a circle with the desired radius.
It's also worth noting that there may be slight rounding differences between your two drawing methods where you have an odd-sized bounding box (i.e. so the centre point falls logically on a half-pixel).
UPDATE
Using your code snippets (thanks), and assuming no zoom and zero offsets etc., I get a radius of 750.704 pixels and the following parameters for the ellipse:
pDC->Ellipse(629, 94, 2131, 1596);
According to MSDN, this means that the ellipse will be drawn in a figure of the following dimensions:
width = (2131 - 629) = 1502
height = (1596 - 94) = 1502
So as far as I can see, this should produce a circle rather than an ellipse.
The next thing to do is to find out how you're drawing the polygon - for that we need to see the implementation of RotateDeg - can you post that code? I'm suspecting some simple rounding error here, that maybe gets magnified when you zoom.
UPDATE 2
Just looking at this code:
for ( i=0; i < 3600; i++ )
{
pt1.RotateDeg ( centerPX, (double)0.1 );
poly->AddPoint( pt1 );
}
You are rotating your polygon points incrementally by 0.1 degrees each time. This will possibly accumulate some errors, so it may be worth doing it something like this instead:
for ( i=0; i < 3600; i++ )
{
vvFPoint ptNew = pt1;
ptNew.RotateDeg ( centerPX, (double)i * 0.1 );
poly->AddPoint( ptNew );
}
Maybe this will mean you have to change your RotateDeg function to take care of the correct quadrants.
One other point, you mentioned that you see the problem when you zoom into the image. If this means you are using you zoom variable, it is worth checking in this line ...:
pDC->Ellipse( left, up, right, down);
... that the parameters still form a square shape, so (right - left) == (down - up).
UPDATE 3
I just ran your RotateDeg function, in its current form, to see how the error accumulates (by feeding in the previous result to the next iteration). At each step, I calculated the distance between the new point and the centre and compared this with the required radius.
The chart below shows the result, where you can see an error of 4 pixels by the time the points have been calculated.
I think that this at least explains part of the difference (i.e. your polygon drawing is flawed) and - depending on zoom - you may introduce asymmetry into the ellipse parameters, which you can debug by comparing the width to the height as I described above.

How to properly move the camera in the direction it's facing

I'm trying to figure out how to make the camera in directx move based on the direction it's facing.
Right now the way I move the camera is by passing the camera's current position and rotation to a class called PositionClass. PositionClass takes keyboard input from another class called InputClass and then updates the position and rotation values for the camera, which is then passed back to the camera class.
I've written some code that seems to work great for me, using the cameras pitch and yaw I'm able to get it to go in the direction I've pointed the camera.
However, when the camera is looking straight up (pitch=90) or straight down (pitch=-90), it still changes the cameras X and Z position (depending on the yaw).
The expected behavior is while looking straight up or down it will only move along the Y axis, not along the X or Z axis.
Here's the code that calculates the new camera position
void PositionClass::MoveForward(bool keydown)
{
float radiansY, radiansX;
// Update the forward speed movement based on the frame time
// and whether the user is holding the key down or not.
if(keydown)
{
m_forwardSpeed += m_frameTime * m_acceleration;
if(m_forwardSpeed > (m_frameTime * m_maxSpeed))
{
m_forwardSpeed = m_frameTime * m_maxSpeed;
}
}
else
{
m_forwardSpeed -= m_frameTime * m_friction;
if(m_forwardSpeed < 0.0f)
{
m_forwardSpeed = 0.0f;
}
}
// ToRadians() just multiplies degrees by 0.0174532925f
radiansY = ToRadians(m_rotationY); //yaw
radiansX = ToRadians(m_rotationX); //pitch
// Update the position.
m_positionX += sinf(radiansY) * m_forwardSpeed;
m_positionY += -sinf(radiansX) * m_forwardSpeed;
m_positionZ += cosf(radiansY) * m_forwardSpeed;
return;
}
The significant portion is where the position is updated at the end.
So far I've only been able to deduce that I have horrible math skills.
So, can anyone help me with this dilemma? I've created a fiddle to help test out the math.
Edit: The fiddle uses the same math I used in my MoveForward function, if you set pitch to 90 you can see that the Z axis is still being modified
Thanks to Chaosed0's answer, I was able to figure out the correct formula to calculate movement in a specific direction.
The fixed code below is basically the same as above but now simplified and expanded to make it easier to understand.
First we determine the amount by which the camera will move, in my case this was m_forwardSpeed, but here I will define it as offset.
float offset = 1.0f;
Next you will need to get the camera's X and Y rotation values (in degrees!)
float pitch = camera_rotationX;
float yaw = camera_rotationY;
Then we convert those values into radians
float pitchRadian = pitch * (PI / 180); // X rotation
float yawRadian = yaw * (PI / 180); // Y rotation
Now here is where we determine the new position:
float newPosX = offset * sinf( yawRadian ) * cosf( pitchRadian );
float newPosY = offset * -sinf( pitchRadian );
float newPosZ = offset * cosf( yawRadian ) * cosf( pitchRadian );
Notice that we only multiply the X and Z positions by the cosine of pitchRadian, this is to negate the direction and offset of your camera's yaw when it's looking straight up (90) or straight down (-90).
And finally, you need to tell your camera the new position, which I won't cover because it largely depends on how you've implemented your camera. Apparently doing it this way is out of the norm, and possibly inefficient. However, as Chaosed0 said, it's what makes the most sense to me!
To be honest, I'm not entirely sure I understand your code, so let me try to provide a different perspective.
The way I like to think about this problem is in spherical coordinates, basically just polar in 3D. Spherical coordinates are defined by three numbers: a radius and two angles. One of the angles is yaw, and the other should be pitch, assuming you have no roll (I believe there's a way to get phi if you have roll, but I can't think of how currently). In conventional mathematics notation, theta is your yaw and phi is your pitch, with radius being your move speed, as shown below.
Note that phi and theta are defined differently, depending on where you look.
Basically, the problem is to obtain a point m_forwardSpeed away from your camera, with the right pitch and yaw. To do this, we set the "origin" to your camera position, obtain a spherical coordinate, convert it to cartesian, and then add it to your camera position:
float radius = m_forwardSpeed;
float theta = m_rotationY;
float phi = m_rotationX
//These equations are from the wikipedia page, linked above
float xMove = radius*sinf(phi)*cosf(theta);
float yMove = radius*sinf(phi)*sinf(theta);
float zMove = radius*cosf(phi);
m_positionX += xMove;
m_positionY += yMove;
m_positionZ += zMove;
Of course, you can condense a lot of this code, but I expanded it for clarity.
You can think about this like drawing a sphere around your camera. Each of the points on the sphere is a potential position in the next timestep, depending on the camera's rotation.
This is probably not the most efficient way to do it, but in my opinion it's certainly the easiest way to think about it. It actually looks like this is nearly exactly what you're trying to do in your code, but the operations on the angles are just a little bit off.

2d rotation around point

I'm trying to add some features into my editor that allow to the user things like dragging a selected item to a given direction
although i've ran into a damn issue.
This is my code :
//Origin
double objectx = selection->getX();
double objecty = selection->getY();
//Point
double pointerx = input->getMouseX();
double pointery = input->getMouseY();
//Displacement
double displacementx = fabs(pointerx - objectx);
double displacementy = fabs(pointery - objecty);
//Angle
double angle = atan2(displacementy,displacementx);
//Point
double pointx = displacementx * cosf(angle) + displacementy * sinf(angle);
double pointy = displacementy * cosf(angle) - displacementx * sinf(angle);
//Final position
double fx = objectx + pointx;
double fy = objecty + pointy;
//Save alpha
const bool alpha = graphics->getAlpha();
//Draw selection
graphics->setAlpha(false);
graphics->color(selection->getColor());
graphics->renderQd(selection->getBitmap(),
CRect(objectx,
objecty,
selection->getWidth(),
selection->getHeight()));
//Draw pointer around selection
graphics->setAlpha(true);
graphics->color(editor::ssImg[0]->getColor());
graphics->renderQd(editor::ssImg[0]->getBitmap(),
CRect(objectx + pointx,
objecty + pointy,
editor::ssImg[0]->getWidth(),
editor::ssImg[0]->getHeight()));
//Restore alpha
graphics->setAlpha(alpha);
The exact issue is that the selection pointer doesn't follow mouse's rotation only but its actually at mouse's position(!).
The wanted behaviour is a pointer locked at selection's offset but pointing to the mouse's angle.
Anyone good at math sees anything wrong here ?
As I understand it, your wanted behavior presumes the existence of three points: an origin around which you're rotating, a "mouse" to provide the direction relative to the origin, and a "selection" to provide the distance from the origin. (Somewhat confusingly, the result of your code is the "selection pointer". I take it that "selection" means the original position of the selected object, and "selection pointer" means the position it's been dragged to so far?)
Your code, however, only actually refers to two of those points: (objectx, objecty), which I assume is the origin, and (pointerx, pointery), which I assume is the "mouse". Your code never refers to the "selection"; so, naturally, the "selection" has no effect on the result of your code.
There are a few other problems — Oli Charlesworth points out, in a comment above, that you're wrongly dividing your angle by π/180, which means that you apply a very small rotation (which is why it looks like you end up with selection pointer = mouse; in fact, they can be up to a few degrees apart relative to the origin of rotation, but that's not instantly noticeable) — but rather than fix those problems, I'd recommend you change your approach. Instead of generating "selection pointer" by rotating "selection" to match the angle of "mouse", I recommend that you generate it by scaling "mouse" to match the magnitude of "selection". The math for this is more straightforward, IMHO.
If you do want to stick with the approach of generating "selection pointer" by rotating "selection" to match the angle of "mouse", then you have two main things to fix. Your current code rotates "mouse" by the current angle of "mouse". Part of the fix is to rotate "selection" instead; the other part of the fix is to rotate by the difference between the current angles of "mouse" and "selection".