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事件")]
///
/// 按钮前置触发区的厚度(单位:m),用户手指进入这个区域将会被认为将要与此Button进行交互,触发OnPointerPotentialInteractChanged事件
///
public float preTriggerZoneThickness = 0.15f;
[Tooltip("按钮后置触发区的厚度(单位:m),决定了用户手指按过(穿过)按钮后还保持按下状态的距离,当手指离开这个后置触发区的时候按钮将会弹起")]
///
/// 按钮后置触发区的厚度(单位:m),决定了用户手指按过(穿过)按钮后还保持按下状态的距离,当手指离开这个后置触发区的时候按钮将会弹起
///
public float rearTriggerZoneThickness = 0.05f;
///
/// 按钮被按下
///
public bool pressed;
///
/// 0是未按下,1是按下但是没按到底,2是按下且按到底
///
public byte state;
///
/// 按键高度(单位:m,必须大于heightMin),从这个高度开始响应按键事件
///
public float heightMax;
///
/// 按键按到底的时候的厚度
///
public float heightMin = 0.001f;
///
/// 当前是哪个指尖点点击到了按键,用于计算按下的z距离
///
Transform other;
///
/// 当前被点击的Button
///
SpatialButton curButton;
///
/// 指尖点当前在按键坐标系下的坐标
///
Vector3 curPos;
///
/// 当前指尖点距离按键底面的z距离
///
public float length;
public float clampLength;
float lastLength;
///
/// 是否开始点击,只要有指尖点进入触发区就开始点击判断流程
///
bool start = false;
//bool rayStart = false;
/////
///// 用于播放按键按下和弹起的音效
/////
//AudioSource audioSource;
///
/// 0是按键开始按下的音效,1是按键按下的音效,2是按键弹起的音效
///
static AudioClip[] clips;
InputInfoBase handInfo;
///
/// Button是否响应虚拟键盘的回车键
///
[SerializeField]
private bool useEnterKey;
#region Event
public enum SpatialButtonEventType
{
OnPointerHoverEnter,
OnPointerStartPress,
OnPointerPressing,
OnPointerDown,
OnPointerUp,
OnPointerEndPress,
OnPointerClicked,
OnPointerHoverExit,
}
private static Dictionary All = new Dictionary();
[Serializable]
public class SpatialButtonEvent : UnityEvent
{ }
[Serializable]
public class Entry
{
public SpatialButtonEventType eventID = SpatialButtonEventType.OnPointerHoverEnter;
public SpatialButtonEvent callback = new SpatialButtonEvent();
}
[FormerlySerializedAs("delegates")]
[SerializeField]
private List m_Delegates;
public List triggers
{
get
{
if (m_Delegates == null)
m_Delegates = new List();
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(), 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();
heightMax = size.z > heightMin ? size.z : heightMin;
//material = _visual.GetChild(0).GetComponent().material;
//audioSource = GetComponent();
clips = new AudioClip[3];
clips[0] = ResourcesManager.Load("Sounds/ButtonStartPress");
clips[1] = ResourcesManager.Load("Sounds/ButtonDown");
clips[2] = ResourcesManager.Load("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();
}
}
}
///
/// handInfo如果为null表示没有手在与此Button交互
///
///
public override void OnRayCastHit(InputInfoBase handInfo)
{
if (this.handInfo != handInfo)
{
if (handInfo == null)
{
OnPointerHoverExit();
}
else
{
OnPointerHoverEnter(Spatial.InteractiveMode.Raycast);
}
this.handInfo = handInfo;
}
}
///
/// 按钮按到底的时候调用,播放按键按下的音效,并更新此按键的pressed状态为true
///
/// 是否是射线交互
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();
}
}
///
/// 按钮被按住的时候调用
///
void ButtonPressing()
{
if (state == 0)
{
////播放按键开始按下的音效
//AudioSource.PlayClipAtPoint(clips[0], Vector3.zero);
OnPointerStartPress();
}
state = 1;
OnPointerPressing();
}
///
/// 按键完全弹起的时候调用,播放按键弹起的音效,并更新此按键的pressed状态为false
///
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
///
public override void OnPointerHoverEnter(Spatial.InteractiveMode interactiveMode = Spatial.InteractiveMode.None)
{
base.OnPointerHoverEnter(interactiveMode);
Execute(SpatialButtonEventType.OnPointerHoverEnter);
}
///
public override void OnPointerStartPress()
{
base.OnPointerStartPress();
Execute(SpatialButtonEventType.OnPointerStartPress);
}
///
public override void OnPointerPressing()
{
base.OnPointerPressing();
Execute(SpatialButtonEventType.OnPointerPressing);
}
///
public override void OnPointerDown()
{
base.OnPointerDown();
Execute(SpatialButtonEventType.OnPointerDown);
}
///
public override void OnPointerUp()
{
base.OnPointerUp();
Execute(SpatialButtonEventType.OnPointerUp);
}
///
public override void OnPointerEndPress()
{
base.OnPointerEndPress();
Execute(SpatialButtonEventType.OnPointerEndPress);
}
///
public override void OnPointerClicked()
{
base.OnPointerClicked();
Execute(SpatialButtonEventType.OnPointerClicked);
}
///
public override void OnPointerHoverExit()
{
base.OnPointerHoverExit();
Execute(SpatialButtonEventType.OnPointerHoverExit);
}
#endregion
}
}