0
\$\begingroup\$

I have a Rectangle and a Vector2 position, my sprite is being drawn at this position. The rectangle is meant to be a collision system with the cursor, when I rotate my sprite around the origin and stuff in a draw call

public virtual void Draw(SpriteBatch spriteBatch)
{
    spriteBatch.Draw(texture, position, null, Color.White, rot, Origin, SCALEFACTOR, SpriteEffects.None, 0f);         
}

It works fine, my object is rotated, at it's centre, by Pi/2, or whatever I put in. However, rotating the rectangle isn't as easy, I read almost every stack overflow question on this problem and still haven't found a fix. I then followed a youtube guide, this is the resulting code for rotating a rectangle.

private void RotateItem(float rotation)
{
    rot += rotation;
    UpdateRectangle();
}

private void UpdateRectangle()
{
    Vector2 tl = Vector2.Transform(Vector2.Zero, transform);
    Vector2 tr = Vector2.Transform(new Vector2(scaledWidth, 0), transform);
    Vector2 bl = Vector2.Transform(new Vector2(0, scaledHeight), transform);
    Vector2 br = Vector2.Transform(new Vector2(scaledWidth, scaledHeight), transform);

    Vector2 min = new Vector2(FindSmallest(tl.X, tr.X, bl.X, br.X), FindSmallest(tl.Y, tr.Y, bl.Y, br.Y));
    Vector2 max = new Vector2(FindBiggest(tl.X, tr.X, bl.X, br.X), FindBiggest(tl.Y, tr.Y, bl.Y, br.Y));

    rectangle = new Rectangle((int)min.X, (int)min.Y, (int)(max.X - min.X), (int)(max.Y - min.Y));
}

private void UpdateTransformMatrix()
{
    transform = Matrix.CreateTranslation(new Vector3(-Origin, 0))
                * Matrix.CreateRotationZ(rot)
                * Matrix.CreateTranslation(new Vector3(position, 0));
}

The width + height don't seem to change until I click on the box (which isn't even drawn properly over the sprite because Origin + and doesn't seem to rotate at all tbh)... I'll show you an example https://gyazo.com/c045b844f306a0ea25f48a0d45127f21

And this is my full draw code + including the rectangle's rectTex

public virtual void Draw(SpriteBatch spriteBatch)
{
    spriteBatch.Draw(texture, position, null, Color.White, rot, Origin, SCALEFACTOR, SpriteEffects.None, 0f);
    if (ShowRectangle)
        spriteBatch.Draw(rectTex, rectangle, null, Color.White, rot, Vector2.Zero, SpriteEffects.None, 0f);          
}
\$\endgroup\$

3 Answers 3

0
\$\begingroup\$

Okay, after a few more hours of fiddling around and referencing, I somehow managed to fix it. It was a matter of order in which I Update the transform matrix and update the rectangle and where I did so. Now it kind of works, however, after X amount of rotates, the height gets set to 0 and the rectangle ceases to exist... I'll post my new Updates and stuff so people who look at this in the future can see

