6
\$\begingroup\$

I have a situation where a sprite's center point needs to be constrained within a certain boundary in 2D space. The boundary is circular, so the sprite is constrained within a radius. This radius is defined as a distance from the center of a certain point.

I've got this code, but it only tells me when the center has overstepped the allowed radius:

float distance = Vector2.Distance(centerPosition, spritePosition));
if (distance > allowedDistance) {
    // Point is outside allowed range
}

I would like to also return the sprite's center to the closest point within that radius whenever it oversteps the bounds.

How can I do that?

\$\endgroup\$

4 Answers 4

5
\$\begingroup\$

Take the position of the object, minus the origin of the circle. This will give you a vector from the origin to the object. The length of this vector is the distance to the object. Multiply this vector by the circle's radius divided by the distance to the object. Set the object's position to the circle's origin plus this vector.

Psuedo-Code:

Vector FromCircleToObject = Object.Position - Circle.Origin;
FromCircleToObject *= Circle.Radius / FromCircleToObject.Length;
Object.Position = Circle.Origin + FromCircleToObject;

You can optimize this by using the vector's length squared. This saves you a square root. If you use the vector's length squared, you must use the circle's radius squared. Luckily, squaring is cheaper than square rooting, so there's still an optimization to be had.

\$\endgroup\$
1
  • \$\begingroup\$ Thank you for this. It is the ONLY answer that is actually readable / easy to understand. \$\endgroup\$
    – user50286
    Commented Feb 6, 2017 at 14:55
2
\$\begingroup\$

What you need to do is reset the sprite's position back to it's greatest extent.

You can do this by taking the vector from the centre to the sprites position, normalising it and then multiplying that by the allowed distance. However, that involves a square root which may be too slow for your game.

Another approach is to limit the sprite to a box around the centre position so your test would become:

if (spritePosition.x > centrePosition.x + allowedDistance)
{
    spritePosition.x = centrePosition.x + allowedDistance;
}
else if (spritePosition.x < centrePosition.x - allowedDistance)
{
    spritePosition.x = centrePosition.x - allowedDistance;
}

and the same for the y.

This obviously doesn't give the same effect but may be quicker. You'd have to profile them both to see and also check the behaviour to see if was acceptable.

\$\endgroup\$
4
  • \$\begingroup\$ I tried this but in a somewhat different manner, this results in the sprite position to be constrained within a square of the center point. I really want the boundary to be circular.. thx though! \$\endgroup\$
    – Phil
    Commented Mar 6, 2011 at 17:42
  • \$\begingroup\$ Oh, and I'll try to use your first suggestion, I was there at one point but didn't figure it out, I'll try it again. Would be extremely thankful if you could show me that part in code. \$\endgroup\$
    – Phil
    Commented Mar 6, 2011 at 17:44
  • \$\begingroup\$ @Phil - I should have made it clear that while the box check is quicker it is just that a box check. \$\endgroup\$
    – ChrisF
    Commented Mar 6, 2011 at 17:46
  • \$\begingroup\$ You did :) I was too fast in my comment and missed your line just above the code. \$\endgroup\$
    – Phil
    Commented Mar 6, 2011 at 17:50
2
\$\begingroup\$

For anyone struggling to visualise it:

diagrammatic explanation

If the distance between A and B is less than the radius, we can quit early. B is already within radius of A, so everything is fine.

Otherwise, find the vector u, from A to B. Divide u by the distance to get the unit vector û. Multiply that by the radius to get a vector to the furthest point from A toward B that is still within radius of A.

\$\endgroup\$
3
  • \$\begingroup\$ What is vector u? Line A to B is the distance. But that doesn't make sense to divide u (distance) by distance. It would always be 1. \$\endgroup\$
    – user50286
    Commented Feb 6, 2017 at 14:48
  • \$\begingroup\$ Vector u is B - A. This graphic isn't helpful at all. I had to read Sion's answer 4 times to finally figure this out. Wish Sion had the graphic. \$\endgroup\$
    – user50286
    Commented Feb 6, 2017 at 14:53
  • \$\begingroup\$ I'm glad you figured it out. How would you suggest improving the diagram? The source SVG is here if it's easiest to show by editing. \$\endgroup\$
    – Anko
    Commented Feb 6, 2017 at 19:02
0
\$\begingroup\$

So, your sprite has a rectangular shape? If so, you are effectively asking, "How do I move a rectangle that intersects with a circle inside the circle so that the rectangle no longer intersects". I assume then that the rectangle is in fact smaller than your circle and the sprite position represents the center of the sprite. If it doesn't, you can convert from top-left to center by simply adding 1/2 the width, height, that is:

center.X = spritePosition.x + spriteImageSize.x/2;
center.Y = spritePosition.y + spriteImageSize.y/2;

In that case, you need to consider the 4 points of the sprite. Check if the first one is outside, and if it is, move it so it isn't. I don't have a formal proof, but I conjecture that it is impossible for you to move a corner inside the circle such that it puts another corner outside that was previously inside. This depends strongly on the fact that the rectangle fits inside the circle.

The code would be something like this:

TL = sprite.getTopLeft();
if(outside(TL, circle)) {
newPoint = moveInside(TL); //see how to move a point inside 
delta = newPoint - TL; //figure out how much we moved
spritePosition += delta; //move the center of the sprite
}

//repeat for TR, BL, BR -- IF YOU MOVE THE SPRITE, YOU MUST RECOMPUTE THE CORNERS! 

If you don't need that level of detail and you'd rather treat the sprite as an infinitely small single point, then you simply need to detect when it is outside the circle and set it so that it is inside. See ChrisF's response on how to do that.

\$\endgroup\$
1
  • \$\begingroup\$ It's just the center point of the sprite that needs to be within the radius, not effectively the sprite itself. Updated my question, sorry for being unclear. \$\endgroup\$
    – Phil
    Commented Mar 6, 2011 at 17:41

You must log in to answer this question.

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