Advertisement

Rotating in the direction of velocity

Started by September 09, 2016 07:30 AM
7 comments, last by alvaro 8 years, 2 months ago

Currently working with AI, and I have the ai agent moving. However I'm not sure how I would go about rotating the ai agent to face its velocity vector (Blue) or heading vector. My initial reaction was to calculate the angle between the two vectors by using tan,


        float theta = convertToDegrees(tan(agent.getDirection().x / agent.getDesiredVector().y));

(The agents direction is a normalized velocity vector)

if theta is greater than 0 rotate. Didn't have any luck with that approach, and I was wondering if anybody had a solution to this problem.

From the image below, I would like to align the red vector to the blue vector then change the red vector whenever the blue vector changes.

i'd say your formula is a bit wrong. that's not how you calculate the angle between 2 vectors. take a look here: http://www.euclideanspace.com/maths/algebra/vectors/angleBetween/ or make some own google search. As your vectors are normalized the magnitude is 1 what makes things easier but you still need to use the right formula. Something I am not sure about is what angle, clockwise or counter clockwise you will get from the calculation so you need to either research on that or simply test it out. I hope that will help you a bit.

Other than that you can take a research on rotating in 2D graphics. I'm sure you will find some helpfull tutorials that explain you how to get the desired result.

good luck

Advertisement
"Didn't have any luck" is a pretty bad description of the problem. What does it do? How does it differ from what you want? Are there cases where it does the right thing?

In any case, you are better off ditching angles altogether if you can: Use normalized unit vectors instead. If you are not scared of math, think of them as unit-modulus complex numbers.

Whatever you are doing with the angle, at some point you are going to compute its cosine and its sine. But those are precisely the coordinates of the unit-length vector!

If you are using an API that requires you to specify an angle, use atan2.
To clarify, do you want to have it always facing the direction it is moving or do you want it to slowly rotate towards another target over time?
My current game project Platform RPG

@HappyCoder, yes I would love the agent to rotate and face in the direction it is moving.

"Didn't have any luck" is a pretty bad description of the problem. What does it do? How does it differ from what you want? Are there cases where it does the right thing?

Well, the agent constantly rotates without stopping. I would love the agent to stop rotating when theta is equal to 0 to signal the red vector is aligned with the blue vector.

"Didn't have any luck" is a pretty bad description of the problem. What does it do? How does it differ from what you want? Are there cases where it does the right thing?

Well, the agent constantly rotates without stopping. I would love the agent to stop rotating when theta is equal to 0 to signal the red vector is aligned with the blue vector.


You might be getting confused between the object's attitude (its "angular position") and a rotation that is applied every frame (its "angular velocity"). What you need to do is set the object's attitude to what you want it to be every frame.

Perhaps you can show us some of your code (whatever part seems relevant).
Advertisement

Code

        float theta = convertToDegrees(tan(agent.getDirection().x / agent.getDesiredVector().y));

        if(theta > 0)
            agent.rotate(1.0f);
        else if(theta < 0)
            agent.rotate(-1.0f);
 

Non Member Function

const float convertToDegrees(const float rad)
{
    const float PI = 3.14159265359;
    return (rad * 180.0f ) / PI;
}

Agent.h

    sf::Vector2f HeadingDirection;
    sf::Vector2f DesiredVector;

Agent.cpp

sf::Vector2f Agent::Update(sf::Time & dt, SteeringBehaviors & behavior, const Agent & agent)
{
    sf::Vector2f steeringForce = behavior.FollowPath(agent);

    if(steeringForce.x >= m_MaxForce || steeringForce.y >= m_MaxForce)
        steeringForce = sf::Vector2f(m_MaxForce, m_MaxForce);

    m_Acceleration = steeringForce / m_Mass;
    
    m_Velocity += m_Acceleration * dt.asSeconds();

    if(m_Velocity.x >= m_MaxSpeed || m_Velocity.y >= m_MaxSpeed)
        m_Velocity = sf::Vector2f(m_MaxSpeed, m_MaxSpeed);

    HeadingDirection = NormalizeVector(m_Velocity);
    return sf::Vector2f(m_Velocity.x * dt.asSeconds(), m_Velocity.y * dt.asSeconds());

}
 
 
const sf::Vector2f Agent::getDesiredVector() const
{
    return DesiredVector;
}
 
const sf::Vector2f Agent::getDirection() const
{
    return HeadingDirection;
}

I'm still sure the error lies within


float theta = convertToDegrees(tan(agent.getDirection().x / agent.getDesiredVector().y));

have you debuged that value to see what it looks like every frame? i mean what you seem to want is the angle between the current direction and the desired direction but you never have your current direction in the formula.

From what i can read here:http://www.sfml-dev.org/tutorials/2.0/graphics-transform.php rotate does a relative rotation so this is the right function to use when you want to "slowly" rotate into a direction.

So the real question becomes what value is theta?. I realy think you are calculating it plain wrong. look here: http://www.cplusplus.com/reference/cmath/tan/ you are not getting an angle there because it takes an angle as parameter and returns the tangens of that angle. what you want is atan(). look here: http://www.cplusplus.com/reference/cmath/atan/ which returns a radian you can then convert to degrees.

and then again like mentioned way earlier atan2 is the way to go here: http://www.cplusplus.com/reference/cmath/atan2/

I can only hint you to take a look at "How to determin the angle between two 2dimensional vectors". I hinted you to a page earlier but you can also go and do your own search in your preffered search engine.

Edit: This one provides a simple explanation IMHO and you realy should take a look at it: http://stackoverflow.com/questions/21483999/using-atan2-to-find-angle-between-two-vectors

float theta = convertToDegrees(tan(agent.getDirection().x / agent.getDesiredVector().y));


That looks pretty bad, now that I look at it closely. First of all, you are dividing coordinates of different vectors, which is probably not what you want. Then you are taking the tangent of that ratio, when you probably want the arctangent. Then you convert the return value of the tangent as if it were an angle, which it isn't; this reinforces my feeling that you meant to use arctangent.

If you just want to check whether you should be turning left or right, then next thing you are going to complain about is that your agent oscillates once the desired attitude is reached.

Here's some code that does what you probably mean to do:
#include <iostream>
#include <complex>
#include <cmath>

typedef std::complex<double> Complex;

Complex capped_rotation(Complex rotation, Complex max_rotation) {
  return (std::imag(rotation) > std::imag(max_rotation)) ? max_rotation
    : (std::imag(rotation) < -std::imag(max_rotation)) ? std::conj(max_rotation)
    : rotation;
}

int main() {
  static const double degree = std::atan(1.0) / 45.0;

  Complex current = std::polar(1.0, 140.0 * degree);
  Complex desired = std::polar(1.0, -5.0 * degree);
  Complex max_rotation = std::polar(1.0, 20.0 * degree);

  std::cout << "We are starting at " << current << " and want to get to " << desired << ".\n\n";

  for (int i = 0; i < 30; ++i) {
    std::cout << "After " << i << " steps, we are at " << current << ".\n";
    current *= capped_rotation(desired / current, max_rotation);
  }
}

Don't be intimidated by the complex numbers. Complex numbers of modulus 1 are precisely the normalized direction vectors, which can also represent rotations.

This topic is closed to new replies.

Advertisement