177 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			177 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using System.Collections.Generic;
 | |
| using UnityEngine.XR.Interaction.Toolkit;
 | |
| 
 | |
| namespace UnityEngine.XR.Content.Interaction
 | |
| {
 | |
|     /// <summary>
 | |
|     /// Socket Interactor for holding a group of Interactables in a 2D grid.
 | |
|     /// </summary>
 | |
|     /// <remarks>
 | |
|     /// The grid starts at the position of the Attach Transform.
 | |
|     /// During Awake, a Grid Socket instantiates one GameObject (as child of its Attach Transform) for each grid cell.
 | |
|     /// The Transform component of these instantiated objects are used as the actual attach point for the Interactables.
 | |
|     /// </remarks>
 | |
|     public class XRGridSocketInteractor : XRSocketInteractor
 | |
|     {
 | |
|         [Space]
 | |
|         [SerializeField]
 | |
|         [Tooltip("The grid width. The grid width is along the Attach Transform's local X axis.")]
 | |
|         int m_GridWidth = 2;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// The grid width. The grid width is along the Attach Transform's local X axis.
 | |
|         /// </summary>
 | |
|         public int gridWidth
 | |
|         {
 | |
|             get => m_GridWidth;
 | |
|             set => m_GridWidth = Mathf.Max(1, value);
 | |
|         }
 | |
| 
 | |
|         [SerializeField]
 | |
|         [Tooltip("The grid height. The grid height is along the Attach Transform's local Y axis.")]
 | |
|         int m_GridHeight = 2;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// The grid height. The grid height is along the Attach Transform's local Y axis.
 | |
|         /// </summary>
 | |
|         public int gridHeight
 | |
|         {
 | |
|             get => m_GridHeight;
 | |
|             set => m_GridHeight = Mathf.Max(1, value);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// (Read Only) The grid size. The maximum number of Interactables that this Interactor can hold.
 | |
|         /// </summary>
 | |
|         public int gridSize => m_GridWidth * m_GridHeight;
 | |
| 
 | |
|         [SerializeField]
 | |
|         [Tooltip("The distance (in local space) between cells in the grid.")]
 | |
|         Vector2 m_CellOffset = new Vector2(0.1f, 0.1f);
 | |
| 
 | |
|         /// <summary>
 | |
|         /// The distance (in local space) between cells in the grid.
 | |
|         /// </summary>
 | |
|         public Vector2 cellOffset
 | |
|         {
 | |
|             get => m_CellOffset;
 | |
|             set => m_CellOffset = value;
 | |
|         }
 | |
| 
 | |
|         readonly HashSet<Transform> m_UnorderedUsedAttachedTransform = new HashSet<Transform>();
 | |
|         readonly Dictionary<IXRInteractable, Transform> m_UsedAttachTransformByInteractable =
 | |
|             new Dictionary<IXRInteractable, Transform>();
 | |
| 
 | |
|         Transform[,] m_Grid;
 | |
| 
 | |
|         bool hasEmptyAttachTransform => m_UnorderedUsedAttachedTransform.Count < gridSize;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Creates the grid.
 | |
|         /// </summary>
 | |
|         void CreateGrid()
 | |
|         {
 | |
|             m_Grid = new Transform[m_GridHeight, m_GridWidth];
 | |
| 
 | |
|             for (var i = 0; i < m_GridHeight; i++)
 | |
|             {
 | |
|                 for (var j = 0; j < m_GridWidth; j++)
 | |
|                 {
 | |
|                     var attachTransformInstance = new GameObject($"[{gameObject.name}] Attach ({i},{j})").transform;
 | |
|                     attachTransformInstance.SetParent(attachTransform, false);
 | |
| 
 | |
|                     var offset = new Vector3(j * m_CellOffset.x, i * m_CellOffset.y, 0f);
 | |
|                     attachTransformInstance.localPosition = offset;
 | |
| 
 | |
|                     m_Grid[i, j] = attachTransformInstance;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /// <inheritdoc />
 | |
|         protected override void Awake()
 | |
|         {
 | |
|             base.Awake();
 | |
|             CreateGrid();
 | |
| 
 | |
|             // The same material is used on both situations
 | |
|             interactableCantHoverMeshMaterial = interactableHoverMeshMaterial;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// See <see cref="MonoBehaviour"/>.
 | |
|         /// </summary>
 | |
|         protected virtual void OnValidate()
 | |
|         {
 | |
|             m_GridWidth = Mathf.Max(1, m_GridWidth);
 | |
|             m_GridHeight = Mathf.Max(1, m_GridHeight);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// See <see cref="MonoBehaviour"/>.
 | |
|         /// </summary>
 | |
|         protected virtual void OnDrawGizmosSelected()
 | |
|         {
 | |
|             Gizmos.color = Color.green;
 | |
|             Gizmos.matrix = attachTransform != null ? attachTransform.localToWorldMatrix : transform.localToWorldMatrix;
 | |
|             for (var i = 0; i < m_GridHeight; i++)
 | |
|             {
 | |
|                 for (var j = 0; j < m_GridWidth; j++)
 | |
|                 {
 | |
|                     var currentPosition = new Vector3(j * m_CellOffset.x, i * m_CellOffset.y, 0f);
 | |
|                     Gizmos.DrawLine(currentPosition + (Vector3.left * m_CellOffset.x * 0.5f), currentPosition + (Vector3.right * m_CellOffset.y * 0.5f));
 | |
|                     Gizmos.DrawLine(currentPosition + (Vector3.down * m_CellOffset.x * 0.5f), currentPosition + (Vector3.up * m_CellOffset.y * 0.5f));
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /// <inheritdoc />
 | |
|         protected override void OnSelectEntering(SelectEnterEventArgs args)
 | |
|         {
 | |
|             base.OnSelectEntering(args);
 | |
| 
 | |
|             var closestAttachTransform = GetAttachTransform(args.interactableObject);
 | |
|             m_UnorderedUsedAttachedTransform.Add(closestAttachTransform);
 | |
|             m_UsedAttachTransformByInteractable.Add(args.interactableObject, closestAttachTransform);
 | |
|         }
 | |
| 
 | |
|         /// <inheritdoc />
 | |
|         protected override void OnSelectExiting(SelectExitEventArgs args)
 | |
|         {
 | |
|             var closestAttachTransform = m_UsedAttachTransformByInteractable[args.interactableObject];
 | |
|             m_UnorderedUsedAttachedTransform.Remove(closestAttachTransform);
 | |
|             m_UsedAttachTransformByInteractable.Remove(args.interactableObject);
 | |
| 
 | |
|             base.OnSelectExiting(args);
 | |
|         }
 | |
| 
 | |
|         /// <inheritdoc />
 | |
|         public override bool CanSelect(IXRSelectInteractable interactable)
 | |
|         {
 | |
|             return IsSelecting(interactable)
 | |
|                    || (hasEmptyAttachTransform && !interactable.isSelected && !m_UnorderedUsedAttachedTransform.Contains(GetAttachTransform(interactable)));
 | |
|         }
 | |
| 
 | |
|         /// <inheritdoc />
 | |
|         public override bool CanHover(IXRHoverInteractable interactable)
 | |
|         {
 | |
|             return base.CanHover(interactable)
 | |
|                    && !m_UnorderedUsedAttachedTransform.Contains(GetAttachTransform(interactable));
 | |
|         }
 | |
| 
 | |
|         /// <inheritdoc />
 | |
|         public override Transform GetAttachTransform(IXRInteractable interactable)
 | |
|         {
 | |
|             if (m_UsedAttachTransformByInteractable.TryGetValue(interactable, out var interactableAttachTransform))
 | |
|                 return interactableAttachTransform;
 | |
| 
 | |
|             var interactableLocalPosition = attachTransform.InverseTransformPoint(interactable.GetAttachTransform(this).position);
 | |
|             var i = Mathf.RoundToInt(interactableLocalPosition.y / m_CellOffset.y);
 | |
|             var j = Mathf.RoundToInt(interactableLocalPosition.x / m_CellOffset.x);
 | |
|             i = Mathf.Clamp(i, 0, m_GridHeight - 1);
 | |
|             j = Mathf.Clamp(j, 0, m_GridWidth - 1);
 | |
|             return m_Grid[i, j];
 | |
|         }
 | |
|     }
 | |
| }
 |