using NaughtyAttributes; using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; namespace UDE_HAND_INTERACTION { public class FingerPressController : MonoBehaviour { private FingerPressInteractable _fingerPressInteractable; [SerializeField] private Transform _buttonBaseTransform; [SerializeField, Interface(typeof(IInteractableView))] private IInteractableView InteractableView; private enum QuadStyle { PureColor, Sprite } [SerializeField, Space] private QuadStyle _quadStyle; private bool QuadStyleCheck => _quadStyle == QuadStyle.PureColor; private bool InUILayer; [SerializeField, ShowIf("QuadStyleCheck")] private Renderer _renderer; [SerializeField, ShowIf("QuadStyleCheck")] private Color _normalColor = Color.red; [SerializeField, ShowIf("QuadStyleCheck")] private Color _hoverColor = Color.blue; [SerializeField, ShowIf("QuadStyleCheck")] private Color _selectColor = Color.green; [SerializeField, ShowIf("QuadStyleCheck")] private Color _disabledColor = Color.black; private Image imageSprite; private SpriteRenderer spriteRender; [SerializeField, HideIf("QuadStyleCheck")] private Sprite _normalSprite; [SerializeField, HideIf("QuadStyleCheck")] private Sprite _hoverSprite; [SerializeField, HideIf("QuadStyleCheck")] private Sprite _selectSprite; [SerializeField, HideIf("QuadStyleCheck")] private Sprite _disabledSprite; [Space] public UnityEvent OnHover; public UnityEvent OnSelect; private float _maxOffsetAlongNormal; private Vector2 _planarOffset; private HashSet _fingerPressInteractors; private FingerPressInteractor _postProcessInteractor; private Action _postProcessHandler => UpdateComponentPosition; private Material _material; protected bool _started = false; protected virtual void Start() { this.BeginStart(ref _started); _fingerPressInteractable = transform.parent.GetComponent(); this.AssertField(_fingerPressInteractable, nameof(_fingerPressInteractable)); this.AssertField(_buttonBaseTransform, nameof(_buttonBaseTransform)); InUILayer = transform.GetChild(0).GetComponent() != null; if (InUILayer) { imageSprite = GetComponentInChildren(); imageSprite.enabled = !QuadStyleCheck; } else if(!QuadStyleCheck) { spriteRender = GetComponentInChildren(); spriteRender.enabled = true; } _material = _renderer.material; if(_renderer == null) GetComponent().enabled = QuadStyleCheck; UpdateVisual(); _fingerPressInteractors = new(); _maxOffsetAlongNormal = Vector3.Dot(transform.position - _buttonBaseTransform.position, -1f * _buttonBaseTransform.forward); Vector3 pointOnPlane = transform.position - _maxOffsetAlongNormal * _buttonBaseTransform.forward; _planarOffset = new Vector2( Vector3.Dot(pointOnPlane - _buttonBaseTransform.position, _buttonBaseTransform.right), Vector3.Dot(pointOnPlane - _buttonBaseTransform.position, _buttonBaseTransform.up)); this.EndStart(ref _started); } protected virtual void OnEnable() { if (_started) { _fingerPressInteractors.Clear(); _fingerPressInteractors.UnionWith(_fingerPressInteractable.Interactors); _fingerPressInteractable.WhenInteractorAdded.Action += HandleInteractorAdded; _fingerPressInteractable.WhenInteractorRemoved.Action += HandleInteractorRemoved; _fingerPressInteractable.WhenStateChanged += UpdateVisualState; UpdateVisual(); } } protected virtual void OnDisable() { if (_started) { _fingerPressInteractors.Clear(); _fingerPressInteractable.WhenInteractorAdded.Action -= HandleInteractorAdded; _fingerPressInteractable.WhenInteractorRemoved.Action -= HandleInteractorRemoved; _fingerPressInteractable.WhenStateChanged -= UpdateVisualState; if (_postProcessInteractor) { _postProcessInteractor.WhenPostprocessed -= _postProcessHandler; _postProcessInteractor = null; } } } private void OnDestroy() { Destroy(_material); } private void UpdateVisual() { switch (_fingerPressInteractable.State) { case InteractableState.Normal: if (!QuadStyleCheck) { if(InUILayer) imageSprite.sprite = _normalSprite; else spriteRender.sprite = _normalSprite; } else { _material.color = _normalColor; } break; case InteractableState.Hover: if (!QuadStyleCheck) { if (InUILayer) imageSprite.sprite = _hoverSprite; else spriteRender.sprite = _hoverSprite; } else { _material.color = _hoverColor; } OnHover.Invoke(); break; case InteractableState.Select: if (!QuadStyleCheck) { if (InUILayer) imageSprite.sprite = _selectSprite; else spriteRender.sprite = _selectSprite; } else { _material.color = _selectColor; } OnSelect.Invoke(); break; case InteractableState.Disabled: if (!QuadStyleCheck) { if (InUILayer) imageSprite.sprite = _disabledSprite; else spriteRender.sprite = _disabledSprite; } else { _material.color = _disabledColor; } break; } } private void UpdateVisualState(InteractableStateChangeArgs args) => UpdateVisual(); private void HandleInteractorAdded(FingerPressInteractor fingerPressInteractable) { _fingerPressInteractors.Add(fingerPressInteractable); if (_postProcessInteractor == null) { _postProcessInteractor = fingerPressInteractable; _postProcessInteractor.WhenPostprocessed += _postProcessHandler; } } private void HandleInteractorRemoved(FingerPressInteractor fingerPressInteractor) { _fingerPressInteractors.Remove(fingerPressInteractor); if (fingerPressInteractor == _postProcessInteractor) { _postProcessInteractor.WhenPostprocessed -= _postProcessHandler; using var enumerator = _fingerPressInteractors.GetEnumerator(); if (enumerator.MoveNext() && enumerator.Current != null) { _postProcessInteractor = enumerator.Current; _postProcessInteractor.WhenPostprocessed += _postProcessHandler; } else { _postProcessInteractor = null; UpdateComponentPosition(); } } } private void UpdateComponentPosition() { float closestDistance = _maxOffsetAlongNormal; foreach (FingerPressInteractor fingerPressInteractor in _fingerPressInteractors) { float pokeDistance = Vector3.Dot(fingerPressInteractor.Origin - _buttonBaseTransform.position, -1f * _buttonBaseTransform.forward); pokeDistance -= fingerPressInteractor.Radius; if (pokeDistance < 0f) { pokeDistance = 0f; } closestDistance = Math.Min(pokeDistance, closestDistance); } transform.position = _buttonBaseTransform.position + _buttonBaseTransform.forward * (-1f * closestDistance) + _buttonBaseTransform.right * _planarOffset.x + _buttonBaseTransform.up * _planarOffset.y; } } }