2

我现在正在经历一场噩梦,试图找到正确的公式来获得一个与我的精灵方向相对应的边界框。

我知道 !互联网上有很多例子、解决方案、解释,包括这里,在这个网站上。但相信我,我都试过了。我试图只应用解决方案,我试图理解解释,但每篇文章都给出了不同的解决方案,但它们都不起作用。

我显然在这里遗漏了一些重要的东西......

所以,基本上,我有一个精灵,它的纹理是原生的(20 宽 * 40 高)并且在启动应用程序时位于 (200,200)。精灵起源是经典

_origin = new Vector2((float)_texture.Width / 2, (float)_texture.Height / 2);

所以 origin 会返回一个 (5.5;8) 向量 2

通过键盘输入,我可以旋转这个精灵。默认旋转为 0 或 Key.Up。然后旋转 90 对应 Key.Right,旋转 180 对应 Key.Down,依此类推...

目前,不涉及移动,只是旋转。

所以这是我计算边界矩形的代码:

public partial class Character : ICollide
{
    private const int InternalRunSpeedBonus = 80;
    private const int InternalSpeed = 80;
    private Vector2 _origin;
    private Texture2D _texture;
    private Texture2D _axisBase;
    private Texture2D _axisOrig;

    public Character()
    {
        MoveData = new MoveWrapper { Rotation = 0f, Position = new Vector2(200, 200), Speed = new Vector2(InternalSpeed) };
    }

    public MoveWrapper MoveData { get; set; }

    #region ICollide Members

    public Rectangle Bounds
    {
        get { return MoveData.Bounds; }
    }

    public Texture2D Texture
    {
        get { return _texture; }
    }
    #endregion ICollide Members

    public void Draw(SpriteBatch theSpriteBatch)
    {
        theSpriteBatch.Draw(_texture, MoveData.Position, null, Color.White, MoveData.Rotation, _origin, 1f, SpriteEffects.None, 0);//main sprite
        theSpriteBatch.Draw(_axisOrig, MoveData.Position, null, Color.White, 0f, _origin, 1f, SpriteEffects.None, 0);//green
        theSpriteBatch.Draw(_axisBase, MoveData.Position, null, Color.White, 0f, Vector2.Zero, 1f, SpriteEffects.None, 0);//red

    }

    public void Load(ContentManager theContentManager)
    {
        _texture = theContentManager.Load<Texture2D>("man");
        _axisBase = theContentManager.Load<Texture2D>("axis");
        _axisOrig = theContentManager.Load<Texture2D>("axisOrig");
        _origin = new Vector2((float)_texture.Width / 2, (float)_texture.Height / 2);

    }

    public void MoveForward(GameTime theGameTime, KeyboardState aCurrentKeyboardState)
    {
        InternalMove(theGameTime, aCurrentKeyboardState);
    }

    private void InternalMove(GameTime theGameTime, KeyboardState aCurrentKeyboardState, bool forward = true)
    {
        //stuff to get the move wrapper data valorized (new position, speed, rotation, etc.)
        MoveWrapper pm = MovementsHelper.Move(MoveData.Position, MoveData.Rotation, aCurrentKeyboardState, InternalSpeed,
                                              InternalRunSpeedBonus, theGameTime, forward);
        pm.Bounds = GetBounds(pm);
        MoveData = pm;
    }

    public void MoveBackward(GameTime theGameTime, KeyboardState aCurrentKeyboardState)
    {
        InternalMove(theGameTime, aCurrentKeyboardState, false);

    }

    private Rectangle GetBounds(MoveWrapper pm)
    {
        return GetBoundingBox(pm, _texture.Width, _texture.Height);
    }

