SteamVR

SteamVR 插件交互解析

1动作和动作集

动作集->动作:SteamVR2.x的核心

SteamVR动作类型:

  • Boolearn:布尔型,通常用于按钮动作
  • Sigle:类似浮点,返回0~1浮点数,常用于获取Trigger键的键程值。
  • vector2:二维,如获取Trackpad上手指接触点坐标
  • vector2:三维,入获取手柄在空间中的位置
  • Pose:用于获取手柄的运动数据,包括位置和旋转信息
  • Skeleton:提供用于呈现首部模型的谷歌数据,每个关节点的位置和旋转

了解按键的绑定

 使用Action(动作)的优势:

  • 更方便进行多硬件平台的适配,实现跨平台
  • 针对单一平台,能减少需求变更时的代码修改工作

脚本获取动作输入

获取动作的两种方式:

1声明变量形式引用

例:public SteanVR_Action_Boolean Fire;

2直接获取 

例:SteamVR_Actions.default_Fire

监听动作的三种方式:
  • 可以同时注册监听:

SteamVR_Action.default_Fire.AddOnStateUpListener(FireActionStateUpHandler,SteamVR_Input_Sourse.Any);

  • 也可以通过C#的事件监听:
SteamVR_Action.default_Fire.OnstateUp+=OnFireActionStateUp;

private void OnFireActionStateUp(SteamVR_Action_Boolean fromAction, SteamVR_Input_Sources fromSource) { throw new NotImplementedException(); }

private void FireActionStateUpHandler(SteamVR_Action_Boolean fromAction, SteamVR_Input_Sources fromSource) { throw new NotImplementedException(); }

若跳转场景后用不到功能,可对应有RemoveOnStateUpListener和-=

  • 通过Update监听

if (SteamVR_Input.GetAction<SteamVR_Action_Boolean>("Fire").GetStateDown(SteamVR_Input_Sources.Any)) {

}

类似于

if (Input.GetKeyDown(KeyCode.Space))
{

}

2 Interction System

 Player,Hand的使用

注:

  • player核心单例类
  • Hand实现各种交互的基础

2019.4之后  unity提供 URP(通用渲染管线),HDRP(高清渲染管线)对手部控制器模型的渲染可能有问题

player:

  • 命名空间:

using Valve.VR.InteractionSystem;

  • 作为单例类的使用实例:

//获取手柄速度

Player.instance.rightHand.GetTrackedObjectVelocity();

Interctable组件

  • 交互前提:有碰撞器
  • 交互感知:Interctable
  • 交互(抓取,抛掷):Throwable组件
与UI交互
控制器直接接触UI进行交互
  1. UI元素加碰撞器
  2. 添加UIElement组件
不直接接触进行交互(非Interctable组件)
凝视UI交互效果(头部射线)
  • InputModule:SteamVR UI的事件系统,类比原EventSystem

 示例:

public class HIdeGaze : MonoBehaviour
{
public Transform headForRaycast;//发射射线位置

public Transform curssor;//光标

public Image progressImage;//光标图片

public Vector3 rayPositionoffset;//“眼睛”位置

public LayerMask layerMask;//参与交互的层级

private float rayLenth = 50f;

private GameObject currentInteractable;

private GameObject lastInteractable;

public float startTime;

public float activeTime=3;

public bool isEnable;


private void Awake()
{
if (curssor!=null)
{
//隐藏光标
curssor.gameObject.SetActive(false);
}

//是否开启凝视
isEnable = true;
}

// Update is called once per frame
void Update()
{
//必须要有发射射线的位置指定
if (headForRaycast==null)
{
return;
}

if (progressImage!=null)
{
progressImage.fillAmount = 0;
}

if (isEnable)
{
EyeRaycast();
}
}

private void EyeRaycast()
{
Vector3 adjustedPosition = headForRaycast.position + (headForRaycast.right * rayPositionoffset.x) +
(headForRaycast.up * rayPositionoffset.y) + (headForRaycast.forward * rayPositionoffset.z);

Ray ray = new Ray(adjustedPosition, headForRaycast.forward);
RaycastHit hit;
if (Physics.Raycast(ray,out hit,rayLenth,layerMask))
{
if (curssor!=null)
{
curssor.gameObject.SetActive(true);
curssor.position = hit.point;
curssor.rotation = headForRaycast.rotation;
}

//获取游戏对象上的BUtton组件

Button aButton = hit.transform.GetComponent<Button>();
if (aButton==null)
{
//取消选择
DeactivateLastInteractable();

//设置当前交互UI为空
currentInteractable = null;

return;
}

currentInteractable = aButton.gameObject;

//如果当前交互对象存在且当前交互对象不是上一次交互的对象,及找到新的对象
if (currentInteractable&&currentInteractable!=lastInteractable)
{
//通知BUtton目前有悬停事件
InputModule.instance.HoverBegin(currentInteractable);
}
else if (currentInteractable==lastInteractable)
{
//与上一次交互的对象是同一个,认为是停留,凝视时间积累
startTime += Time.deltaTime;

if (progressImage!=null)
{
//填充效果
progressImage.fillAmount = (startTime / activeTime);
}

if (startTime>activeTime)
{
//InputModule通知BUtton,此时为点击事件
InputModule.instance.Submit(currentInteractable);
//重置凝视时间
startTime = 0;
//取消选择UI
DeactivateLastInteractable();
//凝视交互不可用
isEnable = false;
//1秒后开启凝视交互
Invoke("ReEnable", 1f);

}
}
//如果当前交互对象不是上一个交互对象,取消凝视交互
if (currentInteractable!=lastInteractable)
DeactivateLastInteractable();
//上一个交互对象设置为当前交互对象
lastInteractable = currentInteractable;
}
else//如果射线没有碰撞任何交互对象
{
DeactivateLastInteractable();
currentInteractable = null;
}
}

private void DeactivateLastInteractable()
{
//凝视时间归零
startTime = 0;
//进度图像归零
if (progressImage!=null)
{
progressImage.fillAmount = 0;
}
//上一个交互对象为null,则返回
if (lastInteractable=null)
{
return;
}

//INputModule通知上一个交互对象此时指针移出
InputModule.instance.HoverEnd(lastInteractable);
lastInteractable = null;

//隐藏用于标识视线的光标
if (curssor!=null)
{
curssor.gameObject.SetActive(false);
}
}

private void ReEnable() {
isEnable = true;
}
}

 使用射线进行UI交互
  • UI添加碰撞器
  • 控制器添加SteamVr_laser Point组件
  • 编写控制脚本,示例如下:

using UnityEngine;
using UnityEngine.EventSystems;
using Valve.VR.Extras;

public class LaserInteraction : MonoBehaviour
{

private SteamVR_LaserPointer _laser;

private GameObject uGUIElement;

public bool isEnabled = true;

private void Awake()
{
_laser = GetComponent<SteamVR_LaserPointer>();

if (_laser!=null)
{
if (!isEnabled)
{
_laser.enabled = false;
return;
}
else
{
_laser.PointerIn += _laser_PointerIn;
_laser.PointerOut += _laser_PointerOut;
_laser.PointerClick += _laser_PointerClick;
}
}
}

private void _laser_PointerClick(object sender, PointerEventArgs e)
{
IPointerClickHandler _pointerClickHandler = e.target.GetComponent<IPointerClickHandler>();
if (_pointerClickHandler != null)
{
_pointerClickHandler.OnPointerClick(new PointerEventData(EventSystem.current));
}
}

private void _laser_PointerOut(object sender, PointerEventArgs e)
{
IPointerExitHandler _pointerExitHandler = e.target.GetComponent<IPointerExitHandler>();
if (_pointerExitHandler != null)
{
_pointerExitHandler.OnPointerExit(new PointerEventData(EventSystem.current));
}
}

private void _laser_PointerIn(object sender, PointerEventArgs e)
{
IPointerEnterHandler _pointerEnterHandler = e.target.GetComponent<IPointerEnterHandler>();
if (_pointerEnterHandler != null)
{
_pointerEnterHandler.OnPointerEnter(new PointerEventData(EventSystem.current));
}
}
}

与3D物体交互
 自定义物体的手部抓取姿态

  • 3D物体身上添加组件,碰撞器,Interactable,Trowable,SteamVR_Skeleton_Poser,注:场景中需要有player

  • 在SteamVR_Skeleton_Poser下调整手部关节,注意创建动作与保存动作
  • 创建手部动作的另一部分,在SteamVR_Skeleton_Poser下的Blending Editor下进行两部分动作的融合,完成完整动作,要绑定手柄动作
  • 编写脚本控制3D物体的动作符合手部动作,并将脚本挂在3D物体上

using UnityEngine;
using Valve.VR;
using Valve.VR.InteractionSystem;

public class Gun : MonoBehaviour
{

//板机游戏对象
public Transform TriggerGo;

//扳机完全按下的角度
public Vector3 TriggerDownRotation;

//扳机的初始角度
private Quaternion _triggerOriginRotation;

private Interactable _interactable;

// Start is called before the first frame update
void Start()
{
_triggerOriginRotation = TriggerGo.localRotation;

_interactable = GetComponent<Interactable>();
if (_interactable!=null)
{
_interactable.onAttachedToHand+= InteractableOnonAttachedToHand;
_interactable.onDetachedFromHand += InteractableOnonDetachedFromHand;
}
}

private void InteractableOnonDetachedFromHand(Hand hand)
{
SteamVR_Actions.default_Squeeze.RemoveOnAxisListener(OnSqueezeAxis,hand.handType);
}


private void InteractableOnonAttachedToHand(Hand hand)
{
SteamVR_Actions.default_Squeeze.AddOnAxisListener(OnSqueezeAxis, hand.handType);
}
/// <summary>
///
/// </summary>
/// <param name="fromAction"></param>
/// <param name="fromSource"></param>
/// <param name="newAxis">手柄当前按下的角度</param>
/// <param name="newDelta">与上次相比的变化亮,正为按下,负为抬起</param>
private void OnSqueezeAxis(SteamVR_Action_Single fromAction, SteamVR_Input_Sources fromSource, float newAxis, float newDelta)
{
TriggerGo.localRotation = Quaternion.Lerp(_triggerOriginRotation, Quaternion.Euler(TriggerDownRotation), newAxis);
}
}

Item Package实现双手持握道具交互

  • 创建空物体作为道具的位置,添加Item Package Swpaner组件
  • 创建空物体作预制体,添加Item Package组件,指定双手单手,指定使用到的三个模型预制体
  • 为模型预制体添加组件,Destroy on Detached from hand,itempackage Reference,
  • 为道具预览位置添加碰撞器,实现交互
  • 可选:上一部分知识,自定义物体的手部抓取姿态。
使用CircularDrive旋转3D物体
开关门示例:
  • 把手部位添加碰撞体(在根物体添加)
  • 添加CircularDrive组件,并进行设置
旋转阀门示例:

  • 给阀门添加CircularDrive,linear Mapping,linear Displacement组件
  • 在linear Displacement组件中在要移动的轴上输入移动距离
  • 将Max Angle和out Angle设置为同一个值

 使用LinearDrive平移3D物体
  • 给抽屉添加碰撞体,LinearDrive组件
  • 设置初始点和结束点
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值