Homing missile problem

Started by
23 comments, last by SimonForsman 9 years, 10 months ago

an API that uses degrees (which is a bad thing)

Yes, SFML has this problem. Hundreds of conversions needs to be made every frame which will in turn be converted back (to radians) in order to pass on to OpenGL.


Actually, glRotate also uses degrees, so maybe SFML and SDL are just trying to be transparent here and OpenGL is where the nonsense stems from. The only thing glRotate can possibly do with the angle is compute its sine and cosine. If it were up to me, OpenGL would allow you to pass in the sine and the cosine, and if we wanted to have a version that takes an angle, it would be in radians.
Advertisement

After a bit of thought and some calculations, I discovered that the conversion of range from [0,360] to [180,-180] seems to be as simple as this:


float current = 180 - rocket_sprite.getRotation();

With this, the code should work just as in the ActionScript example, since now it's all in the same range and at the same units.

It does not. The missile goes shakey, and there's no easing.

I can't understand why.

I'll keep looking into it, but right now I'm baffled. Why does it work there, but not here?

For reference, HERE's the ActionScript tutorial. The example of how it should work is under "Step 10: Less Precision for Better Looks".

...And here is my current code:


#include <math.h>
#define PI 3.14159265

class rocket
{
private:
    sf::Texture rocket_texture;
    sf::Sprite rocket_sprite;
    sf::Vector2f position;
    float rotation;

public:
    rocket();
    bool rocket_load();
    void rocket_show( sf::RenderWindow * window );
    void set_position( sf::Vector2f new_position );
    void rocket_rotate( sf::RenderWindow * window );
    void rocket_move();
};

rocket::rocket() { rotation = 0.f; }

bool rocket::rocket_load()
{
    rocket_texture.loadFromFile( "rocket.png" );
    rocket_sprite.setTexture( rocket_texture );

    return true;
}

void rocket::rocket_show( sf::RenderWindow * window )
{
    rocket_sprite.setOrigin( rocket_texture.getSize().x / 2 , rocket_texture.getSize().y / 2 );
    rocket_sprite.setPosition( position );
    window->draw( rocket_sprite );
}

void rocket::set_position( sf::Vector2f new_position ) { position = new_position; }

void rocket::rocket_rotate( sf::RenderWindow * window )
{
    sf::Vector2i mouse_position = sf::Mouse::getPosition( *window );
    float targetx = mouse_position.x - position.x;
    float targety = mouse_position.y - position.y;
    rotation = atan2( targety , targetx ) * ( 180 / PI );

    int ease = 5;
    float current = 180 - rocket_sprite.getRotation();

    if( abs( rotation - current ) > 180 )
    {
        if( rotation > 0 && current < 0 ) rotation -= ( 360 - rotation + current ) / ease;
        else if( current > 0 && rotation < 0 ) rotation += ( 360 - rotation + current ) / ease;
    }
    else if( rotation < current ) rotation -= abs( current - rotation ) / ease;
    else rotation += abs( rotation - current ) / ease;

    rocket_sprite.setRotation( rotation );
}

void rocket::rocket_move()
{
    int speed = 5;
    float speedx = 0;
    float speedy = 0;
    speedx = speed * ( 90 - abs( rotation ) ) / 90;
    if( rotation < 0 ) speedy = -speed + abs( speedx );
    else speedy = speed - abs( speedx );
    position.x += speedx;
    position.y += speedy;
}

After a bit of thought and some calculations, I discovered that the conversion of range from [0,360] to [180,-180] seems to be as simple as this:
float current = 180 - rocket_sprite.getRotation();
With this, the code should work just as in the ActionScript example, since now it's all in the same range and at the same units.
It does not. The missile goes shakey, and there's no easing.

There are several aspects one needs to consider:

1.) Is the direction of 0° the same in AS and in SFML? E.g. does it mean straight to the right in both cases? Let us assume "yes".

2.) Is the rotational direction the same in AS and in SFML? E.g. does it mean counter-clockwise (or clockwise) in both cases? Let us assume "yes".

Then the half circle from 0° through +90° up to +180° is the same for both! There is no transformation needed. However, the other half of the circle is from +180° through 270° up to 360° in SFML and from -180° through -90° up to 0° in AS.

If you think of the periodicity of a circle, i.e. the same angle is reached when going 360° in any direction, the negative angles just go in the opposite rotational direction of the positive ones. So going 10° in the one direction is the same as going 360° minus those 10° in the other direction. That also means that it (should) make no difference whether you invoke setRotation with either -10° or +350°. As you can see, the difference of both is just 360°.

So why do we need to consider a transformation anyhow? Because the entire if-then-else block is written in a way that expects the angles being [0,+180] for the "upper" half circle and [-180,0] for the lower one.

So a transformation means that half of the values, namely those in the "upper" half-circle, are identity mapped (i.e. they need to be used as are), and only the other half of values, namely those of the "lower" half-cirlce, need actually to be changed. That is the reason several code snippets above, inclusive mine, use a stepwise transform that considers both halves of the circle separately.

It also means that the reverse transform, i.e. going back to the value space of SFML just before invoking setRotation, should not be required if SFML allows for negative angles, too. (I'm not sure about what SFML allows, so I suggested in my first post to do the reverse transform.)

Welp, no wonder my code does not work. You are right, it seems that SFML and ActionScript handle rotation a bit differently. I think that in ActionScript the missile points up at 90 degrees, but in SFML it points down.

All I need now to adjust the code it to find out how exactly does ActionScript handle rotation. Direction, degrees, and all that. Then, I'll be able to adjust the code accordingly. I'll try searching online tomorrow.

Thank you for explaining all this to me. :-)

an API that uses degrees (which is a bad thing)

Yes, SFML has this problem. Hundreds of conversions needs to be made every frame which will in turn be converted back (to radians) in order to pass on to OpenGL.


Actually, glRotate also uses degrees, so maybe SFML and SDL are just trying to be transparent here and OpenGL is where the nonsense stems from. The only thing glRotate can possibly do with the angle is compute its sine and cosine. If it were up to me, OpenGL would allow you to pass in the sine and the cosine, and if we wanted to have a version that takes an angle, it would be in radians.

Well, glRotate belongs to the old transformation stack which is depracated, these days you can (and should) pass whatever it is you want to pass directly to the shader.

[size="1"]I don't suffer from insanity, I'm enjoying every minute of it.
The voices in my head may not be real, but they have some good ideas!

This topic is closed to new replies.

Advertisement