0
votes

I am trying to ease (gradually move) the rotation of an object to an arbitrary position. The angle of rotation is determined by a Virtual Thumbstick class which returns X/Y coordinates between -1 and 1. If there is no movement on the thumbstick, I am rotating back to point to 0, except I am compensating for the angle of the sprite's image.

The problem I am having is that this code will only allow approximately 1.5 rotations (anywhere between -3*PI and 3*PI) instead of continuous rotation. Using Math.Atan2 with the X/Y coords of the thumbsticks, the returned angle is constrained between -PI and PI but allows continuous rotation. Also, if I rotate the object in one direction and release the thumbstick, it will rotate back to top from the direction it came. I want it to rotate back to the top on the shortest route.

if (VirtualThumbsticks.LeftThumbstick.Length() > .2f)
{
    double rotateTo = Math.Atan2(VirtualThumbsticks.LeftThumbstick.Y, VirtualThumbsticks.LeftThumbstick.X);

    if (rotateTo > Rotation + Math.PI) rotateTo -= (Math.PI * 2);
    if (rotateTo < Rotation - Math.PI) rotateTo += (Math.PI * 2);

    Rotation += (rotateTo - Rotation) * 0.2;
}
else
{
    Rotation += (-1.57079 - Rotation) *0.2;
}

If there are any Flash/ActionScript game developers that know what I'm talking about, please chime in as I can apply that to C#.

Thanks in advance, everyone!

EDIT:

This chunk of code works flawlessly in AS3:

function enterFrameHandler(e:Event):void
{
    var curMouseX = Math.round(-(arrow.x - stage.mouseX));//(stage.stageWidth/2)-(stage.mouseX/2);
    var curMouseY = Math.round(-(arrow.y - stage.mouseY));//(stage.stageHeight/2)-(stage.mouseY/2);
    var angleTo:Number = Math.atan2(curMouseX, -curMouseY) * TO_DEGREES;
    if (angleTo > arrow.rotation+180) angleTo -= 360;
    if (angleTo < arrow.rotation-180) angleTo += 360;

    tf_angle.text = angleTo.toString();
    tf_mouseX.text = curMouseX.toString();
    tf_mouseY.text = curMouseY.toString();

    arrow.rotation += (angleTo - arrow.rotation) * 0.2;
}

I'm beginning to wonder if there is an issue with my types or typecasting that is causing the problem. If anyone has any ideas, your input is greatly appreciated.

2
@Tigran - The rotation is not continuous. It hits a wall after rotating 1.5 times in either direction and snaps back. Also, when letting go of the thumbstick, it snaps back to 0 (or -1.57097 in this case) from the direction it came. Please re-read the question for clarification.ImKevinJones

2 Answers

0
votes

This is written in C# using XML, so the actionscript parallel may not apply, but i bet that it does. In Flash, rotations are adjusted at the end of every frame, because there is little benefit in storing a rotation transform of 123456789 degrees. so the transform resets to the modulo value of the rotation.

However, in calculating shortest rotation, you can run into problems if you are expecting some large number and comparing it to some other number instead of calculating their relative positions using their modulo values.

Maybe this is the source of the problem. its just a guess.

On the odd chance you are not familiar with it, the modulo operator - aka percent sign - "%" will give you the remainder of division. this is helpful for numbers that loop, like grid alignments, and rotations.

Example:

21 % 5 = 1;
 720 % 360 = 0;
0
votes

The issue has been solved. The problem was lying in the rotation value not being "normalized". Basically, if the rotation value exceeds Math.PI*2, it must return to 0... and if the rotation value falls below 0, it must return to Math.PI*2.

The last comment on this page has solved my problem and the rotation is normalized now.

http://forums.create.msdn.com/forums/t/27527.aspx

My resulting code:

if (VirtualThumbsticks.LeftThumbstick.Length() > .2f)
{
    float angleTo = (float)Math.Atan2(VirtualThumbsticks.LeftThumbstick.Y, VirtualThumbsticks.LeftThumbstick.X);

    if (angleTo > Rotation + Math.PI) angleTo -= (float)(Math.PI * 2);
    if (angleTo < Rotation - Math.PI) angleTo += (float)(Math.PI * 2);

    accelerationRotation += (angleTo - Rotation) * (float)0.25;

}
else
{
    accelerationRotation += ((float)(Math.PI / 2) - Rotation) * (float)0.2;
}

Rotation = Wrap(accelerationRotation, 0, (float)Math.PI * 2);

The Wrap function is as follows:

public static float Wrap(float value, float lower, float upper)
{
    unchecked
    {
        if (lower >= upper)
        {
            throw new ArithmeticException("rotary bounds out of negative or zero size");
        }

        float distance = upper - lower;

        float times = (float)Math.Floor((value - lower) / distance);

        return value - (times * distance);
    }
}