开发者

How can I perform clipping on rotated rectangles?

So I have this Panel class. It's a little like a Window where you can resize, close, add buttons, sliders, etc. Much like the status screen in Morrowind if any of you remember. The behavior I want is that when a sprite is outside of the panel's bounds it doesn't get drawn and if it's partially outside only the part inside gets drawn. So what it does right now is first get a rectangle that represents the bounds of the panel, and a rectangle for the sprite, it finds the rectangle of intersection between the two then translates that intersection to the local coordinates of the sprite rectangle and uses that for the source rectangle. It works and as clever as I feel the code is I can't shake the feeling that there's a better way to do this. Also, with this set up I cannot utilize a global transformation matrix for my 2D camera, everything in the "world" must be passed a camera argument to draw. Anyway, here's the code I have:

for the Intersection:

     public static Rectangle? Intersection(Rectangle rectangle1, Rectangle rectangle2)
     {
        if (rectangle1.Intersects(rectangle2))
        {
            if (rectangle1.Contains(rectangle2))
            {
                return rectangle2;
            }
            else if (rectangle2.Contains(rectangle1))
            {
                return rectangle1;
            }
            else
            {
                int x = Math.Max(rectangle1.Left, rectangle2.Left);
                int y = Math.Max(rectangle1.Top, rectangle2.Top);
                int height = Math.Min(rectangle1.Bottom, rectangle2.Bottom) - Math.Max(rectangle1.Top, rectangle2.Top);
                int width = Math.Min(rectangle1.Right, rectangle2.Right) - Math.Max(rectangle1.Left, rectangle2.Left);
                return new Rectangle(x, y, width, height);
            }
        }
        else
        {
            return null;
        }
     }

and for actually drawing on the panel:

    public void DrawOnPanel(IDraw sprite, SpriteBatch spriteBatch)
    {
        Rectangle panelRectangle = new Rectangle(
           (int)_position.X,
           (int)_position.Y,
           _width,
           _height);
        Rectangle drawRectangle = new Rectangle();

        drawRectangle.X = (int)sprite.Position.X;
        drawRectangle.Y = (int)sprite.Position.Y;
        drawRectangle.Width = sprite.Width;
        drawRectangle.Height = sprite.Height;

        if (panelRectangle.Contains(drawRectangle))
        {
            sprite.Draw(
                spriteBatch,
                drawRectangle,
                null);
        }
        else if (Intersection(panelRectangle, drawRectangle) == null)
        {
            return;
        }
        else if (Intersection(panelRectangle, drawRectangle).HasValue)
        {
            Rectangle intersection = Intersection(panelRectangle, drawRectangle).Value;

            if (Intersection(panelRectangle, drawRectangle) == drawRectangle)
            {
                sprite.Draw(spriteBatch, intersection, intersection);
            }
            else
            {
                sprite.Draw(
                    spriteBatch,
                    intersection,
                    new Rectangle(
                        intersection.X - drawRectangle.X,
                        intersection.Y - drawRectangle.Y,
                        intersection.Width,
                        intersection.Height));
            }
        }
    }

So I guess my question is, is there a better way to do this?

Update:开发者_JAVA技巧 Just found out about the ScissorRectangle property. This seems like a decent way to do this; it requires a RasterizerState object to be made and passed into the spritebatch.Begin overload that accepts it. Seems like this might be the best bet though. There's also the Viewport which I can apparently change around. Thoughts? :)


There are several ways to limit drawing to a portion of the screen. If the area is rectangular (which seems to be the case here), you could set the viewport (see GraphicsDevice) to the panel's surface.

For non-rectangular areas, you can use the stencil buffer or use some tricks with the depth buffer. Draw the shape of the surface in the stencil buffer or the depth buffer, set your render state to draw only pixels located in the shape you just rendered in the stencil/depth buffer, finally render your sprites.


One way of doing this is simple per-pixel collision. Although this is a bad idea if the sprites are large or numerous, this can be a very easy and fast way to get the job done with small sprites. First, do a bounding circle or bounding square collision check against the panel to see if you even need to do per-pixel detection.

Then, create a contains method that checks if the position, scale, and rotation of the sprite put it so far inside the panel that it must be totally enclosed by the panel, so you don't need per-pixel collision in that case. This can be done pretty easily by just creating a bounding square that has the width and height of the length of the sprite's diagonal, and checking for collision with that.

Finally, if both of these fail, we must do per-pixel collision. Go through and check against every pixel in the sprite to see if it is within the bounds of the panel. If it isn't set the alpha value of the pixel to 0.

Thats it.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