SteamVR如何取消头盔凝视多功能头盔

移动端VR也能玩SteamVR!VRidge使用教程
在安卓端VR软件VRidge的一次重大更新中,加入了对SteamVR和OpenVR的支持。这篇教程来把使用方法介绍给大家。
此前蜂桑(本文原作者)曾提到一种可以将电脑与手机联通的应用,只需要将电脑屏幕上的游戏画面分屏发送到手机屏幕上,反过来再用手机的陀螺仪控制游戏视角,再配上一副手机VR眼镜,就可以用很低廉的成本体验VR游戏,玩家们一般管此类软件叫做手机虚拟现实远程桌面。
此类软件从Oculus公布DK1之后就开始出现了,但迄今为止,依旧存在画质下降、延迟偏高、体验差劲等很多问题。不过局限于几十、一百多人民币一个的手机VR眼镜,比起六七百美元的高端PC用VR头盔,巨大的价格差异还是使得人们对此类软件的需求从未消失过,这一类软件的数量也是有增无减。
开发者RiftCat也做了一款名为VRidge的安卓应用,它适用于安卓手机端的虚拟现实远程桌面应用。该软件不久前的重大更新中,加入了对SteamVR和OpenVR的支持。也就是说,如果你有一台谷歌纸盒或同类VR设备的话,不出意外你就可以玩很多SteamVR游戏而不必额外花799美金去购买一台HTC Vive,当然体验好坏咱再另说。蜂桑把应用连带使用方法都给大家带来。
一、VRidge系统安装设置教程
下文参考下面这段视频来做解说。
1.首先去官网/vridge给自己注册一个账号并登录;
2.登录过后下载并安装VRidge的PC客户端;
3.启动VRidge的PC客户端并登录,下载手机端应用并安装到你的手机里(老规矩,蜂桑都给大家下好了,去17AVR社区里拿/topic/1736/vridge);
4.把连接装好客户端的PC和装好并启动应用的手机放到同一个Wifi环境下,让它们连接;
5.一旦电脑和手机连接成功,配置就基本完成,剩下的视频都是在解说使用效果了。
二、VRidge玩SteamVR游戏教程
在我们开始之前,我们想要强调两件事情:
1.这只是一个Beta测试版,所以不要专门为了VRidge购买SteamVR游戏,亏了可没人管赔。目前VRidge的技术依赖很多第三方元素,因而兼容性无法保障。你可以用很多SteamVR上免费的SteamVR游戏来测试其兼容性。
2.虽然没有真金白银那么重要,但这里还有另一条附带的提醒:要经常保存你的游戏,因为真的可能会有故障。不是所有的游戏都可用,而且随着游戏的更新,兼容性也变来变去。下面测试了一些热门游戏,而以下是这些游戏可能问题的简短总结:
《欧洲卡车模拟2》(Euro Truck Simulator2)
你要去ETS的Steam选项中开启名为“Oculus”的beta测试分支。然后你要在启动参数内增加“-openvr”。这个游戏需要停用Steam桌面影院,这样就游戏就不会以扁平模式启动。
《精英危机》(Elite:Dangerous)
可用,不过文字有点模糊不清。其实这个问题即使在HTC Vive设备上也很常见,这不是VRidge的锅。我们建议Steam分辨率使用,所以确保你的手机支持1080P显示。此外本作有可能黑屏。
《虚拟桌面/Steam桌面影院》(Virtual Desktop/Steam Desktop Theater)
这些应用延迟特别严重而且抓取也有问题,因为相比原生VR游戏,所有画面都被抓取并渲染了两次。RiftCat还在试图寻找减轻这些应用延迟的方案。
三、教程正文
在兼容性问题警示之后,为了使SteamVR游戏可用,你还要先完成以下几个步骤:
1.在Steam内安装SteamVR并运行
2.关闭Steam
3.安装VRidge SteamVR驱动。SteamVR路径应该可以自动填充;如果它是空的,那就手动把它指向SteamVR位置。也可以在连接完手机应用和客户端之后再做这一步。(※如果SteamVR更新了,那么你必须重复这个步骤。)
4.再次打开Steam并启动SteamVR。你必须在手机应用连接在RiftCat上的前提下启动SteamVR。如果你没有匹配手机应用就已经启动SteamVR了,那么就关闭它,连接完手机应用之后再启动。
如果你看不到这个按钮,从你的游戏库中像启动常规游戏那样把SteamVR启动起来
P.S.如果SteamVR要求你再过一遍“房间设置”,那么选择站立模式,然后在指令要求的时候站起来。不然你的镜头可能会太接近地面,用起来不是很舒服。你会发现SteamVR的设置和教程的VR模式反了,而且连接也不正确,不过这不会影响其他游戏。
5.SteamVR小窗会打开,如果你是在手机设备连接着的状态下打开的SteamVR,那么它就应该显示“连接”状态并且SteamVR游戏也会显示“在VR中玩”这个选项。
这样就Ok啦!你现在应该可以玩很多神奇的SteamVR游戏了。
如果你的手机设备连接断开了,你也可以通过重启手机应用来重新连接VRidge。应该不需要重启游戏或者SteamVR。
如果你卡在“没有发现可用的头戴式设备”这一步,关闭SteamVR,确保你的手机设备已经连接到Riftcat客户端,再重新启动SteamVR。
如果你有问题,请通过VRidge的帮助中心来联系RiftCat(他们会不断在那里添加已知SteamVR问题的),或者Reddit论坛也行(可以在那里分享变通方案和解决方案)。因为是测试版软件,按照本教程一步步操作依旧有可能出现各种各样的问题,可以去Reddit论坛上进行圈内探讨和排除故障。VRidge的讨论串地址是:
目前VRidge还处于beta测试状态,所以在谷歌纸盒上使用该应用玩SteamVR游戏时可能还不能感受到其应有的品质,而且时不时会有卡顿或者报错。这不是小问题,但在高端头显没到或不想买之前,它毕竟解决的是从平面到VR这个质变的大问题。在解决根本性问题时,留下一些相对较小的问题待议,也是可以接受的嘛。
VRidge应用现在可以在Google Play上免费下载而且没有IAPs需求。不过如果你不希望碰到这样那样的报错的话,你也可以等VRidge的成品发布后再使用。像本文撰写过程中,RiftCat就又做了更新,添加了对Leap Motion手部追踪的支持,进一步扩展VRidge可玩的SteamVR游戏范围。不过Leap Motion的普及程度可能比Rift和Vive还低呢,我们就暂时不做这个新版本的教程了。
本文来源:
本站原创内容。未经许可,禁止转载。
评论仅代表网友个人看法,不代表游戏时光同意其观点。
评论仅代表网友个人看法,不代表游戏时光同意其观点。为什么关不掉steamvr?_百度知道
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。
为什么关不掉steamvr?
最近电脑很奇怪,总是开机就打开steamvr,关了又会马上自动开启,关的时候还说会关闭firefox.exe,怎么解决?
我有更好的答案
(闪红灯)电池电量偏低 【手把控制器】 (绿灯)正常使用模式 2、软体等) (红灯)遭遇错误(线路、显示;(闪蓝灯)正在与头盔配对 (蓝灯)正在与头盔连结 (橘灯)充电中 【基地台】nbsp. 换个地方安装SteamVR (不同的硬碟) (可能因为系统名称有所冲突) 3. 重新安装Vive相关的驱动程式(使用USBDevview与类似软体)【头戴式显示器】 (绿灯)正常使用模式 (弱绿灯)待机模式 开启SteamVR的权限为系统管理员【ERROR(301)未找到SteamVR关键元件】排除方式nbsp,并可能的话关闭可能影响的防毒程式 (绿灯)正常使用模式 (蓝灯)感测到移动中,请确定有完整固定 (弱绿灯)待机模式 (紫灯)两个基地台正在努力同步中nbsp
采纳率:97%
来自团队:
为您推荐:
其他类似问题
换一换
回答问题,赢新手礼包SteamVR脚本解析 - 简书
SteamVR脚本解析
SteamVR各脚本的功能
SteamVR/Scripts/下脚本各功能的实现
Paste_Image.png
1、SteamVR.cs 单例管理类,管理SteamVR程序的运行和终止。
2、SteamVR_Camera.cs 给场景添加一个最基本可运行的SteamVR组。
3、SteamVR_CameraFlip.cs 使用Shader将屏幕图像反转得到最终图像。
4、SteamVR_CameraMask.cs 将头盔中看不到的屏幕像素遮盖。
5、SteamVR_Controller.cs 管理类,管理所有设备的输入控制
6、SteamVR_ControllerManager.cs 管理类,管理场景中的设备活动
7、SteamVR_Ears.cs 控制Audio Listener的方向
8、SteamVR_ExternalCamera.cs 用于渲染外部摄像机
9、SteamVR_Fade.cs 屏幕渐变功能
10、SteamVR_Frustum.cs 生成用于渲染的面片
11、SteamVR_GameView.cs 处理除眼图像之外的渲染
12、SteamVR_IK.cs 手柄IK的控制
13、SteamVR_LoadLevel.cs 用于场景之间的平滑切换
14、SteamVR_Menu.cs 给出一个范例菜单
15、SteamVR_Overlay.cs 提供和控制2D图像的绘制
16、SteamVR_PlayArea.cs 对移动空间的设置
17、SteamVR_Render.cs 控制眼图像的渲染
18、SteamVR_RenderModel.cs 渲染手柄模型
19、SteamVR_Skybox.cs 设置天空盒
20、SteamVR_SphericalProjection.cs 应该是应用畸变投影矩阵
21、SteamVR_Stats.cs 通过GUI Text显示头盔状态
22、SteamVR_Status.cs 由事件控制的渐变效果的基类
23、SteamVR_StatusText.cs 继承22的文字渐变
24、SteamVR_TestController.cs 测试手柄每个按钮的输入
25、SteamVR_TrackedCamera.cs 提供记录相机的位置的功能
26、SteamVR_TrackedObject.cs 使场景中的物体和控制器的Pose保持一致
27、SteamVR_UpdatePose.cs 当使用OpenVR接口时用此更新Pose
28、SteamVR_Utils.cs 一些公共方法和数据结构
SteamVR/Extras/脚本下功能的实现
Paste_Image.png
SteamVR_GazeTracker.cs 提供凝视时的事件
SteamVR_LaserPointer.cs 应该是镭射光线
SteamVR_Teleporter.cs 传送功能
SteamVR_TestThrow.cs 投掷东西
SteamVR_TestTrackedCamera.cs 跟踪相机测试
SteamVR_TrackedController.cs 手柄按钮事件的接口
详细脚本解析:
SteamVR_GazeTracker.cs脚本解析
这个脚本的作用是判断当前物体是否被用户(头显)所注视,进入注视和离开注视都会有回调。处于注视状态的物体与实际注视点的距离范围定义为小于0.15米,而离开注视状态的距离范围为大于0.4米。之所以有一个大概的范围,并且使用了一个平面来相交,是因为注视这个动作是比较粗略的,玩家比较难能精确注视。
  Gaze回调的事件结构体,只有一个参数,即距离,表示凝视点与物体(中心)的距离
public struct GazeEventArgs
public delegate void GazeEventHandler(object sender, GazeEventArgs e);
public class SteamVR_GazeTracker : MonoBehaviour
//当前是否处于gaze状态
public bool isInGaze =
//入gaze状态回调,使用者可以通过代码添加自己的事件处理方法(在Inspector
中不会出现)
public event GazeEventHandler GazeOn;
//离开gaze状态回调
public event GazeEventHandler GazeO
//定义的进入gaze与离开gaze的距离范围
public float gazeInCutoff = 0.15f;
public float gazeOutCutoff = 0.4f;
// Contains a HMD tracked object that we can use to find the user's gaze
//头显的transform对象
Transform hmdTrackedObject =
// Use this for initialization
void Start ()
public virtual void OnGazeOn(GazeEventArgs e)
//如果有注册GazeOff回调,调用它
if (GazeOn != null)
GazeOn(this, e);
public virtual void OnGazeOff(GazeEventArgs e)
//如果有注册GazeOff回调,调用它
if (GazeOff != null)
GazeOff(this, e);
// Update is called once per frame
void Update ()
// If we haven't set up hmdTrackedObject find what the user is looking at
if (hmdTrackedObject == null)
//首次调用会去查找头显,方法是查找所有SteamVR_TrackedObject对象。所有的跟踪对象(比如头显、手柄、基站)都是SteamVR_TrackedObject对象(相应的对象上附加了SteamVR_TrackedObject脚本)
SteamVR_TrackedObject[] trackedObjects = FindObjectsOfType&SteamVR_TrackedObject&();
foreach (SteamVR_TrackedObject tracked in trackedObjects)
if (tracked.index == SteamVR_TrackedObject.EIndex.Hmd)
//找到头显设备,取其transform对象。头显设备的索引是0号索引
hmdTrackedObject = tracked.
if (hmdTrackedObject)
//构造一条从头显正方向的射线
Ray r = new Ray(hmdTrackedObject.position, hmdTrackedObject.forward);
//构造一个头显正方向、在当前物体位置的平面
Plane p = new Plane(hmdTrackedObject.forward, transform.position);
float enter = 0.0f;
//射线与物体平面正向相交,返回的enter为沿射线的距离。如果不相交,或者反向相交,则下面的Raycast返回false
if (p.Raycast(r, out enter))
//intersect为射线与物体平面在三维空间的交点
Vector3 intersect = hmdTrackedObject.position + hmdTrackedObject.forward *
//计算空间两点的距离,即物体当前位置与交点的距离
float dist = Vector3.Distance(intersect, transform.position);
//Debug.Log("Gaze dist = " + dist);
if (dist & gazeInCutoff && !isInGaze)
//当前物体与凝视点的距离小于0.15米,则认为进入gaze状态
isInGaze =
GazeEventA
e.distance =
OnGazeOn(e);
else if (dist &= gazeOutCutoff && isInGaze)
//当前物体与凝视点的距离超过0.4米,则认为离开gaze状态
isInGaze =
GazeEventA
e.distance =
OnGazeOff(e);
SteamVR_LaserPointer.cs
这个脚本的作用与上面的SteamVR_GazeTracker相关及类似。GazeTracker是通过头显的正视方向与物体相交来计算交点的。而这里是通过所谓的激光束来与物体相交的。激光束就是手柄指向的方向,可以在游戏里面把这个方向渲染出一条激光束出来,特别是在通过手柄进行菜单的UI操作的时候。在github openvr的sample目录下的unity_teleport_sample示例有使用,它被加到右手柄上
 同上面的GazeTracker一样,触发的事件所带的参数
