获取Vector2之间的角度

时间:2013-09-14 03:53:04

标签: c# windows-phone-8 xna monogame

当汽车撞到星星时,我想将星号(我的代码中的硬币)移动到屏幕的正确右上角。在每次更新期间,恒星和道路都以恒定速度向下移动。由于道路向下移动,汽车不会移动但似乎向上移动。虽然它可以根据用户的命令移动到左右车道。

所以我使用以下方法计算了星星和屏幕右上角之间的角度

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

在我的Update方法中,以下计算要移动的速度并将其发送到屏幕的右上角

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));

在我的Draw方法中,我使用

更新了coin.Position
coin.Position += collidedCoinVelocity * 10 ;

问题是星号(硬币)没有像我想要的那样发送到右上角,但是它位于右侧屏幕中间的某个位置。

当明星在右侧车道上被击中时,它与右上角之间的角度总是

1.2196048576751 radians = 69.878211 degree

当星星位于左侧车道时,角度为

0.952588487628243 radians = 54.5793 degree

我是否正确计算了角度,我错过了什么?也许我忘了考虑明星的向下运动了?

enter image description here

enter image description here

修改

我更新了图片以显示我想要计算的角度并编辑我的问题以使其更清晰。

编辑2

添加了第二张图片,以显示被击中后星星的去向。

4 个答案:

答案 0 :(得分:4)

似乎你偶然交换了罪和cos,并且那里似乎有一个随机的负面。这样就行了

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

应该是

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

虽然你甚至不需要计算这么多角度。要获得没有角度的单位矢量,只需使用

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;

答案 1 :(得分:1)

我认为你应该这样做:

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

考虑到反转的Y轴,您需要否定Y分量。

然后,正如 Zong Zheng Li 所写,你已经吞噬了Sin和Cos:

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

但由于Y轴反转,您仍然需要否定Vector2.Y分量。

答案 2 :(得分:1)

嗯......嗯......仅供参考;从笛卡尔坐标转换为使用角度,然后回到笛卡尔坐标在这里没有任何意义。

只需得出你的最终速度:

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

Velocity = direction * 10;
Position += Velocity;

也;永远不要更新Draw中的位置。绘图用于绘图,而不是更新。更新保留在更新!

另外2;您应该概括您的代码。所有移动对象都应该继承相同的基础,包括Velocity,Position和Acceleration以及处理这些内容的代码。这样,你只需要改变你的逻辑来操纵速度和/或加速度来移动东西。

MovingObject.Update:

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

(deltaTime =自上一帧以来的秒数,或者(浮动)gameTime.ElapsedGameTime.TotalSeconds)

然后你只需在子类更新结束时使用base.Update(),只要你设置了正确的值,位置和速度就会起作用。

答案 3 :(得分:0)

Vector2为点之间的插值提供静态函数。

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

使用此方法,当击中星星时,设置点A和点B. 将其传递给Vector2.Lerp函数 包含Percentage_of_distance_moved浮点值。

这是修改后的星级:

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);
        }
}

这就是我使用它的方式:

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);
    }
}