ColliderChecker

From Unify Community Wiki
(Difference between revisions)
Jump to: navigation, search
m (What it Does)
m (C# Code)
Line 61: Line 61:
 
         foreach (var hit in hitsOutbound) { DebugDrawPoint(hit.point, 0.1f, new Color(0, 1, 0, 0.75f), 0.5f); }
 
         foreach (var hit in hitsOutbound) { DebugDrawPoint(hit.point, 0.1f, new Color(0, 1, 0, 0.75f), 0.5f); }
  
         if (pointIsInsideACollider)
+
         if (pointIsInsideACollider && hitsInbound.Length > 0)
 
             return hitsInbound[hitsInbound.Length - 1].collider;
 
             return hitsInbound[hitsInbound.Length - 1].collider;
 
         else
 
         else

Revision as of 18:48, 17 February 2020

by Hayden Scott-Baron (@docky)

What it Does

This tells you which collider the point is in.

It does this by checking disparity in the number of outbound ray collisions vs inbound ray collisions. It uses a recursive Raycast because RaycastAll returns only one hit position per collider.

This relies on every collider having an entrance and exit during raycast. Avoid non-convex meshcolliders with holes (such as planes with a non-convex meshCollider).

How to Use

This is a static class, so you just need to call it from another class.

Basic check whether this is inside any collider within 1000 units. Pass in radius and mask to improve precision.

bool insideCollider = (ColliderChecker.IsInsideAnyCollider (transform.position));

Reports which collider the point is in. Returns null if not true.

Collider whichCollider = ColliderChecker.WhichColliderIsThisPointWithin(transform.position, range, mask)


C# Code

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
// Simple class to check whether a point in space is inside a collider
// NOTE: NO NON-CONVEX MESH COLLIDERS WITH HOLES!!
// be careful with mesh colliders on planes. Use box colliders and convex where possible. 
// by Hayden Scott-Baron (@docky)
 
public static class ColliderChecker
{
    public static bool IsInsideAnyCollider(Vector3 pos)
    {
        LayerMask mask = ~0; // all layers
        return IsInsideAnyCollider(pos, 1000f, mask);
    }
 
    public static bool IsInsideAnyCollider(Vector3 pos, float radiusOfCheck, LayerMask mask)
    {
        return (WhichColliderIsThisPointWithin(pos, radiusOfCheck, mask) != null);
    }
 
    public static Collider WhichColliderIsThisPointWithin(Vector3 pos, float radiusOfCheck, LayerMask mask)
    {
        // any ray direction is okay, but horizontal will avoid problems with one-sided ground plane
        // note: random ray directon will help you find errors with one-sided geometry, but not necessary if your geometry is whole
        // Ray ray = new Ray (pos, Random.onUnitSphere);
        Ray ray = new Ray(pos, new Vector3(0.5f, 0, 1));
 
        RaycastHit[] hitsOutbound = RecursiveRaycast(ray, radiusOfCheck, mask);
        RaycastHit[] hitsInbound = RecursiveRaycast(new Ray(ray.GetPoint(radiusOfCheck), -ray.direction), radiusOfCheck, mask);
        bool pointIsInsideACollider = (hitsOutbound.Length != hitsInbound.Length);
 
        //note feel free to comment out the Debug Draw
        Debug.DrawLine(ray.origin, ray.GetPoint(radiusOfCheck), new Color(0, 0, 1, 0.5f), 0.25f);
        foreach (var hit in hitsInbound) { DebugDrawPoint(hit.point, 0.1f, new Color(1, 0, 0, 0.5f), 0.5f); }
        foreach (var hit in hitsOutbound) { DebugDrawPoint(hit.point, 0.1f, new Color(0, 1, 0, 0.75f), 0.5f); }
 
        if (pointIsInsideACollider && hitsInbound.Length > 0)
            return hitsInbound[hitsInbound.Length - 1].collider;
        else
            return null;
    }
 
    // Physics.RaycastAll only returns one hit point per collider, so this doesn't work for this.
    // Recursive Raycast iterates through the ray and returns all the points
    public static RaycastHit[] RecursiveRaycast(Ray ray, float maxDist, LayerMask m)
    {
        // build a list of hit points
        List<RaycastHit> hits = new List<RaycastHit>();
        float remainingDist = maxDist;
 
        // I wish this wasn't necessary!
        float fudgeFactor = 0.001f;
 
        while (remainingDist > 0f)
        {
            RaycastHit hit;
            if (Physics.Raycast(ray, out hit, remainingDist, m))
            {
                // add the collision and iterate on the distance
                hits.Add(hit);
 
                // next ray needs to move forward or it'll ignore this collider ¯\_(ツ)_/¯ 
                ray.origin = ray.GetPoint(hit.distance + fudgeFactor);
                remainingDist -= (hit.distance - fudgeFactor);
            }
            else
            {
                // no more collisions
                remainingDist = 0f;
            }
        }
        return hits.ToArray();
    }
 
    private static void DebugDrawPoint(Vector3 pos, float size, Color color, float duration)
    {
        Vector3 v1 = new Vector3(1, 0, 1);
        Vector3 v2 = new Vector3(-1, 0, 1);
        Debug.DrawLine(pos - Vector3.down * size, pos + Vector3.down * size, color, duration);
        Debug.DrawLine(pos - v1 * size, pos + v1 * size, color, duration);
        Debug.DrawLine(pos - v2 * size, pos + v2 * size, color, duration);
    }
 
}
Personal tools
Namespaces

Variants
Actions
Navigation
Extras
Toolbox