Unity XR实现交互(抓取,移动旋转,传送,射击)-Pico

Unity XR实现交互(抓取,移动旋转,传送,射击)

前言

Unity XR提供了XR Interaction Toolkit交互工具包直接在Package Manager中安装/更新即可,对于简单的交互需求可以实现0代码,只需要将相应的脚本挂载到手柄和物体身上即可。本次演示使用当前最新的2.1.0 preview版本,如果版本较低可能会有部分脚本不可用,请尽可能更新到最新的版本。

本次演示使用Pico XR SDK,在导入SDK后其会自行携带一个XR Interaction Toolkit的0.9版本,请一定从Package Manager中进行更新,否则后续的选项会有较大的区别。

PS: 更新到新版后,很多脚本都提供了新版Action-based方式,和老版Device-based,本篇博客会以Device-based进行讲解,关于Unity新版输入系统的使用,笔者会在下一篇文章着重去讲。

一、手柄的创建

在这里插入图片描述

  • Ray Interactor:会创建一个带有射线的手柄模板对象
  • Direct Interactor:会创建一个不具有射线的手柄模板对象
  • XR Origin(VR): 建议,会直接创建出VR摄像机+两个射线手柄对象,更为方便后续可直接在此基础上做更改。

二、手柄的抓取

1. XR DirectController 手柄直接抓取

在这里插入图片描述

  • Interaction Manager: 交互管理类 — 关键,一般会随着XR Origin一起创建出来,一般创建手柄会直接挂载上去,如果没有挂载需要去Hireachy面板手动挂载一下。
  • Interaction Layer Mask : 在可抓取物体身上的XR Grab Interactable脚本上也有同名属性,只有物体的Layer Mask和手柄的匹配才可被抓取。
  • Attack Transform : 手柄的抓取位置,同时物体也可以设置抓取位置,当抓取时手柄的抓取位置会和物体的抓取位置相同,根据具体需求加以配置合适的位置即可。
  • 碰撞体:如果不是使用模板配置,而是自行挂载脚本很容易忘记此组件,直接抓取必须具有碰撞体才可以检测到可抓取物体并进行抓取操作,而射线则无需碰撞体,只需射线检测。

2. XR Ray Interactor 手柄射线抓取

手柄射线抓取一般使用默认设置即可实现将击中的物体吸到手柄上,无需添加碰撞体,诸如Layer Mask等属性与上述一致,不再赘述。

3. XR Grab Interactable 可抓取物体

在这里插入图片描述

  • Interaction Manager/LayerMask/Attack Transform,几个常用属性在XR DirectController中描述过,不再赘述
  • Collider:必须拥有,抓取是通过碰撞器检测所实现的
  • Rigibody:建议拥有,VR场景下的可抓取物体的抓取投掷等效果均基于自带的刚体。
  • Interactable Events:里面提供了抓取物体一系列事件的回调,如抓取物体的回调,释放物体的回调,抓取物体后按下Trigger键的回调等等,后续的射击功能正是基于此实现的。

三、手柄摇杆控制移动和旋转

以左手摇杆控制持续旋转,右手摇杆控制持续移动为例

在这里插入图片描述

根据上图,在XR Origin上添加Locomotion System,Continous Turn Provider等组件,再按照上图箭头进行配置即可。

  • 注意这些脚本不是必须放在XR Origin身上,任意地方均可但属性必须进行配置,放在XR Origin上方便管理。
  • Continous 是持续进行移动和旋转的脚本,由手柄的摇杆控制,如果想一次旋转或移动一段距离/角度,可以用Snap Turn/Move,在低版本的XR互动包中可能会不支持某些脚本。

四、手柄射线传送

