开发平台:Unity 2019版本以上
常见交互方式:一头盔、二手柄、三手柄
思路:
Start()
函数中获取追踪脚本private SteamVR_TrackedObject trackedObj;
void Start()
{
trackedObj = GetComponent<SteamVR_TrackedObject>();
}
Update()
函数中通过控制器找到索引void Update()
{
// 通过控制器获取键位
var device = SteamVR_Controller.Input((int)trackedObj.index);
// 获取手柄具体某一键位
if(device.GetPress(SteamVR_Controller.ButtonMask.Trigger))
{
print("玩家按下扳机键");
}
// 通过openvr_api实现调用
if(device.GetTouchDown(Value.VR.EVRButtonId.k_Button_SteamVR_Touchpad))
{
print("玩家按下触摸板");
}
// 手柄震动反馈
if(device.GetHairTrigger())
{
// 震动灵敏度 0-3999
device.TriggerHapticPulse(1000, EVRButtonId.k_EButton_Grip);
}
}
其他:脚本无法正常调用或运作情况下,重启VR设备即可解决。
private float GetAngle(Vector2 from, Vector2 to)
{
float angle;
// 返回角度,不分正负
angle = Vector2.Angle(from, to);
// 获取两向量夹角
Vector3 cross = Vector3.Cross(from, to);
return cross.z > 0 ? -angle : angle ;
}
Vector2 from = new Vector2(0, 0);
Vector2 to = device.GetAxis();
//角度计算
float angle = GetAngle(from, to);
//判断
if(angle > 45 && angle < 135)
{
Debug.Log("按下——下");
}
源于封装脚本SteanVR_GazeTracker
,通过当前头戴设备发出射线,选中交互。
设备追踪脚本SteamVR_TrackedObject
,对Camera组件添加。
实现逻辑:
SteanVR_GazeTracker
SteamVR_GazeTracker gaze;
void Start()
{
gaze = GetComponent<SteamVR_GazeTracker>();
gaze.GazeOn += GazeOnFunction;
gaze.GazeOff -= GazeOffFunction;
}
// 进入凝视状态
private void GazeOnFuncetion(object sender, GazeEventArgs e)
{
gameObject.GetComponent<MeshRenderer>().material.color = Color.red;
}
// 退出凝视状态
private void GazeOffFunction(object sender, GazeEventArgs e)
{
gameObject.GetComponent<MeshRenderer>().material.color = Color.green;
}
VR世界中不存在平面说法,即UI设计模式为World Space
SteamVR_Laser_Pointer
:手柄射线脚本
实现逻辑:
SteamVR_LaserPoint laserPointer;
void Start()
{
laserPointer = GetComponent<SteamVR_LaserPointer>();
// 事件机制
laserPointer.PointerIn += PointerInFunction;
laserPointer.PointerOut += PointerOutFUnction;
}
// 射线进入时
private void PointerInFunction(object sender, PointerEventArgs e)
{
// 判断对象
if (e.target.name == "XXX") { Destroy(e.target.gameObject); }
if (e.target.name == "OOO") { e.target.GetComponent<MeshRenderer>().color = Color.Red; }
}
// 射线离开时
private void PointerOutFunction(object sender, PointerEventArgs e)
{
// ......
}
SteamVR_Teleporter
:瞬移交互脚本
实现逻辑:
SteamVR_TrackedObject
的引用private SteamVR_TrackedObject trackedObj;
private StramVR_Controller.Device device;
void Start()
{
// 获取当前手柄追踪组件
trackedObj = GetComponent<SteamVR_TrackedObject>();
// 获取手柄对应索引
device = SteamVR_Controller.Input((int)trackedobj.index);
}
public GameObject target;
void Update()
{
if (device.GetPress(SteamVR_Controller.ButtonMask.Trigger))
{
if(target != null)
{
// 对象使用手柄位置,并设置手柄为父对象
target.transform.parent = transform;
// 获取刚体,取消重力,启用动力学
Rigidbody rig = target.GetComponent<Rigidbody>();
rig.useGravity = false;
rig.isKinematic = true;
}
}
}
// 进入碰撞
private void OnTriggerEnter(Collider other)
{
// 检测:体积过大物体无法拖拽
// 存储触摸的对象
target = other.transform.gameObejct;
}
// 退出碰撞
private void OnTriggerExit(Collider other)
{
target = null;
}
描述:全名 VR ToolKit(虚拟现实工具包) ,负责统一管理各种VR设备SDK,提供开发VR程序的框架。其包含多种VR常用功能,如控制器激光指针、虚拟空间运动、UI、虚拟物体交互等。
开发者角度:仅需配置SDK,无需考虑哪种VR设备。
VRTK_SDK_Manager.cs
,并添加VRTK中SDKSetups
与SDKSetupSwitch
为子物体VRTK_Control_Events
至子物体上。同理复制子物体LefController,重命名为RightController实现逻辑:
VRTK_ControllerEvents
组件private VRTK_ControllerEvents controller;
void Start() {
controller = GetComponent<VRTK_ControllerEvents>();
controller.TriggerPressed += Controller_TriggerPress;
}
void Update() {
if (controller.triggerPressed) {
print("按下逻辑");
}
}
private void Controller_TriggerPressed(object sender, ControllerInteractionEventsArgs e) {
throw new System.NotImplementedException();
}
VRTK_Pointer
):用于发射光标指针VRTK_BezierPointerRenderer
):用于绘制曲线光标VRTK_StraightPointerRenderer
):用于绘制直线光标限制瞬移区域:VRTK_Height_Adjust+Teleport.cs
组件中 Taget List Policy 属性。该属性需要添加VRTK_Policy List
组件
区域障碍显示:VRTK_Player Area.cs
组件
被拾取物体:
VRTK_InteractableObject.cs
组件,并启用属性 Is Grabbable手柄控制器:
VRTK_InteractTouch.cs
组件VRTK_InteractGrab.cs
组件关于抓取选项:(Grab Options)
Window -> VRTK -> Setup Interactable Object 打开 Setup Object 窗口
VRTK_SDK Object Alias
:配置Body/Head
VRYKUICanvas
组件VRTK_UICanvas.cs
组件VRTK_pointer
VRTK_Controller Tooltip
:按钮引导显示
VRTK_Controller Events_Unity Events
:Unity事件触发方法
VRTK_Interact Touch
:触摸脚本
VRTK_Interact Use
:射线检测
VRTK_Interact Grab
:拾取脚本
被射线检测物体或交互物体脚本需要继承VRTK_InteractableObject
,被交互对象不可设置为Static
public class Door : VRTK_InteractableObject {
private bool isOpen = false;
public override void StartUsing(VRTK_InteractUse currentUsingObject = null) {
base.StartUsing(currentUsingObject);
transform.rotation = Quaternion.Euler(new Vector3(0, 0, 0));
if(isOpen) transform.Rotate(new Vector3(0, -90, 0));
else transform.Rotate(new Vector3(0, 0, 0));
isOpen = !isOpen;
}
}
存储材质数据FloorMatData.cs
using UnityEngine;
using Dot.Tweening;
[System.Serializable]
public class FloorMatData {
public Material floorMat;
public Sprite image;
}
切换材质FloorMatList.cs
using UnityEngine;
public class FloorMatList : MonoBehaviour {
public Transform imageBg;
public bool isShowMat = false;
public FloorMatData[] matDataList;
public GameObject buttonMat;
public GameObject floor;
void Start() {}
void Update() {}
private void Init() {
imageBg.localScale = new Vector3(0, 1, 1);
foreach(var item in matDataList) {
GameObject cloneButton = Instantiate(buttonMat);
cloneButton.transform.parent = imageBg;
cloneButton.GetComponent<RectTransform>();
rect.localPosition = Vector3.zero;
rect.localRotation = Quaternion.Euler(Vector3.zero);
rect.localSacle = Vector3.zero;
cloneButton.GetComponent<Image>().sprite = data.image;
cloneButton.GetComponent<Button>().onClick.AddListener(delegate() { OnButtonClick(cloneButton);})
}
}
public void ShowMatList() {
if(isShowMat) {
imageBg.DOScale(new Vector3(0, 1, 1), 0.3f);
}
else {
imageBg.DOScale(Vector3.one, 0.3f);
}
isShowMat = !isShowMat;
}
private void OnButtonClick(GameObject button) {
// 修改图片材质
floor.GetComponent<MeshRenderer>().material = matDataList[button.transform.GetSiblingIndex()].floorMat;
}
}