【问题标题】:XNA 2D Box outline/Bounding Box?XNA 2D 框轮廓/边界框?
【发布时间】:2013-07-04 15:20:54
【问题描述】:

我如何计算精灵的“最远”边缘来创建一个 具有原点的变形精灵周围的矩形轮廓?

我想实现这样的目标 http://oi43.tinypic.com/14l39k0.jpghttp://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);

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

【问题讨论】:

  • 请添加语言标签

标签: c# xna 2d


【解决方案1】:

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

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

【讨论】:

  • 感谢您的回复,它帮助我找到了每个角落的位置 :) 我在制作矩形时遇到了麻烦。我想要这样的东西 i42.tinypic.com/2m62v41.png 矩形根据角落里,我怎么能做到这一点?
【解决方案2】:

大约一年前,我曾考虑尝试在一个旋转的矩形物体(汽车)上放置一个对撞机,但结果是空的。虽然你可以旋转一个精灵,但你不能围绕它旋转一个矩形来进行碰撞。

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

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

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

干杯, A.

【讨论】:

    【解决方案3】:

    我知道这个问题有点老了,但是如果有人对 工作 简单的 MonoGame 解决方案感兴趣(也考虑到纹理大小,因为在 MonoGame 中,起源是基于纹理大小而不是目标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();
    

    【讨论】:

      猜你喜欢
      • 2020-06-17
      • 1970-01-01
      • 2018-02-10
      • 2012-03-23
      • 1970-01-01
      • 2016-11-20
      • 2020-12-08
      • 1970-01-01
      • 2020-12-14
      相关资源
      最近更新 更多