简体   繁体   中英

Getting the angle between Vector2

I want to move the star (coin in my code) to the exact top right corner of the screen when the car hit the star. Both star and road are moving downward at a constant speed during each update. The car does not move but appears to be moving upward because of the road moving downward. Although it can move to the left and right lane on user's command.

So I calculated the angle between the star and the top right corner of the screen using the following method

public double AngleBetween(Vector2 a, Vector2 b)
{
    return Math.Atan2(b.Y - a.Y, b.X - a.X);
}

In my Update method, the following calculate the velocity to move and send it to the top right corner of the screen

double angleBetween = coin.AngleBetween(coin.Position, new
   Vector2(currentGame.GraphicsDevice.Viewport.Bounds.Right, 0));              
collidedCoinVelocity = new Vector2((float)Math.Sin(angleBetween), 
   -(float)Math.Cos(angleBetween));

In my Draw method, I updated the coin.Position using

coin.Position += collidedCoinVelocity * 10 ;

The problem is the star (coin) is not sent to the top right corner as I wanted but it's somewhere in the middle of the right screen bound.

When the star is hit when it's on the right lane, the angle between it and the top right corner is always

1.2196048576751 radians = 69.878211 degree

When the star is on the left lane the angle is

0.952588487628243 radians = 54.5793 degree

Am I calculating the angle correctly, what am I missing? Perhaps I am forgetting to consider the downward movement of the star?

在此输入图像描述

在此输入图像描述

EDIT

I have updated the image to show the angle I am trying to calculate and edited my question to make it clearer.

EDIT 2

Added a second image to show where the star goes after being hit.

It seems you swapped sin and cos by accident, and there seems to be a random negative in there. Thus this line

collidedCoinVelocity = new Vector2((float)Math.Sin(angleBetween), 
   -(float)Math.Cos(angleBetween));

should probably be

collidedCoinVelocity = new Vector2((float)Math.Cos(angleBetween), 
   (float)Math.Sin(angleBetween));

Although you don't even need to calculate so many angles. To get a unit vector without angles just use

double dx = b.X - a.X;
double dy = b.Y - a.Y;
double mag = Math.Sqrt(dx * dx + dy * dy);
collidedCoinVelocity = new Vector2(dx, dy) / mag;

I think you should do:

public double AngleBetween(Vector2 a, Vector2 b)
{
    return Math.Atan2(-(b.Y - a.Y), b.X - a.X);
}

Considering the reversed Y axis you need to negate Y components.

Then, as Zong Zheng Li wrote, you've swaped Sin and Cos:

collidedCoinVelocity = new Vector2((float)Math.Cos(angleBetween), 
   -(float)Math.Sin(angleBetween));

but you still need to negate the Vector2.Y component, due to the reversed Y axis.

Erhm... Just FYI; converting from cartesian coordinates to using angle, and then back to cartesian makes no sense here.

Just derive your final velocity like this:

Vector2 direction = new Vector2(currentGame.GraphicsDevice.Viewport.Bounds.Right, 0) - coin.Position;
direction.Normalize();

Velocity = direction * 10;
Position += Velocity;

Also; NEVER update position in Draw. Draw is for Drawing, not updating. Updating stays in Update!

Also 2; You should generalize your code. all moving objects should inherit the same base that includes things like Velocity, Position and Acceleration, and the code to handle these. That way, you only have to change your logic to manipulate Velocity and/or Acceleration to make stuff move.

MovingObject.Update:

Velocity += Acceleration * deltaTime;
Positioin += Velocity * deltaTime;

(deltaTime = time in seconds since last frame, or (float)gameTime.ElapsedGameTime.TotalSeconds)

Then you just use base.Update() at the end of your subClasses update, and position and speed will work :) as long as you set the correct values.

Vector2 provides a static function for interpolation between points.

star_pos = Vector2.Lerp(position_when_hit, destinatin, percent);
                percent += 0.005f;

Using this method, when star is hit, set Point A and Point B.
Pass it to the Vector2.Lerp function.
Include a Percentage_of_distance_moved float value.

This is the modified star class:

class star
    {
        Texture2D starT;
        public Vector2 star_pos;
        Vector2 position_when_hit;
        Vector2 destinatin;
        float percent;
        bool star_moving_up;

        public star(Random rand,Texture2D star)
        {
            this.starT = star;
            star_moving_up = false;
            destinatin = new Vector2(800, 0);
            star_pos = new Vector2(rand.Next(500), rand.Next(500));
        }
//Try to call this only once
        public void on_hit(){
            if (!star_moving_up)
            {
                position_when_hit = star_pos;
                star_moving_up = true;
            }
        }

        public void update(GameTime gameTime)
        {
            if (star_moving_up)
            {
                star_pos = Vector2.Lerp(position_when_hit, destinatin, percent);
                //a larger percent value will move the star faster.
                percent += 0.001f;
            }
            else
            {
                //Your Logic for moving it down
            }
        }

        public void draw(SpriteBatch sp)
        {
            sp.Draw(starT, star_pos, Color.White);
        }
}

And this is how I used it:

public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        Texture2D star;
        Random rand;
        star STAR;



public Game1()
    {
        graphics = new GraphicsDeviceManager(this);
        Content.RootDirectory = "Content";
    }


    protected override void Initialize()
    {
        graphics.PreferredBackBufferWidth = 800;
        graphics.PreferredBackBufferHeight = 600;

        base.Initialize();
    }


    protected override void LoadContent()
    {
        rand = new Random();
        spriteBatch = new SpriteBatch(GraphicsDevice);            
        star = Content.Load<Texture2D>("Bitmap1");
        STAR = new star(rand, star);

    }


    protected override void UnloadContent()
    {

    }


    protected override void Update(GameTime gameTime)
    {
        // Allows the game to exit
        if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
            this.Exit();
//calls on_hit every update call after 4 seconds. just for demo purpose. 
        if (gameTime.TotalGameTime > TimeSpan.FromSeconds(4))
            STAR.on_hit();
        STAR.update(gameTime);
        base.Update(gameTime);
    }


    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.CornflowerBlue);

        spriteBatch.Begin();
        STAR.draw(spriteBatch);
        spriteBatch.End();
        base.Draw(gameTime);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM