1
votes

I'm trying to rotate a sprite to face the mouse by incrementing and decrementing the Rotation using a turning radius. It works fine up until the point where the mouse is top-left and moves to the top-right. I have it so that if the angle difference is greater than the current Rotation value, the sprite's Rotation gets incremented, otherwise it gets decremented. So when it goes from 6.5 radians to 0, it rotates counterclockwise by 350-some degrees, instead of clockwise by 15-some degrees. Me and others have been working on this all day and have no idea how to solve this. My code is below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using System.IO;

namespace Physics
{
public class Ship
{
    public Texture2D Texture { get; set; }
    public Vector2 Position { get; set; }
    public double Rotation { get; set; }
    MouseState prevState, currState;

    Vector2 A, B;

    public const double NINETY_DEGREES = 1.57079633;

    double turningRadius = 2 * (Math.PI / 180);
    double targetRotation, rotationDifferential;

    public Ship(Texture2D texture, Vector2 position)
    {
        Texture = texture;
        Position = position;
        A = new Vector2(Position.X, Position.Y);
        B = new Vector2(Mouse.GetState().X, Mouse.GetState().Y);
        Rotation = 0;
    }

    public void Update()
    {
        currState = Mouse.GetState();
        A.X = Position.X;
        A.Y = Position.Y;
        B.X = currState.X;
        B.Y = currState.Y;

        if (B.Y > A.Y)
            if (B.X > A.X) //Bottom-right
                targetRotation = Math.Atan((B.Y - A.Y) / (B.X - A.X)) + NINETY_DEGREES;
            else //Bottom-left
                targetRotation = (Math.Atan((A.X - B.X) / (B.Y - A.Y))) + (NINETY_DEGREES * 2);
        else
            if (B.X > A.X) //Top-right
                targetRotation = Math.Atan((B.X - A.X) / (A.Y - B.Y));
            else //Top-Left
                targetRotation = Math.Atan((A.Y - B.Y) / (A.X - B.X)) + (NINETY_DEGREES * 3);

        if (Rotation > targetRotation)
            Rotation -= turningRadius;
        else
            Rotation += turningRadius;

        prevState = currState;
    }

    public void Draw(SpriteBatch spriteBatch)
    {
        spriteBatch.Draw(Texture, Position, null, Color.White, (float)Rotation, new Vector2(Texture.Width / 2, Texture.Height / 2), 0.5f,
            SpriteEffects.None, 0.0f);
    }
}

}

4

4 Answers

3
votes

Let me make sure I understand. You have a vector (Cos(Rotation), Sin(Rotation) representing which direction your object is facing; another vector (B.X-A.X, B.Y-A.Y) representing the direction you want your object to face; and you want to know whether to rotate your object (the first vector) clockwise or counter-clockwise to face it in the direction of the second vector?

Simple, just treat them both as 3D vectors (set Z = 0) and take their cross product.

  • If the resulting vector has a positive Z component, rotate counter-clockwise
  • If it has a positive Z component, rotate clockwise
  • If the Z component is 0, the two vectors are parallel, so just check if they are facing opposite directions (and rotate either way) or the same direction (nothing to do!)

This works because of the right-hand rule which defines cross-products.

1
votes

This should just reverse direction to whichever is faster.

spin = targetRotation - Rotation;  
if (abs(spin) > Math.PI) 
  spin *= -1;
if (spin > 0
  Rotation += turningRadius;
else
  Rotation -= turningRadius;

Edit: Changed 180 to Math.PI

0
votes

Try the following (This is some code from a tank game I made, not sure if it still applies in this scenario). I trimmed it down to the essentials:

using System;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace Physics
{
    public class Ship
    {
        public Texture2D Texture { get; set; }
        public Vector2 Position { get; set; }
        public double Rotation { get; set; }
        private MouseState currState;

        public Ship(Texture2D texture, Vector2 position)
        {
            Texture = texture;
            Position = position;
            Rotation = 0;
        }

        public void Update()
        {
            currState = Mouse.GetState();

            var mouseloc = new Vector2(currState.X, currState.Y);
            Vector2 direction = Position - mouseloc;
            Rotation = Math.Atan2(direction.Y, direction.X) + MathHelper.PiOver2;
        }

        public void Draw(SpriteBatch spriteBatch)
        {
            spriteBatch.Draw(Texture, Position, null, Color.White, (float) Rotation,
                             new Vector2(Texture.Width/2, Texture.Height/2), 0.5f,
                             SpriteEffects.None, 0.0f);
        }
    }
}

I think you just need a little more math to modify the rate of the rotation, I'll come back if I come up with a solution. Also, this code will vary based on the default rotation of the sprite. In my example, it assumes that the sprite is facing downwards (6 o'clock). Hope this points you in the right direction!

0
votes

Edit my apologies I removed my head from my posterior and actually read the question this time. So here's a nice little method that I spent ~3 hours writing up tonight.

private float AngleLerp(float nowrap, float wraps, float lerp) {

    float c, d;

    if (wraps < nowrap) {
        c = wraps + MathHelper.TwoPi;
        //c > nowrap > wraps
        d = c - nowrap > nowrap - wraps
            ? MathHelper.Lerp(nowrap, wraps, lerp)
            : MathHelper.Lerp(nowrap, c, lerp);

    } else if (wraps > nowrap) {
        c = wraps - MathHelper.TwoPi;
        //wraps > nowrap > c
        d = wraps - nowrap > nowrap - c
            ? MathHelper.Lerp(nowrap, c, lerp)
            : MathHelper.Lerp(nowrap, wraps, lerp);

    } else { return nowrap; } //Same angle already

    return MathHelper.WrapAngle(d);
}

It lerps the nowrap towards the wraps value. The wraps angle can be the one that jumps from 0 to TwoPi and back so in this case the angle of the mouse. nowrap should be your objects angle.

Hope I was actually helpful this time.