How do I calculate if I should be adding or subtracting rotation?

Started by
16 comments, last by SimonForsman 12 years ago
(I'm an XNA/C# Newbie).

I currently have an Asteroids-style character that is controlled by the mouse.

However, I'm having an issue when my ship's movement would have it turn across the "west" line.

mvlufm.jpg

This is my current code for rotating:


if (destAngle - rotation <= 0)
{
rotation -= movingRotationSpeed;
}
else
{
rotation += movingRotationSpeed;
}


However, an issue arises when the mouse crosses over the "west" line (with respect to the sprite). The destAngle changes from -3.140 to +3.140 (radians/PI). If I'm going clockwise at the time, the ship will then do a massive anti-clockwise movement (and vice verse).

What computation should I be doing to determine if the ship should move clockwise or anti-clockwise given I have its current angle and the destination angle?

Here is my entire code segment for that method:


MouseState mouseState = Mouse.GetState();
Vector2 mouseLoc = new Vector2(mouseState.X, mouseState.Y);

Vector2 direction = position - mouseLoc;
destAngle = (float)(Math.Atan2(-direction.Y, -direction.X));

if (mouseState.LeftButton == ButtonState.Pressed)
{
Console.WriteLine(rotation + " + " + destAngle);

if (destAngle - rotation <= 0)
{
rotation -= movingRotationSpeed;
}
else
{
rotation += movingRotationSpeed;
}

if (position.X == mouseState.X && position.Y == mouseState.Y)
{ }
else
{
//The ship always moves "forward".
position.X += speed * (float)(Math.Cos(rotation));
position.Y += speed * (float)(Math.Sin(rotation));
}
}
Advertisement
If what I think you mean is correct, you cannot have a naturally smooth rotation transition if you only use velocity (= rotation). You need to implement acceleration in order to obtain smooth transitions. Basically instead of deriving your velocity from the mouse position, you derive your acceleration from the mouse position, add the acceleration to the velocity, and finally add the velocity to the resulting position. This will give you more or less smooth changes of direction depending on how strong you make the acceleration.

Also you do not need angles in your code here. If you look closely you are doing an inverse tangent of your mouse coordinates to obtain the rotation angle, and then proceed to undo that operation by reconstructing the rotation vector with sin and cos. You could simply normalize the rotation vector and use that directly. You will need to do that to implement acceleration as it is much nicer with vectors than with angles.

“If I understand the standard right it is legal and safe to do this but the resulting value could be anything.”

Not what I mean at all. I'm asking for what calculation I need to compare the current angle and the destination angle in order to determine whether to rotate the ship clockwise or counterclockwise.
What I would do is something along the lines of the following:

targetRotation;
otherTargetRotation = (targetRotation < 0) ? targetRotation + 2*PI : targetRotation - 2*PI;
if(abs(targetRotation - rotation) > abs(otherTargetRotation - rotation)
{
//go one way
} else
{
//go the other
}


Note: This is totally untested, but sounds good in my head.
otherTargetRotation = (targetRotation < 0) ? targetRotation + 2*PI : targetRotation - 2*PI;


I'm not sure I understand this syntax? But that aside.

I've disabled forward motion for the purposes of testing without it going off the screen.

Your solution has seemingly worsened the problem. Whenever the destination is above the X-axis, it turns to face left and then stops rotating. Now given that it is impossible for it to not rotate (there's no if condition where it doesn't rotate): that means that it reaches an argument where both statements are true, and the ship is turning up then down again instantly.
The question mark colon is a ternary operator for simplifying an if else block used for assignment.

http://en.wikipedia.org/wiki/Ternary_operation
What that line says is if the targetRotation is less than 0 set the otherTargetRotation to targetRotation + 2PI; otherwise, set it to targetRotation-2PI.

Here is complete working code with the missing bits filled in. This has been tested and works.


#include <iostream>
#include <cmath>
using namespace std;
#define PI 3.14159265
int main()
{
cout << "Current Rotation?\n";
float currentRotation;
cin >> currentRotation;
cout << "Target Rotation?\n";
float targetRotation;
cin >> targetRotation;
while(currentRotation != targetRotation) //rotate
{
int direction = targetRotation - currentRotation > 0 ? 1 : -1;
float otherTargetRotation = targetRotation < 0 ? targetRotation + 2*PI : targetRotation - 2*PI;
cout << "Target: " << targetRotation << endl;
cout << "Other: " << otherTargetRotation << endl;
char c;
cin >> c;
if(abs(targetRotation - currentRotation) > abs(otherTargetRotation - currentRotation))
{
direction *= -1;
}
currentRotation+=.1*(float)direction;
if(currentRotation > PI) currentRotation-= 2*PI;
if(currentRotation < PI*-1.0f) currentRotation+=2*PI;
if(abs(targetRotation-currentRotation) < 0.1) currentRotation = targetRotation;
cout << "Current Rotation: " << currentRotation << endl;
}
}


You will obviously want to set your own thresh hold and velocity, and remove the input/output statements.

EDIT: Some of my code was cut-off in my copy-paste. It should now be fixed.
While I'm thankful for it, I don't understand your help. It's both in a different language, and I'm at a loss to translate it because I don't know what <<, >>, cin, cout, etc, do.
Zael's method is written in C++ rather than C#. He uses cin and cout to set the variables for the rotation.




cout << "Current Rotation?\n";
float currentRotation;
cin >> currentRotation;
cout << "Target Rotation?\n";
float targetRotation;
cin >> targetRotation;

>> sets the value entered on the command line into the variable that follows the >>. Similarly bout << prints whatever follows into a command prompt.

You will want to remove all the cin and cout anyways and set the variables according to your game. The important part to understand is



while(currentRotation != targetRotation) //rotate
{
int direction = targetRotation - currentRotation > 0 ? 1 : -1;
float otherTargetRotation = targetRotation < 0 ? targetRotation + 2*PI : targetRotation - 2*PI;
if(abs(targetRotation - currentRotation) > abs(otherTargetRotation - currentRotation))
{
direction *= -1;
}
currentRotation+=.1*(float)direction;
if(currentRotation > PI) currentRotation-= 2*PI;
if(currentRotation < PI*-1.0f) currentRotation+=2*PI;
if(abs(targetRotation-currentRotation) < 0.1) currentRotation = targetRotation;
}

as that is the part that does the actual rotation. What do you not understand in this part?

[background=rgb(250, 251, 252)]Sorry, I was editing that post as you wrote. I managed to successfully translate the code, after Googling all the syntax I didn't understand. But thank you for also clarifying.[/background]


___

If I use the while loop as provided in the code, the ship will instantly turn to face the cursor with no "turning" actually present. It just always looks at the cursor. However, while using the while-loop, the rotation itself is perfect.

However, changing the while to an if-loop gives me the "turning" I'm looking for. However, the issue I had with my own code is exactly the same in this code. Upon attempting to cross the west-axis, the ship attempts to do a 359-degree spin in the opposite direction. With forward motion enabled (rather than a stationary sprite), the ship just ~~~~~~~~ in a straight line.

This topic is closed to new replies.

Advertisement