public virtual void Update(GameTime gameTime, MouseState _currState, MouseState _prevState, ref Item controlItem)
{
    ClickCheck(_currState, _prevState, ref controlItem);
    if (controlItem == this)
    {
        int diffX = _currState.X - _prevState.X;
        int diffY = _currState.Y - _prevState.Y;
        position.X += diffX;
        position.Y += diffY;
        UpdateTransformMatrix();
        UpdateRectangle();
    }
}
public Item(Texture2D inTex)
{
    texture = inTex;
    position = new Vector2(400,400);
    scaledWidth = (int)(texture.Width * SCALEFACTOR);
    scaledHeight = (int)(texture.Height * SCALEFACTOR);
    rectangle = new Rectangle((int)position.X,(int)position.Y,scaledWidth, scaledHeight);
    Origin = new Vector2(scaledWidth / 2, scaledHeight / 2);
    UpdateTransformMatrix();
}
protected void ClickCheck(MouseState _currentState, MouseState _prevState, ref Item controlItem)
{
    Rectangle mouseRect = new Rectangle(_currentState.X, _currentState.Y, 1, 1);
    if (rectangle.Intersects(mouseRect) &&
        _currentState.LeftButton == ButtonState.Pressed)
        {
            controlItem = this;
            return;
        }
     if (rectangle.Intersects(mouseRect) &&
                _currentState.RightButton == ButtonState.Released && _prevState.RightButton == ButtonState.Pressed)
        {
             RotateItem((float)Math.PI / 2);
             UpdateTransformMatrix();
             UpdateRectangle();
        }

        controlItem = null;
}
private void RotateItem(float rotation)
{
    rot += rotation;
    UpdateRectangle();
}
private void UpdateRectangle()
{
    Vector2 tl = Vector2.Transform(Vector2.Zero, transform);
    Vector2 tr = Vector2.Transform(new Vector2(scaledWidth, 0), transform);
    Vector2 bl = Vector2.Transform(new Vector2(0, scaledHeight), transform);
    Vector2 br = Vector2.Transform(new Vector2(scaledWidth, scaledHeight), transform);

    Vector2 min = new Vector2(FindSmallest(tl.X, tr.X, bl.X, br.X), FindSmallest(tl.Y, tr.Y, bl.Y, br.Y));
    Vector2 max = new Vector2(FindBiggest(tl.X, tr.X, bl.X, br.X), FindBiggest(tl.Y, tr.Y, bl.Y, br.Y));

    rectangle = new Rectangle((int)min.X, (int)min.Y, (int)(max.X - min.X), (int)(max.Y - min.Y));
}

private void UpdateTransformMatrix()
{
    transform = Matrix.CreateTranslation(new Vector3(-Origin / 1.7f, 0))
                * Matrix.CreateRotationZ(rot)
                * Matrix.CreateTranslation(new Vector3(position, 0));
}

\$\endgroup\$
0
\$\begingroup\$

Collisions on rotated axes is not a simple as rotated rectangles, unless the the rotation is common among all possible colliders, at that point rotate the camera and leave the drawing and collisions alone.

For square or close to square objects, use a circle or Manhatten distance to approximate the collision distance, fudge size as needed, smaller for enemy and characters, larger for bonus or collectable items.

To make sure I answer the the question as asked: To properly rotate a rectangle for collisions(i.e. for Intersect() to work, it requires axis alignment.), the bounds must grow to fit the out-most extents(bounds) of the rotated object. Rotating the rectangle off axis is not helpful. All rectangle intersection code assumes common axes, since they are defined by top-left and width and height on the common axes.

Yes, with four raycasts per object pair of rotated "rectangles"(source point, end point, and +- deviations, orthogonal width or height...), rays, point(or line) line intersections can be costly.

Due to the curse of dimensionality, this is the preferred method in three+ dimensions, when combined with octree or BVH, or other culling methods.

In 2D AABB, axis aligned bounding boxes, is the most efficient general collision method for 2D collisions. (I tend to use a more memory intensive grid or quadtree structure for spacial optimizations, but that is not appropriate for rotated items of arbitrary size.)

The main problem is broad culling, your UpdateRectangle() method produces the axis aligned largest rectangle.

Once an overlap is detected, use the Rectangle.Intersect() method to narrow the search area. This area is small enough for a more complex method like pixel overlap check, or recursive bounds checks, that takes a small amount time to fit within the time budget.

\$\endgroup\$
-1
\$\begingroup\$

i've seen that you rotate the image and the collision, in fact you Don't need to do it with the collision box;

Simply switch width and height of it will work because all you do is 90° rotations.

c# function that could work in this case:

// assuming your collision rectangle is called colRect
public void rotateCollisionRectangle()
{
    // here i Simply switch height and width
    colRect = new Rectangle(colRect.height, colRect.width);
    rot += Math.Pi / 2;
}

also your rectangle when drawn appear a bit Strange, i experienced similare thing when the width was negative. If it's the case, offset the rectangle x and y and say that height and width are positive

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .