XNA 2D Box轮廓/边框?

时间:2013-07-04 15:20:54

标签: c# xna 2d

  

我怎样才能计算精灵的“最远”边缘来创建一个   带有原点的变换精灵周围的矩形轮廓?

     

我想实现这样的目标   http://oi43.tinypic.com/14l39k0.jpg http://i42.tinypic.com/2m62v41.png   红框是“轮廓”,黑框是   转化的精灵。盒子需要根据角落进行扩展 - 只是   一个边界框真的。

     

我尝试了各种各样的方程式来找到a的坐标   转换后的精灵:

Transformed.X = pos.X * (float)Math.Cos(angle) - pos.Y * (float)Math.Sin(angle);
Transformed.Y = pos.X * (float)Math.Sin(angle) + pos.Y * (float)Math.Cos(angle);
     

但我似乎无法使其发挥作用。任何想法我怎么能实现这一目标?

     

任何帮助都将不胜感激。

     

感谢Zenchovey,我能够解决我的问题。这是我使用的代码:

启动变量

Vector2 TransformPos = Vector2.Zero;
Vector2 TransformPos2 = Vector2.Zero;
float[] px = new float[2];
float[] py = new float[2];
float[] pxl = new float[2];
float[] pyl = new float[2];
float ox;
float oy;

更新方法

    // Vars
    ox = pos.X;
    oy = pos.Y;

    // top left
    pxl[0] = pos.X - Origin.X;
    pyl[0] = pos.Y - Origin.Y;
    // bottom left
    pxl[1] = pos.X - Origin.X;
    pyl[1] = pos.Y + Origin.Y;
    // top right
    px[0] = pos.X + Origin.X;
    py[0] = pos.Y - Origin.Y;
    // bottom right
    px[1] = pos.X + Origin.X;
    py[1] = pos.Y + Origin.Y;

    if (rot <= MathHelper.ToRadians(90) && rot >= MathHelper.ToRadians(0))
    {
        TransformPos.X = (float)Math.Cos(rot) * (pxl.Min() - ox) - (float)Math.Sin(rot) * (pyl.Max() - oy) + ox;
        TransformPos.Y = (float)Math.Sin(rot) * (pxl.Min() - ox) + (float)Math.Cos(rot) * (pyl.Min() - oy) + oy;
        TransformPos2.X = (float)Math.Cos(rot) * (px.Max() - ox) - (float)Math.Sin(rot) * (py.Min() - oy) + ox;
        TransformPos2.Y = (float)Math.Sin(rot) * (px.Max() - ox) + (float)Math.Cos(rot) * (py.Max() - oy) + oy;
    }
    else
    if (rot <= MathHelper.ToRadians(270) && rot >= MathHelper.ToRadians(180))
    {
        TransformPos2.X = (float)Math.Cos(rot) * (pxl.Min() - ox) - (float)Math.Sin(rot) * (pyl.Max() - oy) + ox;
        TransformPos2.Y = (float)Math.Sin(rot) * (pxl.Min() - ox) + (float)Math.Cos(rot) * (pyl.Min() - oy) + oy;
        TransformPos.X = (float)Math.Cos(rot) * (px.Max() - ox) - (float)Math.Sin(rot) * (py.Min() - oy) + ox;
        TransformPos.Y = (float)Math.Sin(rot) * (px.Max() - ox) + (float)Math.Cos(rot) * (py.Max() - oy) + oy;
    }
    else
    if (rot <= MathHelper.ToRadians(180) && rot >= MathHelper.ToRadians(90))
    {
        TransformPos2.X = (float)Math.Cos(rot) * (pxl.Max() - ox) - (float)Math.Sin(rot) * (pyl.Min() - oy) + ox;
        TransformPos.Y = (float)Math.Sin(rot) * (pxl.Max() - ox) + (float)Math.Cos(rot) * (pyl.Max() - oy) + oy;

        TransformPos.X = (float)Math.Cos(rot) * (px.Min() - ox) - (float)Math.Sin(rot) * (py.Max() - oy) + ox;
        TransformPos2.Y = (float)Math.Sin(rot) * (px.Min() - ox) + (float)Math.Cos(rot) * (py.Min() - oy) + oy;
    }
    else
    if (rot <= MathHelper.ToRadians(360) && rot >= MathHelper.ToRadians(270))
    {
        TransformPos.X = (float)Math.Cos(rot) * (pxl.Max() - ox) - (float)Math.Sin(rot) * (pyl.Min() - oy) + ox;
        TransformPos2.Y = (float)Math.Sin(rot) * (pxl.Max() - ox) + (float)Math.Cos(rot) * (pyl.Max() - oy) + oy;

        TransformPos2.X = (float)Math.Cos(rot) * (px.Min() - ox) - (float)Math.Sin(rot) * (py.Max() - oy) + ox;
        TransformPos.Y = (float)Math.Sin(rot) * (px.Min() - ox) + (float)Math.Cos(rot) * (py.Min() - oy) + oy; 
    }