public struct PointerEventArgs
//控制器(手柄)索引
public uint controllerI
//目前好像并没有用到
//激光原点到命中点(交点)的距离
//命中物体的transform对象
public delegate void PointerEventHandler(object sender, PointerEventArgs e);
public class SteamVR_LaserPointer : MonoBehaviour
//这个变量并未使用
public bool active =
// 激光的颜色
//激光束的粗细(创建了一个立方体,按下面的scale,x、y是0.002,z是100,就能看 到是一条很长的细线了)
public float thickness = 0.002f;
//一个空的GameObject,用于作激光束的parent
public GameO
//激光束本身,是用一个立方体拉长来模拟的(为啥不用圆柱体?显然立方体要比圆柱体渲染简单得多,在很细的情况下,用立方体是明智的选择)
public GameO
//用来判断是否为第一次调用
bool isActive =
//这个是暴露在inspector中的属性,用于控制是否给激光束(长方体)添加刚体。本身光是没有重量的,没有必要添加刚体吧。所以这里缺省是false
public bool addRigidBody =
//这个变量并未使用
//同上面的GazeTracker一样,用于触发激光命中和离开事件
public event PointerEventHandler PointerIn;
public event PointerEventHandler PointerO
//上次激光命中的物体的transform对象,用于判断是否命中同一个物体
Transform previousContact =
// Use this for initialization
void Start ()
//在脚本被加载的时候,做一些初始化
//首先创建一个holder(即激光束的父物体)
holder = new GameObject();
//holder的transform的parent设为当前脚本所在的物体(通常这个脚本会加到控制器手柄上面)
holder.transform.parent = this.
//位置设在0点(本地坐标系,相对于父亲)
holder.transform.localPosition = Vector3.
holder.transform.localRotation = Quaternion.
//创建激光束,用长方体模拟
pointer = GameObject.CreatePrimitive(PrimitiveType.Cube);
//将父亲设为上面的holder
pointer.transform.parent = holder.
//设置locale为(0.002,0.002,100),看起来就是一条很长的线
pointer.transform.localScale = new Vector3(thickness, thickness, 100f);
//位置设在父亲的(0,0,50)位置,因为对于立方体(长方体),其中心在立方体中心,因为上面被放大到了100倍,那移动位置到(0,0,50)可以让激光束的起点为父亲
pointer.transform.localPosition = new Vector3(0f, 0f, 50f);
pointer.transform.localRotation = Quaternion.
// 如果指定了addRigidBody为true,则为激光束添加一个刚体,对应的collider 则只设为触发器(不会执行碰撞,但会进入代码)。否则,会把collider销毁掉,也就是不需要collider
BoxCollider collider = pointer.GetComponent&BoxCollider&();
if (addRigidBody)
if (collider)
collider.isTrigger =
Rigidbody rigidBody = pointer.AddComponent&Rigidbody&();
rigidBody.isKinematic =
if(collider)
Object.Destroy(collider);
//新建纯色材质并添加到MeshRender中。Color值通过inspector设置
Material newMaterial = new Material(Shader.Find("Unlit/Color"));
newMaterial.SetColor("_Color", color);
pointer.GetComponent&MeshRenderer&().material = newM
public virtual void OnPointerIn(PointerEventArgs e)
//回调激光命中委托
if (PointerIn != null)
PointerIn(this, e);
public virtual void OnPointerOut(PointerEventArgs e)
//回调激光不再命中委托
if (PointerOut != null)
PointerOut(this, e);
// Update is called once per frame
void Update()
if (!isActive)
//第一次调用时将holder设为active(当前物体transform的第一个child就是holder)
isActive =
this.transform.GetChild(0).gameObject.SetActive(true);
//命中物体(或者说激光束)的最远距离记为100米
float dist = 100f;
//当前物体(手柄上)上还要挂一个SteamVR_TrackedController脚本
SteamVR_TrackedController controller = GetComponent&SteamVR_TrackedController&();
构造一条射线
Ray raycast = new Ray(transform.position, transform.forward);
//计算射线命中的场景中的物体
bool bHit = Physics.Raycast(raycast, out hit);
if(previousContact && previousContact != hit.transform)
// 如果之前已经有一个命中的物体,而当前命中的物体发生了变化,那么说明前一个命中的物体就要收到一个不再命中的通知
PointerEventArgs args = new PointerEventArgs();
if (controller != null)
args.controllerIndex = controller.controllerI
args.distance = 0f;
args.flags = 0;
args.target = previousC
OnPointerOut(args);
previousContact =
if(bHit && previousContact != hit.transform)
//通知命中新的物体
PointerEventArgs argsIn = new PointerEventArgs();
if (controller != null)
argsIn.controllerIndex = controller.controllerI
// hit.distance为射线原点到命中点的距离
argsIn.distance = hit.
argsIn.flags = 0;
//target记录的是命中物体的transform
argsIn.target = hit.
OnPointerIn(argsIn);
// 记录上一次命中的物体的transform
previousContact = hit.
previousContact =
if (bHit && hit.distance & 100f)
//如果命中物体距离小于100,则记录下来,否则最远就是100米
dist = hit.
if (controller != null && controller.triggerPressed)
//当按下扳机键时,将光束的粗细增大5倍,同时长度会设为dist,这样看起来光束就会到命中点截止,不会穿透物体
pointer.transform.localScale = new Vector3(thickness * 5f, thickness * 5f, dist);
//按下扳机或者当前控制器没有添加SteamVR_TrackedController时,显示原始粗细的光束
pointer.transform.localScale = new Vector3(thickness, thickness, dist);
//光束的位置总是设在光束长度的一半的位置,使得光束看起来总是从手柄发出来的
pointer.transform.localPosition = new Vector3(0f, 0f, dist/2f);
SteamVR_Teleporter.cs
这个脚本在github上的openvr的samples里面的unity_teleport_sample示例中有使用,从名字上看是用来做瞬移的。这个示例就是在场景中放了一些球,然后在右控制器上放了这个脚本,也就是可以通过右控制器做瞬移。做法是会从控制器上发出一个激光束,按下扳机键就可以瞬移到激光束指到的地方。通过观察球的相对位置就能看到位置的变化。这在游戏里面很常见,拿鼠标一点,被操作的对象就会过去(当然过去有两种方式,一种是慢慢走过去,另一种就是直接跳过去),另一个见过的就是街景地图。
  public enum TeleportType
  //瞬移类型是根据与地形的交点来确定目的位置的
  TeleportTypeUseTerrain,
  //碰撞体类型是根据与任何带碰撞体的物体的交点来确定目的位置的
  TeleportTypeUseCollider,
  //这个是与Y坐标为0的平面(通常就是地面)的交点来确定目的位置的
  TeleportTypeUseZeroY
  public bool teleportOnClick =
  public TeleportType teleportType = TeleportType.TeleportTypeUseZeroY;
  Transform reference
  //取的是最后渲染(depth最大)的相机(SteamVR_Camera)的原始点(这个origin实际就是将SteamVR_Camera添加到的原始场景中的Camera的位置)
  var top = SteamVR_Render.Top();
  //SteamVR相机的层次结构是最上层是origin,然后下面有左右手柄和head,head下面有eye和ears
  return (top != null) ? top.origin:
  void Start ()
