XNA - 限界盒旋转梦魇

时间:2013-05-08 10:47:55

标签: vector xna rotation sprite

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

我知道!互联网上有很多例子,解决方案和解释,包括这里,在这个网站上。但相信我,我已经尝试过所有这些。我试图只应用解决方案,我试图理解解释,但每个帖子都给出了不同的解决方案,但没有一个能够解决。

我显然错过了一些重要的事情......

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

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

因此,原点将返回(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岁的孩子解释一样,因为关于数学,我就是这样!

:)

编辑 :我发现了一个黑客让它在0度,90度,180度,270度下工作但现在我被困在中间位置(45,135,215,325)度,这让我觉得必须在一个公式中计算所有那些东西,以适用于任何角度...

correct behavior for 90° rotation

3 个答案:

答案 0 :(得分: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减去精灵起源。我猜id当时并不理解这个暗示......

答案 1 :(得分:0)

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

Vector2 p1 = MoveData.Position;
var m = Matrix.CreateRotationZ(angleInRadians);
p1 = Vector2.Transform(p1, m);

答案 2 :(得分:0)

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

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

  var m = Matrix.CreateTranslation(-Origin) 
        * Matrix.CreateRotationZ(angleInRadians) 
        * Matrix.CreateTranslation(Position);