Unity Udexreal开发插件包
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

257 lines
9.3 KiB

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<FingerPressInteractor> _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<FingerPressInteractable>();
this.AssertField(_fingerPressInteractable, nameof(_fingerPressInteractable));
this.AssertField(_buttonBaseTransform, nameof(_buttonBaseTransform));
InUILayer = transform.GetChild(0).GetComponent<RectTransform>() != null;
if (InUILayer)
{
imageSprite = GetComponentInChildren<Image>();
imageSprite.enabled = !QuadStyleCheck;
}
else if(!QuadStyleCheck)
{
spriteRender = GetComponentInChildren<SpriteRenderer>();
spriteRender.enabled = true;
}
_material = _renderer.material;
if(_renderer == null)
GetComponent<MeshRenderer>().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;
}
}
}