1

我如何计算精灵的“最远”边缘以围绕具有原点的变换精灵创建矩形轮廓?

我想实现这样的东西 http://oi43.tinypic.com/14l39k0.jpg http://i42.tinypic.com/2m62v41.png 其中红框是“轮廓”,黑框是转换后的精灵. 该框需要根据角扩展-实际上只是一个边界框。

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

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

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

4

3 回答 3

0

如果您找到未旋转精灵上每个角的位置,然后围绕点旋转它们,您进行旋转以找到旋转精灵的每个角。 (这里描述了如何做到这一点)

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

于 2013-07-04T16:09:49.943 回答
0

大约一年前,我试图在一个旋转的矩形物体(汽车)上放置一个对撞机,但结果是空的。虽然您可以旋转精灵,但不能出于碰撞目的围绕它旋转矩形。

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

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

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

干杯,A。

于 2013-07-07T15:02:38.593 回答
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();
于 2018-07-08T20:41:58.080 回答