using DG.Tweening; using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; using VRTK; using VRTK.GrabAttachMechanics; //VR交互控制基类 定义拆装时可抓取和特定位置安装的交互设备 [RequireComponent(typeof(VRTK_InteractableObject))] public abstract class VRGrabInterractableBase : MonoBehaviour, IStepNodeAction { protected VRTK_InteractableObject vRTK_Interactable; public bool defultIsOnRightPos;//默认初始位置和角度是安装目标位置 //protected VRTK_BaseGrabAttach vRTK_BaseGrab; //启用复位逻辑 设置目标位置 public bool checkPos; [PropertyActive("checkPos", true, CompareType.NotEqul)] public Vector3 rightPos; [PropertyActive("checkPos", true, CompareType.NotEqul)] public Vector3 allowPosError;//位置检测允许误差 单位m //启用复位逻辑 设置目标角度 public bool checkAngle; [PropertyActive("checkAngle", true, CompareType.NotEqul)] public Vector3 rightAngle; [PropertyActive("checkAngle", true, CompareType.NotEqul)] public Vector3 allowAngleError; protected bool onFixedPos;//到达特定的位置 public UnityAction atFixedPos;//到达特定的工作位置 复位完成后执行 public UnityAction atFreePos;//放置在了其他位置执行 结束抓取时未满足安置位置时执行 public event UnityAction OnGrabbedEvent;//开始抓取时执行 public event UnityAction UnGrabbedEvent;//结束抓取时 是否满足安装条件 [SerializeField] protected ProcessEnum currentProcess; public Collider thisCollider; protected virtual void Awake() { vRTK_Interactable = this.GetComponent(); vRTK_Interactable.InteractableObjectUngrabbed += Ungrabbed; vRTK_Interactable.InteractableObjectGrabbed += OnGrabbed; vRTK_Interactable.touchHighlightColor = Color.red; vRTK_Interactable.touchHighlightColor.a = 0.3f; if (GroupLearnHelper.isGroup)//分组训练时添加可抓取物体的 同步发送和处理组件 { this.gameObject.AddComponent(); this.gameObject.AddComponent(); } SetGrabInteractable(false);//流程未激活时禁用抓取交互 //vRTK_Interactable. this.GetComponent().precisionGrab = true;//抓取接触位置 不抓取中心 if (thisCollider == null) { thisCollider = this.GetComponentInChildren(); } thisCollider.enabled = false; if (checkPos && defultIsOnRightPos) { SetRightPos(this.transform.localPosition, allowPosError); } if (checkAngle && defultIsOnRightPos) { SetRightAngle(this.transform.localEulerAngles, allowAngleError); } } protected virtual void OnGrabbed(object sender, InteractableObjectEventArgs e) { OnGrabbedEvent?.Invoke(); } protected virtual void Ungrabbed(object sender, InteractableObjectEventArgs e) { bool onAllowableFixed = false;//判断是否启用了复位逻辑 且满足安装位置需要 if (checkPos) { onAllowableFixed = CheckPos(); } if (checkAngle) { if (checkPos && onAllowableFixed)//检查角度时 若先检查了位置 则先判定是否满足位置要求 若位置不满足 则无需检查 直接判定不满足要求 onAllowableFixed = CheckAngle(); } UnGrabbedEvent?.Invoke(onAllowableFixed); Debug.Log(this.gameObject.name + "UnGrabbed"); Ungrabbbed(onAllowableFixed); } /// /// 主动操作物体被放下完成位置角度检测后或场景同步时执行 /// /// 当前位置是否满足安装条件 public void Ungrabbbed(bool onAllowableFixed) { if (onAllowableFixed)//满足安装条件 { StartHoming();//播放归位动画 } else//未处于安装位置 { this.gameObject.GetComponent().isKinematic = false;//安置在其他位置时 启用刚体影响 onFixedPos = false; atFreePos?.Invoke(); atFreePos = null; } } //开始动画复位到准确的安装位置 private void StartHoming() { Debug.Log("开始复位"); this.gameObject.GetComponent().isKinematic = true;//避免重力或其他物体影响其归位 this.transform.DOLocalMove(rightPos, 1f).OnComplete(HomgingOver); this.transform.DOLocalRotateQuaternion(Quaternion.Euler(rightAngle), 1f); } //复位完成 protected virtual void HomgingOver() { atFixedPos?.Invoke(); atFixedPos = null;//只执行一次 onFixedPos = true; } //启用/禁用抓取功能 public void SetGrabInteractable(bool interactable) { vRTK_Interactable.isGrabbable = interactable; } public abstract void Active(ProcessEnum stepName, UnityAction finishedEven); public virtual void UnActive(ProcessEnum stepName) { atFixedPos = null; atFreePos = null; } /// /// 设置与父物体的相对正确位置 不设置不检查 /// /// 目标位置 /// 误差距离 public void SetRightPos(Vector3 targetPos, Vector3 allowError) { checkPos = true; rightPos = targetPos; allowPosError = allowError; } /// /// 设置与父物体的相对正确角度不设置不检查 /// /// 目标角度 /// 允许误差角度 public void SetRightAngle(Vector3 targetAngle, Vector3 allowError) { checkAngle = true; rightAngle = targetAngle; } /// /// 检查位置是否符合要求 /// /// true 符合要求 bool CheckPos() { if (Mathf.Abs(this.transform.localPosition.x - rightPos.x) > allowPosError.x) return false; if (Mathf.Abs(this.transform.localPosition.y - rightPos.y) > allowPosError.y) return false; if (Mathf.Abs(this.transform.localPosition.z - rightPos.z) > allowPosError.z) return false; return true; } /// /// 检查角度是否符合要求 /// /// true 符合要求 bool CheckAngle() { Vector3 currentAngle = NormalizeEuler(this.transform.localEulerAngles); Vector3 targeAngle = NormalizeEuler(rightAngle); if (Mathf.Abs(currentAngle.x - targeAngle.x) > allowAngleError.x || Mathf.Abs(currentAngle.y - targeAngle.y) > allowAngleError.y || Mathf.Abs(currentAngle.z - targeAngle.z) > allowAngleError.z) return false; else return true; } public Vector3 NormalizeEuler(Vector3 rEuler) { if (rEuler.x < -180f) { rEuler.x = rEuler.x + 360f; } else if (rEuler.x > 180f) { rEuler.x = rEuler.x - 360f; } if (rEuler.y < -180f) { rEuler.y = rEuler.y + 360f; } else if (rEuler.y > 180f) { rEuler.y = rEuler.y - 360f; } if (rEuler.z < -180f) { rEuler.z = rEuler.z + 360f; } else if (rEuler.z > 180f) { rEuler.z = rEuler.z - 360f; } return rEuler; } }