- Home /
How to Physics.CheckCylinder?
Just like Physics.CheckCapsule or Physics.CheckBox, what I need is a method that can check for collisions on a Cylinder area. Is there anything like that? Or how can I achieve it?
Answer by Bunny83 · Jul 05, 2017 at 10:09 PM
Unity doesn't have a cylinder collider type. That's mainly because a cylinder is more difficult to describe as a mathematical formula. A capsule is easier since it's just two connected spheres. The best workaround is to use a combination of a capsule and a box.
Make sure the box collider is actually large enough to enclose the whole cylinder section. Also make sure the "caps" of the capsule are not included. Something like this:
This should do what you want:
public static bool CheckCylinder(Vector3 aStart, Vector3 aEnd, float aRadius, int aLayerMask, QueryTriggerInteraction aQueryTriggerInteraction)
{
if (!Physics.CheckCapsule(aStart, aEnd, aRadius, aLayerMask, aQueryTriggerInteraction))
return false;
Vector3 dir = aEnd - aStart;
return Physics.CheckBox(aStart + dir * 0.5f, new Vector3(aRadius, aRadius, dir.magnitude*0.5f), Quaternion.LookRotation(dir), aLayerMask, aQueryTriggerInteraction);
}
This let you specify the top center point and the bottom center point of your cylinder and the radius. It will only return true when an object overlaps with both colliders.
It's not perfect as it has some edge cases where a large object at a 45! angle might touch both colliders but is still outside the cylinder. You can't really get a perfect match with the available methods. However you can increase the precision by using another box that is 45° rotated along the cylinder axis. That would "flatten" the edges of the two boxes.
public static bool CheckCylinder(Vector3 aStart, Vector3 aEnd, float aRadius, int aLayerMask, QueryTriggerInteraction aQueryTriggerInteraction)
{
if (!Physics.CheckCapsule(aStart, aEnd, aRadius, aLayerMask, aQueryTriggerInteraction))
return false;
Vector3 dir = aEnd - aStart;
Quaternion q = Quaternion.LookRotation(dir);
if (!Physics.CheckBox(aStart + dir * 0.5f, new Vector3(aRadius, aRadius, dir.magnitude*0.5f), q, aLayerMask, aQueryTriggerInteraction))
return false;
q = Quaternion.AngleAxis(45, dir) * q;
if (!Physics.CheckBox(aStart + dir * 0.5f, new Vector3(aRadius, aRadius, dir.magnitude*0.5f), q, aLayerMask, aQueryTriggerInteraction))
return false;
return true;
}
Of course this "rotation" of the boxes could be made smaller and smaller and you would use more and more boxes to increase precision.
edit Probably the best solution would be this one:
public static bool CheckCylinder(Vector3 aStart, Vector3 aEnd, float aRadius, int aLayerMask, QueryTriggerInteraction aQueryTriggerInteraction)
{
if (!Physics.CheckCapsule(aStart, aEnd, aRadius, aLayerMask, aQueryTriggerInteraction))
return false;
Vector3 dir = aEnd - aStart;
Quaternion q = Quaternion.LookRotation(dir);
Quaternion q2 = Quaternion.AngleAxis(45f, dir);
Vector3 size = new Vector3(aRadius, aRadius / (1f + Mathf.Sqrt(2f)), dir.magnitude * 0.5f);
for (int i = 0; i < 4; i++)
{
if (Physics.CheckBox(aStart + dir * 0.5f, size, q, aLayerMask, aQueryTriggerInteraction))
return true;
q = q2 * q;
}
return false;
}
It uses one capsule and 4 boxes. The 4 boxes actually form an octagon on the outside of the capsule. In this case we need a collision with the capsule and any of the boxes. I can't attach another image so i post it as comment below.
Hmm, seems i can't upload an image in a comment. So here it is on my dropbox:
Solution worked really well but needed an adjustment, the capsule's caps should extrapolate from Start and End positions. From my tests the code above had the capsule the same size as the boxes.
if (!Physics.CheckCapsule(aStart, aEnd, aRadius, aLayer$$anonymous$$ask, aQueryTriggerInteraction))
should be replaced by
if( !Physics.CheckCapsule(aStart- (dir.normalized*radius), aEnd+ (dir.normalized * aRadius), radius, aLayer$$anonymous$$ask, aQueryTriggerInteraction))
Just out of curiosity, whats the "radius / (1 + sqrt(2))" formula for?
Here's the function as I'm using now, tested it on all possible ways and it's working perfectly. The code includes some DebugDraw for testing purposes, just ignore it.
public static bool CheckCylinder(Vector3 start, Vector3 end, float radius, int layer$$anonymous$$ask, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal)
{
Vector3 dir = end - start;
bool overlappingCapsule = Physics.CheckCapsule(start - (dir.normalized*radius), end + (dir.normalized * radius), radius, layer$$anonymous$$ask, queryTriggerInteraction);
DebugExtension.DebugCapsule(start - (dir.normalized * radius), end + (dir.normalized * radius), overlappingCapsule ? Color.red : Color.green, radius);
//If not colliding with capsule isn't colliding at all
if (!overlappingCapsule)
return false;
//If here it's inside the capsule area
Quaternion q = Quaternion.LookRotation(dir);
Quaternion q2 = Quaternion.AngleAxis(45f, dir);
Vector3 size = new Vector3(radius, radius / (1f + $$anonymous$$athf.Sqrt(2f)), dir.magnitude * 0.5f);
bool boxOverllaping;
for (int i = 0; i < 4; i++)
{
boxOverllaping = Physics.CheckBox(start + dir * 0.5f, size, q, layer$$anonymous$$ask, queryTriggerInteraction);
DebugExtension.DebugBox(start + dir * 0.5f, size, q, boxOverllaping ? Color.red : Color.green);
//Inside capsure area and inside box area
if (boxOverllaping)
return true;
q = q2 * q;
}
//Inside capsure area but not inside any box. This means it's inside the capsule "caps" area.
return false;
}
Answer by Kishotta · Jul 05, 2017 at 10:01 PM
There is no CheckCylinder, but you could check for a capsule that extends beyond the cap of the cylinder, and then also check for a box that aligns with the caps of the cylinder. If both checks pass, you have a cylinder collision.