【问题标题】:Getting the angle between Vector2获取 Vector2 之间的角度
【发布时间】:2013-09-14 03:53:03
【问题描述】:

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

所以我用下面的方法计算了星星和屏幕右上角的夹角

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

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

编辑

我已更新图像以显示我尝试计算的角度并编辑了我的问题以使其更清晰。

编辑 2

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

【问题讨论】:

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


    【解决方案1】:

    看来你无意中交换了 sin 和 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;
    

    【讨论】:

    • 我尝试按照您的建议交换 sin 和 cos,但星星仍未发送到屏幕右上角。
    • @PutraKg 我认为这可能与您如何否定 y 轴值有关。只要您始终如一,就不必这样做。
    • OK 移除取反的 y 轴值。现在星星被发送到离屏幕右上角更近的位置,但还没有完全到那里。看来计算的角度小于应有的角度?
    • @PutraKg 你还在使用应有的 (cos, sin) 吗?如果你还没有,你可以尝试简单的标准化方法。
    • 你说得对,我不需要计算那个角度,但我认为我必须选择另一个答案,因为我认为它对我来说更清楚一些。
    【解决方案2】:

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

    只需像这样得出你的最终速度:

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

    还有;永远不要在 Draw 中更新位置。绘图用于绘图,而不是更新。更新停留在更新中!

    还有 2 个;您应该概括您的代码。所有移动对象都应该继承相同的基础,包括速度、位置和加速度等内容,以及处理这些内容的代码。这样,您只需更改逻辑来操纵速度和/或加速度来使物体移动。

    MovingObject.更新:

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

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

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

    【讨论】:

      【解决方案3】:

      我认为你应该这样做:

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

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

      然后,正如宗政李所写,你已经交换了 Sin 和 Cos:

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

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

      【讨论】:

      • 是的,图形表面的 y 轴是“反转的”,但只要这也是您在游戏中使用的轴即可。
      • 我总是这样做,如果我不否定Y轴我总是会发现一些不能正常工作的东西。
      • 好吧,只有当您选择将游戏视为具有“正常”y 轴而图形具有“反转”y 轴时,您才必须否定它(并反复这样做) .如果您始终保持一致,则可以完全避免这种情况。
      • 是的,我更喜欢认为有一个正常的 Y 轴,我发现在处理三角函数时更容易。
      【解决方案4】:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-01-10
        • 1970-01-01
        • 2014-07-24
        • 2019-04-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多