unityzgy/Assets/Scripts/ThisProjectTool/VRCtrl/RepairSub/StepNode/StepNodeAction/VRGrabInterractableBase.cs
ayuan9957 bf12e02276 feat: 多语言本地化系统 - 支持中/英/法/俄实时切换
- LocalizationManager: 新增翻译表(SetFormatted/SetDuration/LocalizeOperateMsg)
- LoginPanel: InputField placeholder本地化、字体颜色保持
- HistoryPanel: 用时数据本地化、placeholder本地化
- RecordDetailPanel: 操作详情消息本地化(LanguageChanged重建)
- AppraiseWindowBase: 评价等级本地化、操作消息重建
- EditorAppraiser: 所有评估消息改用InjectOperateMsgLocalized
- StudentOperateRecorder: 新增InjectOperateMsgLocalized方法
- LocalizationLanguageTestBar: 单例模式、ScreenSpaceOverlay筛选
- 字体切换时保留颜色和verticalOverflow
2026-07-16 10:05:59 +08:00

220 lines
7.5 KiB
C#

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<bool> UnGrabbedEvent;//结束抓取时 是否满足安装条件
[SerializeField] protected ProcessEnum currentProcess;
public Collider thisCollider;
protected virtual void Awake()
{
vRTK_Interactable = this.GetComponent<VRTK_InteractableObject>();
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<VRGrabedSyncSender>();
this.gameObject.AddComponent<VRGrabedSyncHandler>();
}
SetGrabInteractable(false);//流程未激活时禁用抓取交互
//vRTK_Interactable.
this.GetComponent<VRTK_BaseGrabAttach>().precisionGrab = true;//抓取接触位置 不抓取中心
if (thisCollider == null)
{
thisCollider = this.GetComponentInChildren<Collider>();
}
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);
}
/// <summary>
/// 主动操作物体被放下完成位置角度检测后或场景同步时执行
/// </summary>
/// <param name="onAllowableFixed">当前位置是否满足安装条件</param>
public void Ungrabbbed(bool onAllowableFixed)
{
if (onAllowableFixed)//满足安装条件
{
StartHoming();//播放归位动画
}
else//未处于安装位置
{
this.gameObject.GetComponent<Rigidbody>().isKinematic = false;//安置在其他位置时 启用刚体影响
onFixedPos = false;
atFreePos?.Invoke();
atFreePos = null;
}
}
//开始动画复位到准确的安装位置
private void StartHoming()
{
Debug.Log("开始复位");
this.gameObject.GetComponent<Rigidbody>().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;
}
/// <summary>
/// 设置与父物体的相对正确位置 不设置不检查
/// </summary>
/// <param name="targetPos">目标位置</param>
/// <param name="allowError">误差距离</param>
public void SetRightPos(Vector3 targetPos, Vector3 allowError)
{
checkPos = true;
rightPos = targetPos;
allowPosError = allowError;
}
/// <summary>
/// 设置与父物体的相对正确角度不设置不检查
/// </summary>
/// <param name="targetAngle">目标角度</param>
/// <param name="allowError">允许误差角度</param>
public void SetRightAngle(Vector3 targetAngle, Vector3 allowError)
{
checkAngle = true;
rightAngle = targetAngle;
}
/// <summary>
/// 检查位置是否符合要求
/// </summary>
/// <returns> true 符合要求</returns>
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;
}
/// <summary>
/// 检查角度是否符合要求
/// </summary>
/// <returns>true 符合要求</returns>
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;
}
}