【入门部署】Steam VR 环境部署与基本程序实现说明


注意:此入门指南仅适用于 Unity Old Input System!现 SteamVR 支持 New Input System


开发平台:Unity 2019版本以上

头显设备介绍

  • HTC Vive 由HTC与Value联合开发的一款VR头显(虚拟现实头戴式显示器)产品。
  • 支持 SteamVR 技术

配置要求

  • VR主机:(最低配置)含机箱、显示器、键鼠及备用电缆、插线板
  • 显卡:NVIDIA GTX 970/AMD 290 级别或更高(建议参考官网展示信息)
  • 内存:4GB 以上
  • 处理器:因特i5以上
  • USB版本:2.0 及其以上

下载SteamVR

  • 前往 Steam 平台,搜索 Steam VR 下载安装
    【入门部署】Steam VR 环境部署与基本程序实现说明_第1张图片
  • 该工具用于检查虚拟设备连接情况,与设置活动环境。

SteamVR 场景搭建

  • 前往 Unity Asset Store 下载资源 “SteamVR Plugin”
    【入门部署】Steam VR 环境部署与基本程序实现说明_第2张图片

常见交互方式

常见交互方式:一头盔、二手柄、三手柄
思路:

  1. 找到交互手柄,追踪脚本Index查找,在Start()函数中获取追踪脚本
private SteamVR_TrackedObject trackedObj;

void Start()
{
	trackedObj = GetComponent<SteamVR_TrackedObject>();
}
  1. 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);
	}
}
  1. 获取不同的按键状态:点击(按下、持续按下、抬起)触摸(开始触摸、持续触摸、结束触摸)
  2. 手柄键位:扳机键(Trigger)、触摸板(TouchPad)、侧键(2个 Grip)、菜单键(Application)、系统键(System)

其他:脚本无法正常调用或运作情况下,重启VR设备即可解决。

SteamVR 触摸板分区

常见触摸板分区效果:计算角度实现不同区域(0~360°)【入门部署】Steam VR 环境部署与基本程序实现说明_第3张图片

  1. 初始点位置,判断位置在哪一轴向(x/y轴)
  2. 旋转方向(顺时针、逆时针)
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 ;
}
  1. 角度大小 (Unity中支持 0° ~ 180°/-180° ~ 0°)
Vector2 from = new Vector2(0, 0);
Vector2 to = device.GetAxis();
//角度计算
float angle = GetAngle(from, to);
//判断
if(angle > 45 && angle < 135)
{
	Debug.Log("按下——下");
}

SteamVR 凝视效果

源于封装脚本SteanVR_GazeTracker,通过当前头戴设备发出射线,选中交互。

设备追踪脚本SteamVR_TrackedObject,对Camera组件添加。

实现逻辑:

  1. 查找凝视脚本SteanVR_GazeTracker
  2. 获取凝视组件(Component)
SteamVR_GazeTracker gaze;

void Start()
{
	gaze = GetComponent<SteamVR_GazeTracker>();
	
	gaze.GazeOn += GazeOnFunction;
	gaze.GazeOff -= GazeOffFunction;
}
  1. 调用事件
// 进入凝视状态
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;
}
  1. 响应逻辑

UI 补充说明

VR世界中不存在平面说法,即UI设计模式为World Space


SteamVR 射线交互

SteamVR_Laser_Pointer:手柄射线脚本

实现逻辑:

  1. 获取手柄射线脚本
  2. 使用射线脚本中的事件机制来完成需求
SteamVR_LaserPoint laserPointer;
void Start()
{
	laserPointer = GetComponent<SteamVR_LaserPointer>();
	// 事件机制
	laserPointer.PointerIn += PointerInFunction;
	laserPointer.PointerOut += PointerOutFUnction;
}
  1. 事件绑定
// 射线进入时
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 瞬移交互

SteamVR_Teleporter:瞬移交互脚本


SteamVR 拾取交互

实现逻辑:

  1. 获取对手柄脚本SteamVR_TrackedObject的引用
private SteamVR_TrackedObject trackedObj;
private StramVR_Controller.Device device;

