Entity collision

I’m working on point cloud generation where each point contains some data and can be clicked on to get and display said data. I’m stumped because clicking can only really be done with a collider and raycast or a pointerdown event.

Can anyone tell me if registering clicks and mouse over events on entities is possible in the current version of ECS?

Entities aren’t real objects. They do not have clicks or hover events.
You can use gameobjects and you can create them as entities as well and process them in ECS. You can channel in the mouse events from the game objects as well. See “Hybrid ECS” how to do it.

You can write your own implementation, it’s not hard check intersection between line and sphere for example. You can create your own ray and check (mathematically) intersection between your “collider” components (create component which represents sphere radius)

You could do something like this:

using UnityEngine;
using Unity.Entities;

public class Clickable : MonoBehaviour
{
    public Bounds bounds;

    private void OnDrawGizmos()
    {
        Gizmos.DrawWireCube(bounds.center, bounds.size);
    }
}

public class ClickSystem : ComponentSystem
{
    ComponentGroup clickableGroup;

    protected override void OnCreateManager()
    {
        clickableGroup = GetComponentGroup(typeof(Clickable));
    }

    protected override void OnUpdate()
    {
        var clickableArray = clickableGroup.GetComponentArray<Clickable>();
        int count = clickableArray.Length;

        Ray mouseRay = Camera.main.ScreenPointToRay(Input.mousePosition);

        for (var i = 0; i < count; i++)
        {
            if (clickableArray[i].bounds.IntersectRay(mouseRay))
            {
                if (Input.GetMouseButtonDown(0))
                {
                    Debug.Log("Entity Clicked");
                }
            }
        }
    }
}
1 Like

What is the point of teasing such a solution :p, if it is used in the hybrid approach also with MonoBehaviour, it does not make sense because here you can use the classic colliders and click events and so on :). It cannot be jobified and bursted, it is slow. You must write simple Ray-sphere, Ray-AABB itersection and just create your own “Collider”. Here my fast writed ray-sphere intersection sample:

using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Transforms;
using UnityEngine;

public struct MySphereCollider : IComponentData
{
    public float radius;
    public float3 centerOffset;
}

public class RaySphereCollisionSystem : ComponentSystem
{
    [BurstCompile]
    public struct CheckRaySpheresIntersection : IJobProcessComponentDataWithEntity<MySphereCollider, Position>
    {
        [ReadOnly] public Ray ray;
        public NativeQueue<Entity>.Concurrent collided;

        public void Execute(Entity entity, int index, [ReadOnly] ref MySphereCollider collider, [ReadOnly] ref Position pos)
        {
            if (CheckIntersection(ray, collider, pos))
            {
                collided.Enqueue(entity);
            }
        }
    }

    public static bool CheckIntersection(Ray ray, MySphereCollider sphere, Position sphereCenter)
    {
        // Find the vector between where the ray starts the the sphere's centre
        float3 center = sphereCenter.Value + sphere.centerOffset;
        float3 difference = center - (float3)ray.origin;

        float differenceLengthSquared = difference.x * difference.x + difference.y * difference.y + difference.z * difference.z;
        float sphereRadiusSquared     = sphere.radius * sphere.radius;

        float distanceAlongRay;

        // If the distance between the ray start and the sphere's centre is less than
        // the radius of the sphere, it means we've intersected. N.B. checking the LengthSquared is faster.
        if (differenceLengthSquared < sphereRadiusSquared)
        {
            return true;
        }

        distanceAlongRay = math.dot(ray.direction, difference);
     
        // If the ray is pointing away from the sphere then we don't ever intersect
        if (distanceAlongRay < 0)
        {
            return false;
        }

        // Next we kinda use Pythagoras to check if we are within the bounds of the sphere
        // if x = radius of sphere
        // if y = distance between ray position and sphere centre
        // if z = the distance we've travelled along the ray
        // if x^2 + z^2 - y^2 < 0, we do not intersect
        float dist = sphereRadiusSquared + distanceAlongRay * distanceAlongRay - differenceLengthSquared;

        return !(dist < 0);
    }

    protected override void OnUpdate()
    {
        if (Input.GetMouseButtonDown(0))
        {
            Ray                 mouseRay         = Camera.main.ScreenPointToRay(Input.mousePosition);
            NativeQueue<Entity> collidedEntities = new NativeQueue<Entity>(Allocator.TempJob);
            var checkIntersectionJob = new CheckRaySpheresIntersection()
            {
                collided = collidedEntities.ToConcurrent(),
                ray      = mouseRay
            };

            checkIntersectionJob.Schedule(this).Complete();

            while (collidedEntities.Count > 0)
            {
                Debug.Log("Intersected entity: " + collidedEntities.Dequeue());
            }

            collidedEntities.Dispose();
        }
    }
}

And if you click on sphere you handle intersection:
3854404--326284--terrain.gif

Spheres here is pure entities with Position, MeshInstanceRenderer and MySphereCollider.

10 Likes

I wasnt sure if i should go full ecs or hybrid! I picked hybrid because I thought it might be easy enough to switch over to full from my code.

Question: is Unity’s Bounds not jobable? It’s probably not Burstable since it doesnt use Mathematics, but since its a struct it should be passable to the Job system right?

Also thank you, i’ll look at this for example when i finally dive into the job system