//这个脚本所在的物体必要要添加SteamVR_TrackedController脚本(这个脚本的作用是将控制器的输入转换为事件回调),如果没有添加,则自动添加。这说明这个脚本所在的物体需要是控制器(手柄),在unity_teleport_sample示例中,正是加到了右手柄上
  var trackedController = GetComponent();
  if (trackedController == null)
  trackedController= gameObject.AddComponent();
// 添加扳机按下的回调
  trackedController.TriggerClicked+= new ClickedEventHandler(DoClick);
  if (teleportType== TeleportType.TeleportTypeUseTerrain)
  // Start theplayer at the level of the terrain
  var t =
  if (t != null)
//如果是地形类型,会将相机origin(基本上可以认为就是玩家的位置)的Y坐标先调整为地形的采样高度(即相机origin所在位置的地形的实际Y坐标——即将相机origin放到地形表面——也就是在地形表面的垂直投影的位置),这样可以避免人钻到地型里面了
  t.position = new Vector3(t.position.x, Terrain.activeTerrain.SampleHeight(t.position), t.position.z);
  void DoClick(object sender, ClickedEventArgs e)
//应该是通过这个变量来控制是否通过扳机键来瞬移。因为扳机键还可以用作其它用途,应该是在某种状态下才能通过扳机键来瞬移。比如,需要通过扳机键来瞬移时才需要将这个变量设为true
  if (teleportOnClick)
  var t =
  if (t == null)
  float refY =t.position.y;
创建了一个Y方向,-refY位置的平面(是-refY而不是refY的原因是这个是Plane的distance参数,而Plane的distance是原点到Plane的距离,而距离的正负决定了在平面的哪一边,为正表示原点在法线的正方向,为负表示原点在法线的反方向,这与通常的理解不一样,所以这里为-refY)
Paste_Image.png
Plane plane = new Plane(Vector3.up, -refY);
当前脚本应该绑定在手柄上,因此才会有手柄方向的一条射线
Ray ray = new Ray(this.transform.position,transform.forward);
// hasGroudTarget是指是否射线与地面相交,或者说是否射到了地面上
bool hasGroundTarget =
// dist为射线原点(即手柄的原点)与相交点的距离
float dist = 0f;
  if (teleportType== TeleportType.TeleportTypeUseTerrain)
//与地形进行碰撞
  RaycastHit hitI
  TerrainCollider tc = Terrain.activeTerrain.GetComponent();
  hasGroundTarget = tc.Raycast(ray, out hitInfo, 1000f);
  dist = hitInfo.
  else if (teleportType== TeleportType.TeleportTypeUseCollider)
//与场景中的碰撞体进行碰撞
  RaycastHit hitI
  Physics.Raycast(ray, out hitInfo);
  dist = hitInfo.
//这里并没有设为hasGroundTarget为true,那后面的瞬移就无法完成。所以设置为TeleportTypeUseCollider应该就不能瞬移啊(实测确实不可以),那为什么要设置这种类型?
//从实际意义来说,确实是不能你扳 机指向哪就瞬移到哪,人不是什么地方都能去的
//与地面(Y方向的一个平面)相交
  hasGroundTarget = plane.Raycast(ray, out dist);
  if (hasGroundTarget)
// headPosOnGround是head(head就是头显的位置)在地面(Y=0)的投 影,注意这里用的是localPosition
  Vector3 headPosOnGround = new Vector3(SteamVR_Render.Top().head.localPosition.x, 0.0f, SteamVR_Render.Top().head.localPosition.z);
//这里就是将origin移动到扳机位置了。ray.origin + ray.direction*dist得到的就是射线与地形/地面交点的位置。后面减去的两个点分别是手柄(这里取的是第一个子物体,实际上就是左控制器)和
//头显相对于origin的位置(XZ平面)。感觉没有必要减,按照所见即所得,玩家看到的激光束的交点,就直接把位置定到那就好了,不需要考虑头显或者手柄的偏移
  t.position= ray.origin + ray.direction * dist - new Vector3(t.GetChild(0).localPosition.x, 0f, t.GetChild(0).localPosition.z)- headPosOnG
SteamVR_TestIK.unity
这是一个测试IK(更准确地说应该是SteamVR_IK.cs的,反向运动,就是根据手柄的运动模拟带动手臂的运动)的示例场景。这个场景里有一个模拟手臂:
Paste_Image.png
分左右手,分别在左右两个手柄控制器下面,在场景中的样子是这样的:
Paste_Image.png
它这里只有两个关节(肩关节和腕关节,SteamVR_IK就只支持两个关节),然后有一个手指
SteamVR_TestThrow.unity
这个应该是测试通过手柄扔出一个物体的例子,主要是测试下面的这个SteamVR_TestThrow脚本,可以看到这个脚本被添加到了左右两个手柄上面:
Paste_Image.png
Template这个物体是被扔的物体,它由一个圆及一个子物体立方体组成。通过扳机键创建一个物体并抓在手中,然后通过甩臂并同时松开扳机将物体扔出去
SteamVR_TestThrow.cs
这个脚本上面的测试场景的控制脚本,要与SteamVR_TrackedObject一起使用。实际上它会加到手柄上。
[RequireComponent(typeof(SteamVR_TrackedObject))]
  public&  {
  //要扔掉的物体,并不是一个真正的prefab,而是一个场景中已经创建好的物体
  public GameO
  //这个是手柄上tip(手柄模型的一部分)下面的一个rigidbody。在手柄的模型中,所有的子组件(相对于父组件,即整个model)的位置都是(0,0,0),
//但下面会再带一
个attach的子对象,这个子对象是一个刚体,然后真正的位置是通过它来确定的
  public Rigidbody attachP
  //手柄这个跟踪对象
  SteamVR_TrackedObject trackedO
  //固定关节
  FixedJ
  void Awake()
//Awake不管脚本是不是启用都会调用
  trackedObj= GetComponent();
  void FixedUpdate()
// 返回的Device对象(SteamVR_Controller内部类),对当前跟踪设备的输入进行了一些封装
  var device = SteamVR_Controller.Input((int)trackedObj.index);
  if (joint == null &&device.GetTouchDown(SteamVR_Controller.ButtonMask.Trigger))
//如果还没有建立关节,当按下扳机键时,建立物体与手柄的(关节)关联,相当于就是抓起了物体
// 先创建了物体(复制了场景中的一个物体)
  var go = GameObject.Instantiate(prefab);
//创建的物体的位置位于tip的关联点的位置。
  go.transform.position= attachPoint.transform.
//添加固定关节,这样物体就能跟随手柄动而动了
  joint= go.AddComponent();
//将其与手柄tip的attach关联
  joint.connectedBody= attachP
  else if (joint != null &&device.GetTouchUp(SteamVR_Controller.ButtonMask.Trigger))
//而如果已经建立了关节,再按扳机键时则会销毁关节,由于物体是一个刚体,就会自由下落。当然下面还会通过手柄给它一个初速度
  var go =joint.gameO
  var rigidbody =go.GetComponent();
//销毁关节
  Object.DestroyImmediate(joint);
  joint=
//15秒后销毁物体
  Object.Destroy(go, 15.0f);
  // We shouldprobably apply the offset between trackedObj.transform.position
  // anddevice.transform.pos to insert into the physics sim at the correct
  // location,however, we would then want to predict ahead the visual representation
  // by the sameamount we are predicting our render poses.
/origin是SteamVR_TrackedObject中的一个变量,它大概就是SteamVR_Camera中的origin,也就是CameraRig的顶层物体,基本上它可以代表的是玩家的身体。
//因为除了头部(头显)及手臂(手柄)会动以外,身体本身也可以动。如果没有指定,则直接使用父亲的transform
  var origin =trackedObj.origin ? trackedObj.origin : trackedObj.transform.
  if (origin != null)
//如果指定了origin,因为是相对坐标(速度),转换成世界坐标(速度)
  rigidbody.velocity= origin.TransformVector(device.velocity);
  rigidbody.angularVelocity= origin.TransformVector(device.angularVelocity);
//如果没有指定,则直接使用跟踪设备的速度
  rigidbody.velocity= device.
  rigidbody.angularVelocity= device.angularV
  rigidbody.maxAngularVelocity= rigidbody.angularVelocity.
SteamVR_TrackedController.cs
这个脚本对控制器的输入做了一个简单的封装,将原始的输入数据转化成事件回调模式,它在SteamVR_LaserPointer.cs和SteamVR_Teleporter.cs中有使用,也就是在github上openvr中的teleporter示例中有使用。在SteamVR_Teleporter.cs中,如果当前物体上没有SteamVR_TrackedController.cs,会自动添加它(在老版的插件中,如果没有添加,会报错)。直接看代码看它干了些什么:
  点击事件回调参数
public struct ClickedEventArgs
  public uint controllerI
  public float padX, padY;
  //点击事件处理委托方法
  public delegate void ClickedEventHandler(object sender, ClickedEventArgs e);
  public&  {
  //控制器索引,即跟踪设备的索引
  public uint controllerI
  //控制器的状态,比如按键是否按下,轴数据等
  public VRControllerState_t controllerS
  //是否按了扳机键
  public bool triggerPressed =
  //是否按了steam键?代码中并没有使用
  public bool steamPressed=
 //是否按了菜单键
  public bool menuPressed =
  //是否在触控板上按下了
  public bool padPressed =
  //是否在触控板上触控
  public bool padTouched =
  //是否按下了拾取键
  public bool gripped =
  //各种按键的回调方法
  public event ClickedEventHandler MenuButtonC
  public event ClickedEventHandler MenuButtonU
  public event ClickedEventHandler TriggerC
  public event ClickedEventHandler TriggerU
  public event ClickedEventHandler SteamC
  public event ClickedEventHandler PadC
  public event ClickedEventHandler PadU
  public event ClickedEventHandler PadT
  public event ClickedEventHandler PadU
  public event ClickedEventHandler G
  public event ClickedEventHandler U
  // Use this for initialization
  void Start()
  //如果当前物体上没有添加SteamVR_TrackedObject,则自动添加
  if (this.GetComponent() == null)
  gameObject.AddComponent();
  if (controllerIndex != 0)
  //设置跟踪设备的索引
  this.GetComponent().index =(SteamVR_TrackedObject.EIndex)controllerI
  if (this.GetComponent() != null)
//如果当前物体(跟踪设备)上还有SteamVR_RenderModel,也设置它的索引。在SteamVR的Unity插件中,控制器本身上面并没有SteamVR_RenderModel,
//但在其下面的Model上面有,然后就是其它的跟踪设备(除hmd外)上都有。
  this.GetComponent().index =(SteamVR_TrackedObject.EIndex)controllerI
//如果没有(通过inspector)指定控制器索引,则从SteamVR_TrackedObject中取索引。SteamVR插件的实际情况是,就只是有左右两个控制器的 SteamVR_TrackedObject是没有指定索引的。但会在
//SteamVR_ControllerManager(通常挂在CameraRig顶层上面)中根据实际控制器的索引(比如可能只有一个控制器连接了)设置
  controllerIndex= (uint) this.GetComponent().
//还提供了接口设置控制器索引,这个也是会被SteamVR_ControllerManager广播调
  public void SetDeviceIndex(int index)
  this.controllerIndex= (uint)
//下面就是一些回调调用了
  public virtual void OnTriggerClicked(ClickedEventArgs e)
  if (TriggerClicked != null)
  TriggerClicked(this, e);
  public virtual void OnTriggerUnclicked(ClickedEventArgs e)
  if (TriggerUnclicked != null)
  TriggerUnclicked(this, e);
  public virtual void OnMenuClicked(ClickedEventArgs e)
  if (MenuButtonClicked != null)
  MenuButtonClicked(this, e);
  public virtual void OnMenuUnclicked(ClickedEventArgs e)
  if (MenuButtonUnclicked != null)
  MenuButtonUnclicked(this, e);
  public virtual void OnSteamClicked(ClickedEventArgs e)
  if (SteamClicked!= null)
  SteamClicked(this, e);
  public virtual void OnPadClicked(ClickedEventArgs e)
  if (PadClicked!= null)
  PadClicked(this, e);
  public virtual void OnPadUnclicked(ClickedEventArgs e)
  if (PadUnclicked!= null)
  PadUnclicked(this, e);
  public virtual void OnPadTouched(ClickedEventArgs e)
  if (PadTouched!= null)
  PadTouched(this, e);
  public virtual void OnPadUntouched(ClickedEventArgs e)
  if (PadUntouched!= null)
  PadUntouched(this, e);
  public virtual void OnGripped(ClickedEventArgs e)
  if (Gripped != null)
  Gripped(this, e);
  public virtual void OnUngripped(ClickedEventArgs e)
  if (Ungripped != null)
  Ungripped(this, e);
  // Update is called once per frame
  void Update()
// OpenVR.System即IVRSystem接口或者CVRSystem类
  var system = OpenVR.S
// Update是每帧调用,而IVRSystem.GetControllerState是获取即时的指定索引的控制器的状态
  if (system != null &&system.GetControllerState(controllerIndex, ref controllerState))
  ulong trigger =controllerState.ulButtonPressed & (1UL
0L &&!triggerPressed)
//按下了扳机键
  triggerPressed =
  ClickedEventA
  e.controllerIndex = controllerI
  e.flags = (uint)controllerState.ulButtonP
//padX/padY取的是轴输入设备TrackPad的x、y值。根据结构体的定义, rAxis0其实就是TrackPad
  e.padX = controllerState.rAxis0.x;
  e.padY = controllerState.rAxis0.y;
  OnTriggerClicked(e);
  else if (trigger == 0L &&triggerPressed)
//松开了扳机键
  triggerPressed =
  ClickedEventA
  e.controllerIndex = controllerI
  e.flags = (uint)controllerState.ulButtonP
  e.padX = controllerState.rAxis0.x;
  e.padY = controllerState.rAxis0.y;
  OnTriggerUnclicked(e);
  ulong grip =controllerState.ulButtonPressed & (1UL
0L &&!gripped)
//按下了拾取键
  gripped =
  ClickedEventA
  e.controllerIndex = controllerI
  e.flags = (uint)controllerState.ulButtonP
  e.padX = controllerState.rAxis0.x;
  e.padY = controllerState.rAxis0.y;
  OnGripped(e);
  else if (grip == 0L &&gripped)
//松开了拾取键
  gripped =
  ClickedEventA
  e.controllerIndex = controllerI
  e.flags = (uint)controllerState.ulButtonP
  e.padX = controllerState.rAxis0.x;
  e.padY = controllerState.rAxis0.y;
  OnUngripped(e);
  ulong pad =controllerState.ulButtonPressed & (1UL
0L &&!padPressed)
//在TrackPad上按下了
  padPressed =
  ClickedEventA
  e.controllerIndex = controllerI
  e.flags = (uint)controllerState.ulButtonP
  e.padX = controllerState.rAxis0.x;
  e.padY = controllerState.rAxis0.y;
  OnPadClicked(e);
  else if (pad == 0L &&padPressed)
//从TrackPad上松开了
  padPressed =
  ClickedEventA
  e.controllerIndex = controllerI
  e.flags = (uint)controllerState.ulButtonP
  e.padX = controllerState.rAxis0.x;
  e.padY = controllerState.rAxis0.y;
  OnPadUnclicked(e);
  ulong menu =controllerState.ulButtonPressed & (1UL
0L &&!menuPressed)
//按下了菜单键
  menuPressed =
  ClickedEventA
  e.controllerIndex = controllerI
  e.flags = (uint)controllerState.ulButtonP
  e.padX = controllerState.rAxis0.x;
  e.padY = controllerState.rAxis0.y;
  OnMenuClicked(e);
  else if (menu == 0L &&menuPressed)
// 松开了菜单键
  menuPressed =
  ClickedEventA
  e.controllerIndex = controllerI
  e.flags = (uint)controllerState.ulButtonP
  e.padX = controllerState.rAxis0.x;
  e.padY = controllerState.rAxis0.y;
  OnMenuUnclicked(e);
  pad =controllerState.ulButtonTouched & (1UL
0L &&!padTouched)
//在TrackPad上触摸
  padTouched =
  ClickedEventA
  e.controllerIndex = controllerI
  e.flags = (uint)controllerState.ulButtonP
  e.padX = controllerState.rAxis0.x;
  e.padY = controllerState.rAxis0.y;
  OnPadTouched(e);
  else if (pad == 0L &&padTouched)
// 在TrackPad上取消触摸
  padTouched =
  ClickedEventA
  e.controllerIndex = controllerI
  e.flags = (uint)controllerState.ulButtonP
  e.padX = controllerState.rAxis0.x;
  e.padY = controllerState.rAxis0.y;
  OnPadUntouched(e);
其他参考资料:
时间最强大的一件事就是,它会改变未来
···SteamVR各脚本的功能SteamVR/Scripts/下脚本各功能的实现 Paste_Image.png 1、SteamVR.cs 单例管理类,管理SteamVR程序的运行和终止。2、SteamVR_Camera.cs 给场景添加一个最基本可运行的SteamVR组...
This article is a record of my journey to learn Game Development and it will keep updating. 由于这篇文章的长度早已超出了简书编辑器的限制,因此后续内容将不在本文中更新,请移步本人的独...
(一)VRTK_ SimplePointer(直线简单激光指针) (二)VRTK_ControllerEvents(控制器事件) 当一个控制器按钮被按下,脚本发出一个事件表明按钮被按下了,这使得其他脚本去监听这个事件而不需要实现任何控制器逻辑。脚本也有一个公共的布尔类型的按...
一、脚本上的优化 1、在使用数组或ArrayList对象时应当注意 [csharp]view plaincopyprint? length=myArray.L for(inti=0;i { } 避免 [csharp]view plaincopyprint? fo...
转自/donghua/p/4957415.html Oculus/GearVR开发者群 Welcometo the Unity Development GuideIntroduction简介Welcometo t...
本文参加#未完待续,就要表白#活动,本人承诺,文章内容为原创,且未在其他平台发表过。
2016年,显得很平凡的一年,全球也没有什么大规模的灾难,也没有什么伟大人物的逝世,总之一切都像那个夏天一样,懒洋洋的,空气中的闷热夹着一丝重金属的味道,也许是路边某个CD店的音响...
我长发短发又如何?我有无刘海也无碍!我性格安不安静又怎样!我身高和他匹不匹配也无妨,我和他的理想型相不相似都没用,躺在他怀里躲风的人不会是我,和他一起起床刷牙的人不会是我,被他牵着手看初雪的人不会是我,和他一起逛街散步的人不会是我,反正他不会娶我,我也没有机会嫁给他了。
山花烂漫朝天开,烟雨苍茫故人来。谁料身后事遥远?转眼人身已成尘。劝君莫要总蹉跎,应知报应和因果。自古恶有恶人磨,不见作孽永风流。
很多人经常会疑惑,我为什么时间管理也做了,可是事情还是没办法做好呢?时间管理只是一种方法,不是说你用了你就心安理得的给自己一个理由:我用了,所以我就应该会好。 我们讨论三个话题: 为什么要做时间管理? 关于时间管理的方法很多,方法也多种多样。比如GTD,番茄工作法,每天3件...
最近打球老不顺,虽然技术加强不少但也是伤病不断(;`O?)o不是左手中指挫伤就是今天抢板时候不懂被谁的指甲抠伤左手无名指…看来要休养一周了唉╯﹏╰ 永亨海港城B地块,开始自己单独配合整个带地下室的商业住宅了,对很多用电房间的提资配合渐渐有了眉目,记下来多多理解。比如说开闭所...}

我要回帖

更多关于 魔兽世界取消头盔显示 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信