Raycast Without Colliders

Basically, I want to select objects with the left mouse button. My objects do not have and should not have colliders.

Now if they did, selecting them using

 if (Physics.Raycast(ray.origin, ray.direction, out hit))

is pretty simple.

How would I go about doing this as my objects don’t have colliders?

Put colliders on them anyway. Then set the colliders to a different physics layer and make that layer non-collisive with every layer (including itself) (Edit > Project Settings > Physics). Then use layer-based raycasts to find them. :slight_smile:

Look at this Threadpost: http://forum.unity3d.com/threads/14378-Raycast-without-colliders
It explains in detail how to implement your own raycasting algorithm, only using triangle information and no colliders.

turn on IS TRIGGER option on collider … perfect

STRANGE SOLUTION: to raycast via pixel color, so you can check if you hit a visible pixel of an object…

Make a small sphere with preferably the same color regardless of lighting and textures, I don’t know how that is done, perhaps pink sphere of shaderless unity objects…

If you click with the mouse, then scan a ray in a line from the camera by moving the pink sphere backwards and forwards through space, and see if it gets covered up by a pixel of a different color:

Place sphere 1 meter away... if pixel = pink:
place sphere 1000 meters away, if pixel != pink:
place sphere 500 meters away, if pixel = pink:
place sphere 750 meters away... etc. 

In this way you can devide the distance where the collider object is in 2 and slowly narrow down to the precise point in space where the pink object is covered by another object.

if you devide 3000m by 2 many times, it takes 16 steps to find your collision object to within 0.1 meters… so perhaps 16 frames, and say 10 steps to search 200 meters range.

If it is necessary to take 16 frames, i dont know. perhaps it can be done in one frame?

Anyways, when you have the location of the collision pixel, you can go through all the objects on the screen, compare their position, perhaps compare all the triangles on the screen if you want to be very precise. it would be convenient to have a list of triangles in a particular point in space!?

If someone has advice about the above implementation, and ways to refine it or think’s it’s abit nonsense, I would be very interestested to have it optimised!

I found the solution of “just throw a mesh collider on it” to not be acceptable.

However, it is probably the right call for anything runtime. If you want something for the editor, use this. (Caveats included: This was designed for a 2d system to enable dragging in the 2d plane from a 3d camera view.)

Hopefully, someone will take the information here and distill it into a reusable form:

using UnityEngine;
using UnityEditor;
using System.Collections;
using System;
using System.Reflection;
using System.Linq;
using System.Collections.Generic;

[CustomEditor(typeof(BaseUnit), true)]
public class ClickBaseUnitEditor : Vexe.Editor.Editors.BaseBehaviourEditor
{
    public static bool blockingMouseInput = false; 
    public static Vector3 offset = Vector3.zero;

    public const string SuspendedBuff = "_EDITOR_Suspended";
    public void SuspendUnit(BaseUnit unit)
    {
        if ( Application.isPlaying )
        { /// custom stuff    
        }
    }
    public void ReleaseUnit(BaseUnit unit)
    {/// custom stuff
    }

    public static Ray GetMouseClickRay()
    {
        Event e = Event.current;
        var camera =  SceneView.lastActiveSceneView.camera;

        var mousePosition = e.mousePosition;
        mousePosition.y = camera.pixelHeight - mousePosition.y;
        var ray = camera.ScreenPointToRay(mousePosition);

        return ray;
    }

