- Home /
Get closest Vector3 position from a GameObject and two Transforms (and the line inbetween them)
Hi,
I need to get a Vector3 inside the range between two Transforms, having both Transform as the limit position regarding a third GameObject floating aroung the scene.
I drew an example hoping it would help.
All those Cubes would be the same GameObject and it's possible positions, I mean, it could be anywhere... but the closest V3 would be constrained inside the line between Transform 1 and Transform 2.
I'm working in C#, but if you have the answer in JS I don't care, just need the logic or initial direction in how to solve this problem.
Thanks in advanced!
Answer by dkjunior · Oct 16, 2015 at 01:11 AM
Basically what you need is to draw a perpendicular from a given point to a line. Here's a function to do that (first solution that came to my head, this can probably be done more elegantly).
private Vector3 ClosestPoint(Vector3 limit1, Vector3 limit2, Vector3 point)
{
Vector3 lineVector = limit2 - limit1;
float lineVectorSqrMag = lineVector.sqrMagnitude;
// Trivial case where limit1 == limit2
if(lineVectorSqrMag < 1e-3f)
return limit1;
float dotProduct = Vector3.Dot(lineVector, limit1 - point);
float t = - dotProduct / lineVectorSqrMag;
return limit1 + Mathf.Clamp01(t) * lineVector;
}
The function takes two limiting points (as in your example), and the location of the floating object. It returns a point on the line between two limits that is closest to the floating object.
Thank you very much. This function is returning exactly what I was looking for. Good thing my drawing didn't confused you!
I need to study your answer to understand what are you doing; but it does exactly what I was asking for. Again, thank you!
PS: What's up with that 1e-3f ?? I understand that is to check if there is no distance between limit1 and limit2, but is weird to see that. Is it the same to check for something like this:
if (Vector3.Distance(limit1, limit2) == 0.0F)
Glad it worked for you!
The 1e-3f trick is the practical approach for perfor$$anonymous$$g floating point comparison operations. The thing with floating-point operations is that they will lose precision as you work with them. For example, if you take a (0, 0, 0) point and perform a bunch of transforms on it (move/rotate a few times) so that it eventually returns to its original location, it's very unlikely you will get exactly (0, 0, 0). What you will likely get is something like (0.000001, -0.00000012, 0.0000005). For all practical reasons it's still zeroes, but if you perform a vanilla comparison with 0.0, it won't match and give you many headaches. So whenever you need to compare floating point values for equality it is recommended that you test not for an exact match but for them being within very small distance from each other.
Regarding the algorithm itself, it's basically using line equation in a parameterized form:
p = limit1 + (limit2 - limit1) * t
where each point p belonging to this line is represented by certain value of parameter t. It then tries to find such a value t so that the distance between the point on the line p (corresponding to that t) and your floating point is $$anonymous$$imal. Once found it puts t into line equation to get the point on the line you are looking for. Clamping of t is done so that the resulting point always stays between the limits.
Unity also has the $$anonymous$$athf.Approximately function that does this same thing in a slightly more readable way.
Answer by Bunny83 · Oct 16, 2015 at 01:19 AM
// input values
Transfrom a;
Transform b;
Transform[] gameobjects;
// output values
Transform closest = null;
Vector3 pos;
float minDist = float.PositiveInfinity;
Vector3 posA = a.position;
Vector3 posB = b.position;
Vector3 d = (posB - posA).normalized;
for(int i = 0; i < gameobjects.Length; i++)
{
Vector3 p = gameobjects[i].position;
Vector3 v = p - posA;
Vector3 point = posA + Vector.Project(v, d);
float dist = (point - p).sqrMagnitude;
if (dist < minDist)
{
minDist = dist;
closest = gameobjects[i];
pos = point;
}
}
Untested but should work.
edit
It's hard to tell from your drawing if an object that actually is close to the "ray" that your two transforms defines but it's projected point is outside the line segment what distance you want to calculate.
One way would be to define two Planes which specifiy the ends of the line:
Plane p1 = new Plane(d,posA);
Plane p2 = new Plane(-d,posB);
Inside the for loop you would do:
Vector3 v = p - posA;
Vector3 point = posA + Vector.Project(v, d);
if (!p1.GetSide(p))
point = posA;
if (!p2.GetSide(p))
point = posB;
float dist = (point - p).sqrMagnitude;
// ....
That way you get a capsule shape around your line.
Hello Bunny83! Thanks for answering my question. I will try you code in my testing environment to see how it works.
The answer above worked perfectly when implemented in my project. But I will try and learn from your solution as well. $$anonymous$$orrow when I get back to work I will jump into it.
Thanks again for your time man, have a great day.
PS: Sorry for my drawing, I bet is kinda confusing without the proper description. :p
Your answer
Follow this Question
Related Questions
How do I make and object's position and rotation independent again? 1 Answer
Calculate the distance between an object and my player 1 Answer
Adding rotation to Physics.AddForce(); ... Getting weird error? 0 Answers
Multiple Cars not working 1 Answer
Nav Mesh and transform.position failure! 0 Answers