Transform = new Rectangle((int)TransformPos.X, (int)TransformPos.Y, (int)TransformPos2.X - (int)TransformPos.X, (int)TransformPos2.Y - (int)TransformPos.Y);

它根据它的旋转来查找精灵角的最大值和最小值,以制作边界框。 代码假设原点是精灵的中间位置,你必须根据原点更改代码

3 个答案:

答案 0 :(得分:0)

如果在未旋转的精灵上找到每个角的位置,然后围绕点旋转它们,则进行旋转以找到旋转精灵的每个角。 (How to do this is described here)

然后您可以找到这些点的最大值和最小值x和y值。 minX和minY将是你的边界矩形的左上角,maxX和maxY将是你的边界矩形的右下角。

答案 1 :(得分:0)

我试图在一年前左右将一个对撞机放在旋转的矩形物体(汽车)上,然后空了。虽然你可以旋转精灵,但是为了碰撞,你不能围绕它旋转一个矩形。

我最终在我的对象上使用了一个“完成工作”的圆圈,但并不完美。我读到的其他解决方案包括在一个矩形物体上放置三个圆圈(一个在前面,一个在中间,一个在后面)。报道非常好,但数学比我想要的要多。

作为最后的手段,是否有一个未旋转的矩形可以对你的精灵进行所有可能的旋转?

这些都不是完美的,但它们可能足够接近。

干杯, 甲

答案 2 :(得分:0)

我知道这个问题有点老了,但是如果有人对工作简单的MonoGame解决方案感兴趣(还要考虑纹理大小,因为在MonoGame中起源是基于纹理大小而不是dest rect),这是一个:

    /// <summary>
    /// Rotate a vector around pivot.
    /// </summary>
    /// <param name="vec">Vector to rotate.</param>
    /// <param name="pivot">Point to rotate around.</param>
    /// <param name="theta">Rotation angle.</param>
    /// <returns>Rotated vector.</returns>
    public static Vector2 RotateAround(Vector2 vec, Vector2 pivot, float theta)
    {
        return new Vector2(
            (float)(System.Math.Cos(theta) * (vec.X - pivot.X) - System.Math.Sin(theta) * (vec.Y - pivot.Y) + pivot.X),
            (float)(System.Math.Sin(theta) * (vec.X - pivot.X) + System.Math.Cos(theta) * (vec.Y - pivot.Y) + pivot.Y));
    }

    /// <summary>
    /// Get rectangle and rotation (angle + origin) and calculate bounding box containing the rotated rect.
    /// </summary>
    /// <param name="rect">Rectangle to rotate.</param>
    /// <param name="rotation">Rotation angle.</param>
    /// <param name="origin">Rotation origin.</param>
    /// <param name="textureSize">In MonoGame origin is relative to source texture size, not dest rect. 
    /// So this param specify source texture size.</param>
    /// <returns>Rotated rectangle bounding box.</returns>
    public static Rectangle GetRotatedBoundingBox(Rectangle rect, float rotation, Vector2 origin, Rectangle textureSize)
    {
        // fix origin to be relative to rect location + fix it to be relative to rect size rather then texture size
        var originSize = ((origin / textureSize.Size.ToVector2()) * rect.Size.ToVector2());
        var convertedOrigin = rect.Location.ToVector2() + originSize;

        // calculate top-left rotated corner
        var topLeft = RotateAround(rect.Location.ToVector2(), convertedOrigin, rotation);

        // calculate rest of rotated corners
        Vector2[] corners = new Vector2[] {
            RotateAround(new Vector2(rect.Left, rect.Bottom), convertedOrigin, rotation),
            RotateAround(new Vector2(rect.Right, rect.Bottom), convertedOrigin, rotation),
            RotateAround(new Vector2(rect.Right, rect.Top), convertedOrigin, rotation)
        };

        // find min and max points
        Vector2 min, max;
        min = max = topLeft;
        foreach (var corner in corners)
        {
            if (corner.X < min.X) min.X = corner.X;
            if (corner.Y < min.Y) min.Y = corner.Y;
            if (corner.X > max.X) max.X = corner.X;
            if (corner.Y > max.Y) max.Y = corner.Y;
        }

        // create rectangle from min-max and return it
        return new Rectangle(min.ToPoint() - originSize.ToPoint(), (max - min).ToPoint());
    }

只需将这两个静态函数添加到某个类中,然后使用Draw参数调用GetRotatedBoundingBox()。

用法示例:

        // update rotation and start batch
        rotation += 0.01f;
        spriteBatch.Begin();

        // dest and origin values (play with these to see different results)
        var dest = new Rectangle(new Point(200, 200), new Point(100, 200));
        var origin = new Vector2(15, 200);

        // draw sprite
        spriteBatch.Draw(rectTexture, dest, null, Color.White, rotation, origin, SpriteEffects.None, 0f);

        // draw sprite bounding box (in my case I put the functions under Source.Graphics.Utils static class)
        var boundingBox = Source.Graphics.Utils.GetRotatedBoundingBox(dest, rotation, origin, rectTexture.Bounds);
        spriteBatch.Draw(rectTexture, boundingBox, Color.Red);

        // end batch
        spriteBatch.End();