void Start()
{
	// 获取当前手柄追踪组件
	trackedObj = GetComponent<SteamVR_TrackedObject>();
	// 获取手柄对应索引
	device = SteamVR_Controller.Input((int)trackedobj.index);
}
  1. 检测物品对象(碰撞检测 - 标签、名称、层级)
  2. 手柄按键响应(一般为侧键)
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;
		}
	}
}
  1. 抓取:被抓对象与手柄绑定
    绑定:位置变化(刚体有无)
    松开:被抓对象与手柄的绑定解除(恢复刚体)
// 进入碰撞
private void OnTriggerEnter(Collider other)
{
	// 检测:体积过大物体无法拖拽

	// 存储触摸的对象
	target = other.transform.gameObejct;
}

// 退出碰撞
private void OnTriggerExit(Collider other)
{
	target = null;
}

VRTK(开源)

描述:全名 VR ToolKit(虚拟现实工具包) ,负责统一管理各种VR设备SDK,提供开发VR程序的框架。其包含多种VR常用功能,如控制器激光指针、虚拟空间运动、UI、虚拟物体交互等。

开发者角度:仅需配置SDK,无需考虑哪种VR设备。

VRTK 场景搭建

  1. 前往 Assets Store 搜索并下载 VRTK、SteamVR
  2. 导入下载插件
  3. 创建 GameObject 对象,命名VRTKManager,添加脚本VRTK_SDK_Manager.cs,并添加VRTK中SDKSetupsSDKSetupSwitch为子物体
  4. 创建 GameObject 对象,命名VRTKScripts,并创建子物体LeftController,添加VRTK_Control_Events至子物体上。同理复制子物体LefController,重命名为RightController

实现逻辑:

  1. 获取VRTK_ControllerEvents组件
private VRTK_ControllerEvents controller;

void Start() {
	controller = GetComponent<VRTK_ControllerEvents>();
	controller.TriggerPressed += Controller_TriggerPress;
}

void Update() {
	if (controller.triggerPressed) {
		print("按下逻辑");
	}
}
  1. 定义事件
private void Controller_TriggerPressed(object sender, ControllerInteractionEventsArgs e) {
	throw new System.NotImplementedException();
}

VRTK交互方式

VRTK 瞬移交互

  • 手柄 附加光标指针组件(VRTK_Pointer):用于发射光标指针
    附加 贝塞尔 光标渲染器(VRTK_BezierPointerRenderer):用于绘制曲线光标
    附加 贝塞尔 光标渲染器(VRTK_StraightPointerRenderer):用于绘制直线光标

限制瞬移区域:VRTK_Height_Adjust+Teleport.cs组件中 Taget List Policy 属性。该属性需要添加VRTK_Policy List组件

区域障碍显示:VRTK_Player Area.cs组件

VRTK 拾取交互

被拾取物体:

  1. 附加 Collider组件
  2. 附加 VRTK_InteractableObject.cs组件,并启用属性 Is Grabbable

手柄控制器:

  1. 附加触摸 VRTK_InteractTouch.cs组件
  2. 附加抓取 VRTK_InteractGrab.cs组件

关于抓取选项:(Grab Options)

  • Is Grabblable:是否启用抓取
  • Hold Button To Grab:是否持续抓取
  • Stay Grabbed On Teleport:传送中保持抓取状态
  • Valid Drop(释放物体方式)
    No Drop:抓取后不释放
    Drop AnyWhere:随时释放
  • Grab Override Button:覆盖抓取按钮
  • Grab Attack Mechanic Script:抓取对象
  • Secondary Grab Action Script:抓取行为对象

VRTK 攀爬交互

Window -> VRTK -> Setup Interactable Object 打开 Setup Object 窗口

VRTK_SDK Object Alias:配置Body/Head


  1. 添加VRYKUICanvas组件
    (1)创建 Canvas,并修改 Render Mode 为 World Space
    (2)为 Canvas 添加VRTK_UICanvas.cs组件
    (3)添加 Button
  2. 手柄添加 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;
	}
}

VRTK 材质变更

存储材质数据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;
	}

}

你可能感兴趣的:(vr,unity,游戏引擎)