Search code examples
c#xnagame-physics

C# XNA Movement - Can either turn or move, not both


I'm trying to make an XNA spaceship game, where I can turn, and fire guns and such. I ran into a problem trying to get it so that I can give a ship a waypoint. For some reason, I can get the ship to move, or I can get it to turn, but I can't seem to get it to do both at once. It's really strange.

Basically, if I comment out my rotation code, it will fly straight, otherwise it will turn, but it doesn't move it just stays in place.

 if (GoToWayPoint == true)
 {

     //CurrentWayPoint.Normalize();
     Pos.Normalize();

     float angle = (float)Math.Atan2(CurrentWayPoint.X - Pos.X, CurrentWayPoint.Y - Pos.Y);

     //Rotate if 
     if (Rotation > angle && Rotation - TurnSpeed >= angle)
     {
         Rotation -= TurnSpeed;
     }
     else if (Rotation < angle && Rotation + TurnSpeed <= angle)
     {
         Rotation += TurnSpeed;
     }
     else
     {
         Rotation = angle;
     }

     //Move forward if it's facing the right direction
     if (Rotation == angle)
     {
         Velocity.X += (float)Math.Cos(Rotation) * TangentialVelocity * Speed;
         Velocity.Y += (float)Math.Sin(Rotation) * TangentialVelocity * Speed;
     }        

     if (Pos == CurrentWayPoint || Vector2.Distance(Pos,CurrentWayPoint) < 10)
     {
         Pos = CurrentWayPoint;

         GoToWayPoint = false;
     }

 }

 //Movement, player controlled
 if (PlayerControlled == true)
 {
     KeyboardState NewKey = Keyboard.GetState();

     //Move Up
     if (NewKey.IsKeyDown(Keys.Up) || NewKey.IsKeyDown(Keys.W))
     {
         Velocity.X = (float)Math.Cos(Rotation) * TangentialVelocity * Speed;
         Velocity.Y = (float)Math.Sin(Rotation) * TangentialVelocity * Speed;
     }

     //Move Down
     if (NewKey.IsKeyDown(Keys.Down) || NewKey.IsKeyDown(Keys.S))
     {
         Velocity.X = (float)Math.Cos(Rotation + Math.PI) * TangentialVelocity;
         Velocity.Y = (float)Math.Sin(Rotation + Math.PI) * TangentialVelocity;
     }

     //Move Right
     if (NewKey.IsKeyDown(Keys.Right) || NewKey.IsKeyDown(Keys.D))
     {
         Rotation += TurnSpeed;
     }

     //Move Left
     if (NewKey.IsKeyDown(Keys.Left) || NewKey.IsKeyDown(Keys.A))
     {
         Rotation -= TurnSpeed;
     }
}

//Update Movement
Pos += Velocity;

//Friction
float i = Velocity.X;
float j = Velocity.Y;

Velocity.X = i -= Friction * i;
Velocity.Y = j -= Friction * j;

Additionally, I have code where I can control the GameObject using the arrow keys. That code works fine, and uses the same type code for moving forward.


Solution

  • Your problem lies mostly in this line:

    if (Rotation == angle)
    

    Your ship will only go forward when that conditional evaluates to true. Unfortunately it will never be true, because angle is a float. It is very hard to get two floating point numbers to equal each other unless you specifically assign them the same value (There are other ways, but they're irrelevant in this case).

    You calculate angle here:

    float angle = (float)Math.Atan2(CurrentWayPoint.X - Pos.X, CurrentWayPoint.Y - Pos.Y);
    

    This will in all likelihood give you a value with a lot of digits after the decimal point, like 1.519573617.... On top of that, you're only turning your ship by some fixed amount, TurnSpeed, which will always skip over your precise directional value, change angular direction, and skip over it again, ad infinitum. That's why you never go forward.

    There are two solutions I can think of off the top of my head that you can try:

    One is to lose the if-statement up there. Then your craft will start going, albeit in the wrong direction, but it will adjust its direction as it moves.

    The other is to use an epsilon to compare Rotation with angle. It would look like this:

    if (Math.Abs(Rotation - angle) < epsilon)
    {
        Rotation = angle;
        // move forward
    }
    

    This gives you a bit of wiggle room when comparing the floats, namely an epsilon amount of wiggle room. It will have to be large enough so that your TurnSpeed won't jump over the entire interval, but small enough so that it's not too noticeably different from the exact direction.