123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479 |
- using System;
- using System.CodeDom;
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- using UnityEngine.Events;
- using UnityEngine.UI;
- using EZXR.Glass.Inputs;
- using UnityEditor;
- using EZXR.Glass.Core;
- using UnityEngine.Serialization;
- using EZXR.Glass.Inputs;
- namespace EZXR.Glass.UI
- {
- [ExecuteInEditMode]
- public class SpatialButton : SpatialSelectable
- {
- #region InternalUse
- public BoxCollider _collider;
- #endregion
- [Tooltip("按钮前置触发区的厚度(单位:m),用户手指进入这个区域将会被认为将要与此Button进行交互,触发OnPointerPotentialInteractChanged事件")]
- /// <summary>
- /// 按钮前置触发区的厚度(单位:m),用户手指进入这个区域将会被认为将要与此Button进行交互,触发OnPointerPotentialInteractChanged事件
- /// </summary>
- public float preTriggerZoneThickness = 0.15f;
- [Tooltip("按钮后置触发区的厚度(单位:m),决定了用户手指按过(穿过)按钮后还保持按下状态的距离,当手指离开这个后置触发区的时候按钮将会弹起")]
- /// <summary>
- /// 按钮后置触发区的厚度(单位:m),决定了用户手指按过(穿过)按钮后还保持按下状态的距离,当手指离开这个后置触发区的时候按钮将会弹起
- /// </summary>
- public float rearTriggerZoneThickness = 0.05f;
- /// <summary>
- /// 按钮被按下
- /// </summary>
- public bool pressed;
- /// <summary>
- /// 0是未按下,1是按下但是没按到底,2是按下且按到底
- /// </summary>
- public byte state;
- /// <summary>
- /// 按键高度(单位:m,必须大于heightMin),从这个高度开始响应按键事件
- /// </summary>
- public float heightMax;
- /// <summary>
- /// 按键按到底的时候的厚度
- /// </summary>
- public float heightMin = 0.001f;
- /// <summary>
- /// 当前是哪个指尖点点击到了按键,用于计算按下的z距离
- /// </summary>
- Transform other;
- /// <summary>
- /// 当前被点击的Button
- /// </summary>
- SpatialButton curButton;
- /// <summary>
- /// 指尖点当前在按键坐标系下的坐标
- /// </summary>
- Vector3 curPos;
- /// <summary>
- /// 当前指尖点距离按键底面的z距离
- /// </summary>
- public float length;
- public float clampLength;
- float lastLength;
- /// <summary>
- /// 是否开始点击,只要有指尖点进入触发区就开始点击判断流程
- /// </summary>
- bool start = false;
- //bool rayStart = false;
- ///// <summary>
- ///// 用于播放按键按下和弹起的音效
- ///// </summary>
- //AudioSource audioSource;
- /// <summary>
- /// 0是按键开始按下的音效,1是按键按下的音效,2是按键弹起的音效
- /// </summary>
- static AudioClip[] clips;
- InputInfoBase handInfo;
- /// <summary>
- /// Button是否响应虚拟键盘的回车键
- /// </summary>
- [SerializeField]
- private bool useEnterKey;
- #region Event
- public enum SpatialButtonEventType
- {
- OnPointerHoverEnter,
- OnPointerStartPress,
- OnPointerPressing,
- OnPointerDown,
- OnPointerUp,
- OnPointerEndPress,
- OnPointerClicked,
- OnPointerHoverExit,
- }
- private static Dictionary<Collider, SpatialObject> All = new Dictionary<Collider, SpatialObject>();
- [Serializable]
- public class SpatialButtonEvent : UnityEvent
- { }
- [Serializable]
- public class Entry
- {
- public SpatialButtonEventType eventID = SpatialButtonEventType.OnPointerHoverEnter;
- public SpatialButtonEvent callback = new SpatialButtonEvent();
- }
- [FormerlySerializedAs("delegates")]
- [SerializeField]
- private List<Entry> m_Delegates;
- public List<Entry> triggers
- {
- get
- {
- if (m_Delegates == null)
- m_Delegates = new List<Entry>();
- return m_Delegates;
- }
- set { m_Delegates = value; }
- }
- private void Execute(SpatialButtonEventType id)
- {
- var triggerCount = triggers.Count;
- for (int i = 0, imax = triggers.Count; i < imax; ++i)
- {
- var ent = triggers[i];
- if (ent.eventID == id && ent.callback != null)
- ent.callback.Invoke();
- }
- }
- public void AddEvent(SpatialButtonEventType spatialButtonEventType, UnityAction unityAction)
- {
- var triggerCount = triggers.Count;
- for (int i = 0, imax = triggers.Count; i < imax; ++i)
- {
- var ent = triggers[i];
- if (ent.eventID == spatialButtonEventType && ent.callback != null)
- {
- ent.callback.AddListener(unityAction);
- return;
- }
- }
- Entry entry = new Entry();
- entry.eventID = spatialButtonEventType;
- entry.callback.AddListener(unityAction);
- triggers.Add(entry);
- }
- #endregion
- protected override void Awake()
- {
- base.Awake();
- //将自身注册到ARUIEventSystem中,以在被射线碰到的时候被回调
- SpatialUIEventSystem.RegisterCallBack(GetComponent<Collider>(), this);
- }
- protected override void OnEnable()
- {
- base.OnEnable();
- if (useEnterKey)
- {
- KeyBoard.OnEnterClicked += OnEnterClicked;
- }
- }
- protected override void OnDisable()
- {
- base.OnDisable();
- if (useEnterKey)
- {
- KeyBoard.OnEnterClicked -= OnEnterClicked;
- }
- }
- private void OnEnterClicked()
- {
- if (isActiveAndEnabled)
- {
- ButtonClick();
- }
- }
- // Start is called before the first frame update
- protected override void Start()
- {
- base.Start();
- if (Application.isPlaying)
- {
- //_collider = GetComponent<BoxCollider>();
- heightMax = size.z > heightMin ? size.z : heightMin;
- //material = _visual.GetChild(0).GetComponent<Renderer>().material;
- //audioSource = GetComponent<AudioSource>();
- clips = new AudioClip[3];
- clips[0] = ResourcesManager.Load<AudioClip>("Sounds/ButtonStartPress");
- clips[1] = ResourcesManager.Load<AudioClip>("Sounds/ButtonDown");
- clips[2] = ResourcesManager.Load<AudioClip>("Sounds/ButtonUp");
- }
- }
- // Update is called once per frame
- protected override void Update()
- {
- base.Update();
- if (Application.isPlaying)
- {
- //射线交互
- if (handInfo != null)
- {
- //如果当前射线射到的物体不再是此按钮
- if (handInfo.CurRayContactingTarget == null || handInfo.CurRayContactingTarget.gameObject != gameObject)
- {
- handInfo = null;
- }
- else
- {
- if (handInfo.isPinching)
- {
- ButtonDown(true);
- }
- else
- {
- ButtonUp(true);
- }
- }
- }
- else//近距离
- {
- if (start)//直接近距离按压
- {
- if (other == null || !other.gameObject.activeInHierarchy)//手丢失了的话直接ButtonUp
- {
- ButtonUp(false);
- start = false;
- curPos = new Vector3(9999, 9999, 9999);
- return;
- }
- else//手正常存在的话实时算出指尖在Button局部坐标系下的位置
- {
- //先算出指尖点当前在按键坐标系下的坐标
- curPos = transform.InverseTransformPoint(other.position);
- }
- //然后算出当前指尖点距离按键底面的z距离,这个0.005是指尖点的半径长度
- length = curPos.z - 0.005f;
- clampLength = Mathf.Clamp(length, heightMin, heightMax);
- //更新按钮状态
- if (clampLength == heightMax)//按钮已经弹起来
- {
- ButtonUp(false);
- }
- else if (clampLength == heightMin)//按钮按到底
- {
- ButtonDown(false);
- }
- else if (clampLength < heightMax)//按钮按下但没到底
- {
- ButtonPressing();
- }
- lastLength = length;
- }
- }
- }
- else
- {
- #if UNITY_EDITOR
- //_mesh.localScale = size;
- _collider.size = new Vector3(size.x, size.y, /*size.z + */preTriggerZoneThickness + rearTriggerZoneThickness);
- _collider.center = new Vector3(0, 0, preTriggerZoneThickness - _collider.size.z / 2.0f);
- #endif
- }
- }
- private void OnTriggerStay(Collider other)
- {
- if (Application.isPlaying)
- {
- if (!start)
- {
- //对方必须具备rigidbody才会触发ARButton的Tigger
- if (other.name.Contains("Index_4"))
- {
- if (_collider.bounds.Contains(other.transform.position) && (transform.InverseTransformPoint(other.transform.position).z - 0.005f > heightMin /*heightMax*/))//此处用于确保开始触发按钮事件一定是从Hover开始,避免从按钮底部向上移动指尖造成的异常
- {
- curButton = this;
- this.other = other.transform;
- start = true;
- OnPointerHoverEnter(Spatial.InteractiveMode.Touch);
- }
- }
- }
- }
- }
- private void OnTriggerExit(Collider other)
- {
- if (Application.isPlaying)
- {
- if (other.name.Contains("Index_4"))
- {
- //只有按钮正在被按的状态下,指尖离开触发区才会触发ButtonUp
- if (state != 0)
- {
- ButtonUp(false);
- }
- start = false;
- curButton = null;
- this.other = null;
- OnPointerHoverExit();
- }
- }
- }
- /// <summary>
- /// handInfo如果为null表示没有手在与此Button交互
- /// </summary>
- /// <param name="handInfo"></param>
- public override void OnRayCastHit(InputInfoBase handInfo)
- {
- if (this.handInfo != handInfo)
- {
- if (handInfo == null)
- {
- OnPointerHoverExit();
- }
- else
- {
- OnPointerHoverEnter(Spatial.InteractiveMode.Raycast);
- }
- this.handInfo = handInfo;
- }
- }
- /// <summary>
- /// 按钮按到底的时候调用,播放按键按下的音效,并更新此按键的pressed状态为true
- /// </summary>
- /// <param name="isRay">是否是射线交互</param>
- void ButtonDown(bool isRay)
- {
- if (state != 2)
- {
- if (isRay)
- {
- OnPointerStartPress();
- }
- }
- //按到底也属于Pressing的一种情况,所以应该调用OnPointerPressing
- OnPointerPressing();
- if (state != 2)
- {
- state = 2;
- pressed = true;
- //播放按键按下的音效
- AudioSource.PlayClipAtPoint(clips[/*isRay ? 0 : 1*/0], Vector3.zero);
- OnPointerDown();
- }
- }
- /// <summary>
- /// 按钮被按住的时候调用
- /// </summary>
- void ButtonPressing()
- {
- if (state == 0)
- {
- ////播放按键开始按下的音效
- //AudioSource.PlayClipAtPoint(clips[0], Vector3.zero);
- OnPointerStartPress();
- }
- state = 1;
- OnPointerPressing();
- }
- /// <summary>
- /// 按键完全弹起的时候调用,播放按键弹起的音效,并更新此按键的pressed状态为false
- /// </summary>
- void ButtonUp(bool isRay)
- {
- if (state != 0)
- {
- if (isRay)
- {
- //播放按键弹起的音效
- AudioSource.PlayClipAtPoint(clips[2], Vector3.zero);
- }
- OnPointerUp();
- OnPointerEndPress();
- }
- if (pressed)//按到底然后弹起来才算是一次点击
- {
- ButtonClick();
- }
- state = 0;
- pressed = false;
- }
- void ButtonClick()
- {
- OnPointerClicked();
- }
- #region Pointer Event
- /// <inheritdoc/>
- public override void OnPointerHoverEnter(Spatial.InteractiveMode interactiveMode = Spatial.InteractiveMode.None)
- {
- base.OnPointerHoverEnter(interactiveMode);
- Execute(SpatialButtonEventType.OnPointerHoverEnter);
- }
- /// <inheritdoc/>
- public override void OnPointerStartPress()
- {
- base.OnPointerStartPress();
- Execute(SpatialButtonEventType.OnPointerStartPress);
- }
- /// <inheritdoc/>
- public override void OnPointerPressing()
- {
- base.OnPointerPressing();
- Execute(SpatialButtonEventType.OnPointerPressing);
- }
- /// <inheritdoc/>
- public override void OnPointerDown()
- {
- base.OnPointerDown();
- Execute(SpatialButtonEventType.OnPointerDown);
- }
- /// <inheritdoc/>
- public override void OnPointerUp()
- {
- base.OnPointerUp();
- Execute(SpatialButtonEventType.OnPointerUp);
- }
- /// <inheritdoc/>
- public override void OnPointerEndPress()
- {
- base.OnPointerEndPress();
- Execute(SpatialButtonEventType.OnPointerEndPress);
- }
- /// <inheritdoc/>
- public override void OnPointerClicked()
- {
- base.OnPointerClicked();
- Execute(SpatialButtonEventType.OnPointerClicked);
- }
- /// <inheritdoc/>
- public override void OnPointerHoverExit()
- {
- base.OnPointerHoverExit();
- Execute(SpatialButtonEventType.OnPointerHoverExit);
- }
- #endregion
- }
- }
|