Unity XR射线交互

手柄按键对应的处理事件

前提:每一个需要和手柄射线交互的物体都必须挂载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

### 关于ArcGIS License Server无法启动的解决方案 当遇到ArcGIS License Server无法启动的情况,可以从以下几个方面排查并解决问题: #### 1. **检查网络配置** 确保License Server所在的计算机能够被其他客户端正常访问。如果是在局域网环境中部署了ArcGIS Server Local,则需要确认该环境下的网络设置是否允许远程连接AO组件[^1]。 #### 2. **验证服务状态** 检查ArcGIS Server Object Manager (SOM) 的运行情况。通常情况下,在Host SOM机器上需将此服务更改为由本地系统账户登录,并重启相关服务来恢复其正常工作流程[^2]。 #### 3. **审查日志文件** 查看ArcGIS License Manager的日志记录,寻找任何可能指示错误原因的信息。这些日志可以帮助识别具体是什么阻止了许可服务器的成功初始化。 #### 4. **权限问题** 确认用于启动ArcGIS License Server的服务账号具有足够的权限执行所需操作。这包括但不限于读取/写入特定目录的权利以及与其他必要进程通信的能力。 #### 5. **软件版本兼容性** 保证所使用的ArcGIS产品及其依赖项之间存在良好的版本匹配度。不一致可能会导致意外行为完全失败激活license server的功能。 #### 示例代码片段:修改服务登录身份 以下是更改Windows服务登录凭据的一个简单PowerShell脚本例子: ```powershell $serviceName = "ArcGISServerObjectManager" $newUsername = ".\LocalSystemUser" # 替换为实际用户名 $newPassword = ConvertTo-SecureString "" -AsPlainText -Force Set-Service -Name $serviceName -StartupType Automatic New-ServiceCredential -ServiceName $serviceName -Account $newUsername -Password $newPassword Restart-Service -Name $serviceName ``` 上述脚本仅作为示范用途,请依据实际情况调整参数值后再实施。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值