1. 基本功能版

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 基本的传送管理类Teleportion Provider必须要添加,推荐一起添加在XR Origin中

  • 负责传送的手柄,首先必须是XR Ray Interactor射线控制,一般来讲会使用抛物线,或者贝塞尔曲线,关于其相关参数可以搜索贝塞尔曲线的知识,Sample Frequency取样点会直接影响曲线的平滑程度。

  • Reticle为射线击中目标后的指示器,很适合作为传送目标点的指示器,如果没有合适的指示器可以用球体简单演示。

在这里插入图片描述

除了射线手柄的设置,在需要传送的地面上还需要添加Teleportation Area脚本(如果想只能传送到固定的点可以用Anchor,用法一致),然后射线检测到可以传送的地面后射线会变为绿色,默认按下Trigger键即可实现传送。

2. 进阶版

待改进的问题

  1. 只挂载默认的脚本,有时很大程度上没法满足需求,例如一般手柄的传送功能不会一直存在曲线去选择位置,我们会希望其在按下某个按键或者推动摇杆时出现曲线旋转位置,在松开按键或摇杆时进行传送等。

  2. 负责传送的手柄无法同时挂载Ray Interactor和Direct Interactor也就是说,我们无法通过仅仅挂载脚本去实现手柄平常负责直接抓取,而想传送时得到射线选取位置传送。

针对问题1,2的场景,笔者以左手柄平常负责直接抓取,按下A键出现射线选择位置传送为例,给出一种解决方案。

基本思路

  1. 创建两个左手柄对象,一个挂载Direct Interactor负责日常抓取,一个挂载Ray Interactor负责传送。
  2. 平常Ray Interactor脚本会被禁用掉,只有当按下A键后启用可以选择位置,当松开A键后代码控制发送传送请求,再禁用掉脚本指示器等等。

关于手柄按键的按下和抬起 Down和Up事件,如果使用老的输入系统可以参考这篇文章https://blog.csdn.net/Q540670228/article/details/123139150,关于脚本的启动禁用过程较为简单,下面只给出抬起按键时发起传送请求的代码。

    //传送管理器脚本的获取省略
	public TeleportationProvider provider;
	//负责传送的手柄XRRayInteractor脚本,获取过程省略
	public XRRayInteractor teleportRayInteractor;
	private void LeftAXButtonUpHandler()
    {
    
    
        //射线信息
        RaycastHit rayInfo;
        //判断目标点是否有效 1.是否击中目标 2.是否具有Teleportation组件
        bool isValid = teleportRayInteractor.TryGetCurrent3DRaycastHit(out rayInfo);
        isValid = isValid && rayInfo.collider != null &&
                  (rayInfo.collider.GetComponent<TeleportationArea>() != null ||
                   rayInfo.collider.GetComponent<TeleportationAnchor>() != null);
        if (isValid)
        {
    
    
            //创建传送需求将射线击中的位置作为目的地,提交到provider中的传送队列。
            TeleportRequest request = new TeleportRequest();
            request.destinationPosition = rayInfo.point;
            provider.QueueTeleportRequest(request);
        }
        
        //禁用相关脚本和指示器
        teleportRayInteractor.enabled = false;
        teleportRayInteractor.GetComponent<XRInteractorLineVisual>().reticle?.SetActive(false);
    }

五、手柄控制射击

在VR游戏中经常会有捡起枪进行射击的功能,其一般设计难点在

  1. 判断物体是否被抓取,被哪个手柄抓取
  2. 抓取了此物体的手柄是否按下了Trigger键

在XR Grab Interactable脚本中已经很贴心的提供了一个事件,触发条件正是上述这两个。使用方式如下

private XRGrabInteractable grab;
private void Awake()
{
    grab = GetComponent<XRGrabInteractable>();
    //为activated事件添加监听函数即可
    grab.activated.AddListener(GrabHandler);
}

private void GrabHandler(ActivateEventArgs a)
{
	//当枪械被抓取和按下Trigger后回调此方法,触发射击方法。
    Shoot();
}

猜你喜欢

转载自blog.csdn.net/Q540670228/article/details/125563780