    public static bool MouseClickRaycheckSpineObjects(out IList<BaseUnit> spineUnitsFound, out IList<Vector3> hits)
    {
        var ray = GetMouseClickRay();
        var spineObjs = Transform.FindObjectsOfType<SkeletonAnimation>();
        spineUnitsFound = new List<BaseUnit>();
        hits = new List<Vector3>();
        //Debug.DrawRay(ray.origin, ray.direction * 80f + new Vector3(0.1f,0f,0f), Color.blue,10);

        foreach ( var spineGameObj in spineObjs )
        {
            var root = spineGameObj.transform.position;
            var intersectFound = false;
            var meshFilter = spineGameObj.GetComponent<MeshFilter>();
            if ( meshFilter != null )
            {
                var mesh = meshFilter.sharedMesh;
                for ( int subMeshIndex  = 0; subMeshIndex < mesh.subMeshCount; subMeshIndex ++ )
                { 
                    var triangles = mesh.GetTriangles(subMeshIndex);
                    for ( int iTriBegin = 0; iTriBegin < triangles.Length; iTriBegin += 3 )
                    {
                        Vector3 a, b, c;
                        a = mesh.vertices[triangles[iTriBegin]];
                        b = mesh.vertices[triangles[iTriBegin+1]];
                        c = mesh.vertices[triangles[iTriBegin+2]];

                        a = spineGameObj.transform.TransformPoint(a); 
                        b = spineGameObj.transform.TransformPoint(b); 
                        c = spineGameObj.transform.TransformPoint(c); 

                        if ( Intersect(a, b, c, ray) )
                        {
                            intersectFound = true;
                            break;
                        }
                    }

                    if ( intersectFound ) break;
                }
            }

            if ( intersectFound )
            {
                var baseUnit = spineGameObj.GetComponentInParent<BaseUnit>();
                if ( baseUnit )
                { 
                    var plane = new Plane( Vector3.back, spineGameObj.transform.position );
                    Vector3 p = Vector3.zero;
                    float rayDistance;
                    if ( plane.Raycast(ray, out rayDistance )) {
                        p = ray.GetPoint(rayDistance);                    
                    }

                    if ( baseUnit.gameObject == Selection.activeGameObject )
                    {
                        spineUnitsFound.Insert(0, baseUnit);
                        hits.Insert(0, p);
                    }
                    else
                    {
                        spineUnitsFound.Add(baseUnit);
                        hits.Add( p);
                    }
                }
            }
        }

        if ( spineUnitsFound.Count > 0 ) 
            return true;

        return false;
    }
    public static bool MouseClickRaycheckBaseUnit(out IList<BaseUnit> unitsFound, out IList<Vector3> hits)
    {
        var ray = GetMouseClickRay();
        var coll = Physics2D.RaycastAll(ray.origin, ray.direction, Mathf.Infinity);

        //Debug.DrawRay(ray.origin, ray.direction * 80f + new Vector3(0.1f,0f,0f), Color.blue,10);

        unitsFound = new List<BaseUnit>();
        hits = new List<Vector3>();

        foreach ( var c in coll )
        {
            var baseUnit = c.collider.gameObject.GetComponentInParent<BaseUnit>();
            if ( baseUnit )
            { 
                var plane = new Plane( Vector3.back, baseUnit.transform.position);
                Vector3 p = Vector3.zero;
                float rayDistance;
                if ( plane.Raycast(ray, out rayDistance )) {
                    p = ray.GetPoint(rayDistance);                    
                }

                if ( baseUnit.gameObject == Selection.activeGameObject )
                {
                    unitsFound.Insert(0, baseUnit);
                    hits.Insert(0, p);
                }
                else
                {
                    unitsFound.Add(baseUnit);
                    hits.Add( p);
                }
            }
        }

        IList<BaseUnit> spineUnitsFound;
        IList<Vector3> spineHits;
        var anythingFound = MouseClickRaycheckSpineObjects( out spineUnitsFound, out spineHits );

        if ( anythingFound )
        {
            for ( int i = 0; i < spineUnitsFound.Count; i ++ )
            {
                var baseUnit = spineUnitsFound*;*

var p = spineHits*;*

if ( unitsFound.Contains(baseUnit) )
continue;

if ( baseUnit.gameObject == Selection.activeGameObject )
{
unitsFound.Insert(0, baseUnit);
hits.Insert(0, p);
}
else
{
unitsFound.Add(baseUnit);
hits.Add( p);
}
}
}

if ( unitsFound.Count > 0 )
return true;

return false;
}

public static bool MouseClickRaycheckAgainstBaseUnitPlane(BaseUnit b, out Vector3 intersect)
{
intersect = Vector3.zero;
var plane = new Plane( Vector3.back, b.transform.position);
/*
var camera = SceneView.lastActiveSceneView.camera;
var mousePosition = Event.current.mousePosition;
mousePosition.y = camera.pixelHeight - mousePosition.y;
var ray2 = camera.ScreenPointToRay(mousePosition);
*/
var ray = GetMouseClickRay();

float rayDistance;

if ( plane.Raycast(ray, out rayDistance )) {
intersect = ray.GetPoint(rayDistance);
return true;
}
return false;
}

private void OnSceneGUI( )
{
Event e = Event.current;
var controlID = GUIUtility.GetControlID(FocusType.Passive);
var type = e.GetTypeForControl(controlID );
var selection = Selection.activeGameObject;

switch ( type )
{
case EventType.MouseDown:
GUIUtility.hotControl = controlID;
//Debug.Log(“Mouse Down”);

IList unitsFound;
IList hits;
var anythingFound = MouseClickRaycheckBaseUnit(out unitsFound, out hits);

if ( anythingFound && unitsFound[0].gameObject == Selection.activeGameObject )
{
Debug.Log(“You clicked on:” + unitsFound[0]);
offset = unitsFound[0].transform.position - hits[0];
blockingMouseInput = true;
e.Use();
}
else if ( anythingFound )
{
Selection.activeGameObject = unitsFound[0].gameObject;
}
else
{
Selection.activeObject = null;
}

break;
case EventType.MouseMove:
case EventType.MouseDrag:
if ( blockingMouseInput )
{
if ( selection )
{
var baseUnit = selection.gameObject.GetComponentInParent();
Vector3 p;
if ( MouseClickRaycheckAgainstBaseUnitPlane(baseUnit, out p) )
{
//Debug.Log("Dragging to "+p);
baseUnit.gameObject.transform.position = p + offset;

SuspendUnit( baseUnit );
e.Use();
}
//Debug.Log("Mouse " + type + " | " + e.mousePosition);
}
}
break;
case EventType.MouseUp:
GUIUtility.hotControl = 0;
//Debug.Log(“Mouse Up”);
blockingMouseInput = false;
break;
}
}

///


/// Checks if the specified ray hits the triagnlge descibed by p1, p2 and p3.
/// Möller–Trumbore ray-triangle intersection algorithm implementation.
///

/// Vertex 1 of the triangle.
/// Vertex 2 of the triangle.
/// Vertex 3 of the triangle.
/// The ray to test hit for.
/// true when the ray hits the triangle, otherwise false
public static bool Intersect(Vector3 p1, Vector3 p2, Vector3 p3, Ray ray)
{
// Vectors from p1 to p2/p3 (edges)
Vector3 e1, e2;

Vector3 p, q, t;
float det, invDet, u, v;

//Find vectors for two edges sharing vertex/point p1
e1 = p2 - p1;
e2 = p3 - p1;

// calculating determinant
p = Vector3.Cross(ray.direction, e2);

//Calculate determinat
det = Vector3.Dot(e1, p);

//if determinant is near zero, ray lies in plane of triangle otherwise not
if (det > -Mathf.Epsilon && det < Mathf.Epsilon) { return false; }
invDet = 1.0f / det;

//calculate distance from p1 to ray origin
t = ray.origin - p1;

//Calculate u parameter
u = Vector3.Dot(t, p) * invDet;

//Check for ray hit
if (u < 0 || u > 1) { return false; }

//Prepare to test v parameter
q = Vector3.Cross(t, e1);

//Calculate v parameter
v = Vector3.Dot(ray.direction, q) * invDet;

//Check for ray hit
if (v < 0 || u + v > 1) { return false; }

if ((Vector3.Dot(e2, q) * invDet) > Mathf.Epsilon)
{
//ray does intersect
return true;
}

// No hit at all
return false;
}
}

why shouldn’t your objects have colliders? it doesn’t change any of the movements or rendering for them to have colliders, it just allows things like raycasting to work.