Skip to content

Instantly share code, notes, and snippets.

@andrew-raphael-lukasik
Last active August 9, 2023 20:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save andrew-raphael-lukasik/917046557baed6646c9fdc3b40671307 to your computer and use it in GitHub Desktop.
Save andrew-raphael-lukasik/917046557baed6646c9fdc3b40671307 to your computer and use it in GitHub Desktop.
Simple entity selection system
using UnityEngine;
using Unity.Mathematics;
using Unity.Entities;
using Unity.Transforms;
using Unity.Rendering;
public class LookAtDoubleClickedEntity : MonoBehaviour
{
    void OnEnable ()
    {
        SelectionSystem.onDoubleClick += OnDoubleClick;
    }
    void OnDisable ()
    {
        SelectionSystem.onDoubleClick -= OnDoubleClick;
    }
    void OnDoubleClick ( Entity entity , EntityManager entityManager )
    {
        var clickedTransform = entityManager.GetComponentData<LocalToWorld>( entity );
        Vector3 clickedPos = clickedTransform.Position;
        
        AABB clickedBounds = entityManager.GetComponentData<WorldRenderBounds>( entity ).Value;
        Vector3 clickedCenter = clickedBounds.Center;

        Debug.DrawLine( transform.position , clickedBounds.Center , Color.magenta , 5f );
        transform.rotation = Quaternion.LookRotation( clickedCenter-transform.position , Vector3.up );
    }
}
// src: https://gist.github.com/andrew-raphael-lukasik/917046557baed6646c9fdc3b40671307
using UnityEngine;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Collections;
using Unity.Rendering;
[UpdateInGroup( typeof(PresentationSystemGroup) )]
public class SelectionSystem : SystemBase
{
public static event System.Action<Entity,EntityManager> onClick = (clicked,clickedEntityManager)=>
{
#if DEBUG
Debug.Log($"{clicked} <b>clicked</b>");
#endif
};
public static event System.Action<Entity,EntityManager> onDoubleClick = (clicked,clickedEntityManager)=>
{
#if DEBUG
Debug.Log($"{clicked} <b>double-clicked</b>");
#endif
};
const double k_double_click_threshold = 0.5f;
public NativeQueue<(Entity entity,float distance)> _hits = new NativeQueue<(Entity,float)>( Allocator.Persistent );
public NativeArray<(Entity entity,double time)> _clicks = new NativeArray<(Entity,double)>( length:2 , Allocator.Persistent );
protected override void OnUpdate ()
{
if( EntityManager.Exists(_clicks[0].entity) )
{
var click = _clicks[0];
var oldClick = _clicks[1];
if( click.entity==oldClick.entity && (click.time-oldClick.time)<k_double_click_threshold )
onDoubleClick( click.entity , EntityManager );
else
onClick( click.entity , EntityManager );
_clicks[1] = click;
_clicks[0] = ( Entity.Null , 0f );
}
if( Input.GetMouseButtonDown(0) )
{
var ray = Camera.main.ScreenPointToRay( Input.mousePosition );
var hits = _hits;
var hits_writer = _hits.AsParallelWriter();
var clicks = _clicks.Slice();// alias
double time = Time.ElapsedTime;
Entities
.WithNone<Disabled,Prefab>()
// .WithAll<SELECTABLE>()// uncomment to enable this filter
.ForEach( ( in Entity entity , in WorldRenderBounds wrb ) =>
{
if( wrb.Value.ToBounds().IntersectRay(ray,out float distance) )
hits_writer.Enqueue( (entity,distance) );
})
.WithBurst().Schedule();
Job
.WithName("select_best_click_candidate_job")
.WithCode( ()=>
{
Entity nearest = Entity.Null;
float dist = float.MaxValue;
while( hits.Count!=0 )
{
var hit = hits.Dequeue();
if( hit.distance<dist )
{
nearest = hit.entity;
dist = hit.distance;
}
}
clicks[0] = ( entity:nearest , time:time );
})
.WithBurst().Schedule();
}
}
protected override void OnDestroy ()
{
_hits.Dispose();
_clicks.Dispose();
}
}
// <summary> Tag component to flag selectable entities. </summary>
public struct SELECTABLE : IComponentData {}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment