手柄按键对应的处理事件
前提:每一个需要和手柄射线交互的物体都必须挂载XR Grab interactable和刚体组件
1.官方提供的事件
1.1 射线悬停在物体上
自动执行
(1)注册事件监听和定义事件委托
public class HandleEventListen : MonoBehaviour
{
// 定义事件委托
public delegate void HoverEventHandler(GameObject obj);
public event HoverEventHandler OnHoverEnter; // 射线进入物体事件
public event HoverEventHandler OnHoverExit; // 射线离开物体事件
public delegate void SelectEventHandler(GameObject obj);
public event SelectEventHandler OnSelectExit;
// 物体的交互组件,MyXRGrabInteractable继承自XRGrabInteractable,根据需要重写函数
private MyXRGrabInteractable _interactor;
public static NearFarInteractor _rayInteractorRight; // 右手的射线交互器,只用右手手柄射线
private void Start()
{
_interactor = GetComponent<MyXRGrabInteractable>();//获得物体交互组件
//获得手柄交互组件
GameObject rightController = GameObject.Find("Right Controller");
if (rightController != null)
{
Transform InteractorTrans = rightController.transform.Find("Near-Far Interactor");
if (InteractorTrans != null)
{
_rayInteractorRight = InteractorTrans.GetComponent<NearFarInteractor>();
}
}
if (_rayInteractorRight == null) Debug.LogWarning("没有找到右手手柄射线交互组件");
if (_interactor == null)
{
Debug.LogWarning("零件缺少MyXRGrabInteractable组件");
}
if (_interactor != null)
{
// 注册事件监听
_interactor.hoverEntered.AddListener(HandleHoverEnter); // 监听射线进入物体
_interactor.hoverExited.AddListener(HandleHoverExit); // 监听射线离开物体
Debug.LogWarning("已经添加监听");
}
_equipmentOperator = FindAnyObjectByType<EquipmentOperator>();
}
private void OnDisable()
{
_interactor.hoverEntered.RemoveListener(HandleHoverEnter);
_interactor.hoverExited.RemoveListener(HandleHoverExit);
_interactor.selectExited.RemoveListener(HandleSelectExit);
}
// 处理射线进入物体时的逻辑
private void HandleHoverEnter(HoverEnterEventArgs args)
{
if(args.interactorObject == _rayInteractorRight)
{
OnHoverEnter?.Invoke(part);//OnHoverEnter事件触发,执行委托函数
}
}
// 处理射线离开物体时的逻辑
private void HandleHoverExit(HoverExitEventArgs args)
{
if (args.interactorObject == _rayInteractorRight)
{
OnHoverExit?.Invoke(args.interactableObject.transform.gameObject);
}
}
}
(2)添加事件委托,一旦事件触发,会执行自定义的委托函数
private void InitializeMachineStructs()
{
HandleEventListen handle = FindAnyObjectByType<HandleEventListen>();
handle.OnHoverEnter += PartOperate;
}
//因为HoverEventHandler传入的参数是GameObject,传出void,PartOperate需要一致
private void PartOperate(GameObject clickedPart)
{
//自定义函数
}
1.2 按下手柄的grab键
自动执行XRGrabInteractable(继承自XRBaseInteractable)的
OnSelectEntering(SelectEnterEventArgs args)->
OnSelectExiting(SelectExitEventArgs args) ->
OnSelectExited(SelectExitEventArgs args)
(1)注册事件监听和事件委托
//定义事件委托
public delegate void SelectEventHandler(GameObject obj);
public event SelectEventHandler OnSelectExit;
//注册事件监听(松开Grab按键)
_interactor.selectExited.AddListener(HandleSelectExit);
//松开Grab按键 事件函数定义
private void HandleSelectExit(SelectExitEventArgs arg)
{
if(arg != null)
{
OnSelectExit?.Invoke(arg.interactableObject.transform.gameObject);
}
}
(2)添加事件委托
和上面一样
(3)重写XRGrabInteractable对应的射线悬停函数
适用于:需要自定义不同的操作时,比如物体需要满足某要求之后,才能被抓取。并且如果在重写的OnSelectEntering函数中,有不执行父对象虚函数直接返回的操作,那么需要重写OnSelectExiting和OnSelectExited,否则会报错。因为就算不执行父对象的OnSelectEntering,后续还是会进入父对象的OnSelectExiting和OnSelectExited,所以需要加筛选判断。
public class MyXRGrabInteractable : XRGrabInteractable
{
private EquipmentOperator _equipmentOperator;
//记录是否被Select
public static bool isSelected = false;
private void Start()
{
_equipmentOperator = FindAnyObjectByType<EquipmentOperator>();
}
protected override void OnSelectEntering(SelectEnterEventArgs args)
{
GameObject part = gameObject;
if(_equipmentOperator != null)
{
bool isReady = _equipmentOperator.partIsReady(part);
//满足isReady条件才可以被Select
if (isReady == false)
{
isSelected = false;
return;
}
isSelected = true;
base.OnSelectEntering(args);
}
}
//只有被Select的物体,才可以执行SelectExit
protected override void OnSelectExiting(SelectExitEventArgs args)
{
if (isSelected == true)
{
base.OnSelectExiting(args);
}
}
protected override void OnSelectExited(SelectExitEventArgs args)
{
if (isSelected == true)
{
base.OnSelectExited(args);
}
}
}
1.3 在按下手柄Grab键的前提下,按Trigger键
会执行Activate函数,具体函数实现可以在XRBaseInteractable库中找
2.自定义手柄按钮事件
(1)修改XRI Default Input Action 设置(新增Trigger InputAction)
(2)注册事件监听代码
public class HandleController : MonoBehaviour
{
public InputActionReference trigger_Action;
private NearFarInteractor _rayInteractorRight;
public delegate void TriggerEventHandler(GameObject obj);
public event TriggerEventHandler OnTriggered;
private static bool _firstTriggle = false;
private void Start()
{
_rayInteractorRight = HandleEventListen._rayInteractorRight;
}
//注册事件监听
private void OnEnable()
{
//利用设置的引用类型获得InputAction
InputAction triggerAction = _trigger_ActionRef.action;
if (triggerAction != null)
{
triggerAction.started += HandleTrigger;
}
}
//回调函数
private void HandleTrigger(InputAction.CallbackContext content)
{
//按下tigger时,如果当前射线进入可交互的物体才执行后续操作
var h = _rayInteractorRight.interactablesHovered;
if (h.Count != 0)
{
//trigger按下和松开,都会执行HandleTrigger,通过增加判断,只执行一次
_firstTriggle = !_firstTriggle;
if (_firstTriggle != true)
{
return;
}
Debug.LogWarning("按下扳机");
//触发事件,其他类型监听此事件,并执行自定义回调函数
OnTriggered?.Invoke(HandleEventListen.currentPart);
}
}
}
(3)将脚本挂载到物体上之后,拖入新增Trigger_Action