    public  Rectangle GetBoundingBox(MoveWrapper w, int tWidth, int tHeight)
    {
        //1) get original bounding vectors

        //upper left => same as position
        Vector2 p1 = w.Position;

        //upper right x = x0+width, y = same as position
        Vector2 p2 = new Vector2(w.Position.X + tWidth, w.Position.Y);

        //lower right x = x0+width, y = y0+height
        Vector2 p3 = new Vector2(w.Position.X + tWidth, w.Position.Y + tHeight);

        //lower left x = same as position,y = y0+height
        Vector2 p4 = new Vector2(w.Position.X, w.Position.Y + tHeight);


        //2) rotate all points given rotation and origin
        Vector2 p1r = RotatePoint(p1, w);
        Vector2 p2r = RotatePoint(p2, w);
        Vector2 p3r = RotatePoint(p3, w);
        Vector2 p4r = RotatePoint(p4, w);

        //3) get vector2 bouding rectancle location
        var minX = Math.Min(p1r.X, Math.Min(p2r.X, Math.Min(p3r.X, p4r.X)));
        var maxX = Math.Max(p1r.X, Math.Max(p2r.X, Math.Max(p3r.X, p4r.X)));

        //4) get bounding rectangle width and height
        var minY = Math.Min(p1r.Y, Math.Min(p2r.Y, Math.Min(p3r.Y, p4r.Y)));
        var maxY = Math.Max(p1r.Y, Math.Max(p2r.Y, Math.Max(p3r.Y, p4r.Y)));

        var width = maxX - minX;
        var height = maxY - minY;

        // --> begin hack to get it work for 0,90,180,270 degrees
        var origMod = new Vector2((float)tWidth / 2, (float)tHeight / 2);

        var degree = (int)MathHelper.ToDegrees(w.Rotation);

        if (degree == 0)
        {
            minX -= origMod.X;
            minY -= origMod.Y;
        }
        else if (degree == 90)
        {
            minX += origMod.Y;
            minY -= origMod.X;
        }
        else if (degree == 180)
        {
            minX += origMod.X;
            minY += origMod.Y;
        }
        else if (degree == 270)
        {
            minX -= origMod.Y;
            minY += origMod.X;
        }
        // end hack <--

        return new Rectangle((int)minX, (int)minY, (int)width, (int)height);
    }

    public  Vector2 RotatePoint(Vector2 p, MoveWrapper a)
    {
        var m = Matrix.CreateRotationZ(a.Rotation);

        var refToWorldOrig = p - a.Position;
        Vector2 rotatedVector = Vector2.Transform(refToWorldOrig, m);
        var backToSpriteOrig = rotatedVector + a.Position;
        return backToSpriteOrig;

        //does not work
        //var Origin = new Vector3(_origin, 0);
        //var Position = new Vector3(p, 0);

        //var m = Matrix.CreateTranslation(-Origin)
        //        * Matrix.CreateRotationZ(a.Rotation)
        //        * Matrix.CreateTranslation(Position);

        //return Vector2.Transform(p, m);
    }
}

旋转参数是 MathHelper 度数到弧度结果。

我有一个函数可以绘制一个与边界框相对应的矩形,并且我希望该边界框与我的精灵完全重叠,至少对于 0,90,180 和 270 度角旋转。

相反,旋转计算后我有奇怪的坐标: - 当旋转到 90° 时,边界框 X 是负数(所以盒子不可见) - 当旋转到 180° 时,边界框 X 和 Y 是负数(所以盒子不可见) - 当旋转到 270° 时,边界框 Y 为负数(因此该框不可见)

有人可以向我解释我做错了什么,就像在向 3 岁的孩子解释一样,因为关于数学,这就是我!

:)

编辑:我找到了一个 hack 让它在 0、90、180、270 度下工作,但现在我被困在中间位置(45,135,215、325 度),这让我认为必须有一种方法来计算所有这些东西在一个适用于任何角度的单一公式中......

90° 旋转的正确行为

4

3 回答 3

1

终于找到了让它在没有黑客的情况下工作的方法!!!!!!!!!!!!!!!!

    public Vector2 RotatePoint(Vector2 p, MoveWrapper a)
    {

        var wm = Matrix.CreateTranslation(-a.Position.X - _origin.X, -a.Position.Y - _origin.Y, 0)//set the reference point to world reference taking origin into account
                 * Matrix.CreateRotationZ(a.Rotation) //rotate
                 * Matrix.CreateTranslation(a.Position.X, a.Position.Y, 0); //translate back

        var rp = Vector2.Transform(p, wm);
        return rp;
    }

奖励效果,这比我以前的“hacky”方法更精确(正如我绘制的指南所显示的那样)

我刚刚意识到这与 Blau 提出的非常接近,只是我的第一个翻译将引用设置回世界 0,0,0 减去精灵原点。我猜我当时没看懂提示……

于 2013-05-09T16:06:06.187 回答
0

如果你想围绕一个原点旋转它应该是:

  var Origin = new Vector3(Origin2D, 0);
  var Position = new Vector3(Position2D, 0);

  var m = Matrix.CreateTranslation(-Origin) 
        * Matrix.CreateRotationZ(angleInRadians) 
        * Matrix.CreateTranslation(Position);
于 2013-05-08T16:17:34.590 回答
0

您可以使用矩阵结构旋转位置。

Vector2 p1 = MoveData.Position;
var m = Matrix.CreateRotationZ(angleInRadians);
p1 = Vector2.Transform(p1, m);
于 2013-05-08T10:57:49.197 回答