ColliderChecker

From Unify Community Wiki
Revision as of 18:50, 17 February 2020 by Dock (Talk | contribs)

Jump to: navigation, search

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)


ColliderChecker.cs

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 uncomment the Debug Draw to see what's happening
        //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