HTC Vive之Unity3d开发日记——手柄交互编程_太阳神LoveU的博客-程序员宅基地

技术标签: VIVE手柄  HTC VIVE  VR、AR、MR和全息技术研究开发  交互编程  U3D  手柄控制  


目录:
       HTC Vive之Unity3d开发日记

You can fool all the people some of the time,and some of the people all the time,but you can`t fool all the people all the time.  __Abraham Lincoln , American president
你可以在某些时间里欺骗所有的人,也可以在所有的时间里欺骗某些人,但你决不能在所有的时间里欺骗所有的人.

先引用俺的偶像之一林肯的一句名言,我们也许暂时不知道事情的真相,但是我们总有一天会知道的!
B格有了!继上一篇,如果你已经把设备和插件准备就绪,这一篇将先对目前的开发资源进一步整理,然后再对SteamVR Plugin进行解析.
这两篇算是一个前期准备工作指南!
这一篇算是一个基础教学!
这一篇算是一个中级教学,我从中学到了不少知识,但是缺乏对代码的深度解析,而这个将是我接下来要做的.当然,我希望阁下可以抽时间对这些资源进行深入的理解,这将有利于我们之后可以一起来探讨!
 
如图,我们要实现的是:通过代码来实现对手柄的全面掌控,重点在于交互,至于如何开发一款VR游戏是在实现交互以后需要探讨的事情,我们一步一个脚印来探索!
 
第一步,如图,我们已经导入了SteamVR Plugin,下面的SteamVR绿色图标表明Htc Vive的硬件也已经准备就绪,这是SteamVR_TestThrow场景,也是我们展开分析的入手点.这个测试很简单,就是你按下Trigger的时候,手柄上会实例化一个圆球+cube的结合体,当然松开Trigger的时候这个结合体就会脱离手柄,当然,你还可以施加一个扔的动作,这样的话结合体会有一个对应的加速度.
 
手柄是HTC Vive的重要交互手段,我们通过第一个图片应该对其有一个直观的了解了,总共是九个按钮:
  • 第一个是菜单按钮;
  • 2,3,4,5分别对应的是Trackpad/Touchpad的上下左右,有时候对应的是XBox手柄的▲OX四个按钮或者摇杆;
  • 6对应的是系统按钮/Steam;
  • 7是Trigger/扳机,对应大多数FPS游戏里面的枪械的Shoot/Fire;
  • 8对应的Grip/紧握在手柄的左右两侧各有一个,有时候我们用它来翻页;
  • 9其实是Trackpad/Touchpad在Z轴的一个延伸,相当于是点击事件Click.
接下来就可以直接上代码了!
[C#]  纯文本查看  复制代码
?
 
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
using UnityEngine;
using System.Collections;
 
[RequireComponent( typeof (SteamVR_TrackedObject))]
public class SteamVR_TestThrow : MonoBehaviour
{
         //预设,用于投掷的物体
         public GameObject prefab;
         //位于手柄上的刚体,也就是预设物体出现的地方
         public Rigidbody attachPoint;
 
         //追踪的设备,这里是我们的手柄
         SteamVR_TrackedObject trackedObj;
         //固定关节
         FixedJoint joint;
 
         void Awake()
         {
                 //获取追踪的设备,即手柄
                 trackedObj = GetComponent<SteamVR_TrackedObject>();
         }
 
         void FixedUpdate()
         {
                 //获取手柄的输入,也就是用户的输入
                 var device = SteamVR_Controller.Input(( int )trackedObj.index);
                 //如果关节为空 且 用户按下扳机
                 if (joint == null && device.GetTouchDown(SteamVR_Controller.ButtonMask.Trigger))
                 {
                         //把预设实例化并设置其位置在手柄上的指定位置,这个指定位置就是手柄上的那个圈圈
                         var go = GameObject.Instantiate(prefab);
                         go.transform.position = attachPoint.transform.position;
 
                         //把关节组件添加到实例化的对象上,链接的位置就是这个刚体,添加这个组件的目的就是为了当你松开Trigger的时候分开手柄和预设物体
                         //这个FixedJoint组件实际上就是一个关节,作用是链接两个物体
                         joint = go.AddComponent<FixedJoint>();
                         joint.connectedBody = attachPoint;
                 }
                 //又如果关节不为空 且 手柄上的扳机Trigger松开的时候
                 else if (joint != null && device.GetTouchUp(SteamVR_Controller.ButtonMask.Trigger))
                 {
                         //获取关节上的游戏对象,获取其刚体
                         var go = joint.gameObject;
                         var rigidbody = go.GetComponent<Rigidbody>();
                         //立即摧毁关节,并置为空
                         Object.DestroyImmediate(joint);
                         joint = null ;
                         //15秒后摧毁该对象
                         Object.Destroy(go, 15.0f);
 
                         // We should probably apply the offset between trackedObj.transform.position
                         // and device.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 same amount we are predicting our render poses.
                         //大概意思是:我们也许应该在正确的位置应用trackedObj.transform.position和device.transform.pos之间的偏移量到物理模拟中去
                         //然而,如果那样的话我们就想要预测和渲染动作同样数量的视觉位置
 
                         //原始位置有的话就是原始位置,没有的话取其父类
                         var origin = trackedObj.origin ? trackedObj.origin : trackedObj.transform.parent;
                         if (origin != null )
                         {
                                 //取其速度和角度
                                 rigidbody.velocity = origin.TransformVector(device.velocity);
                                 rigidbody.angularVelocity = origin.TransformVector(device.angularVelocity);
                         }
                         else
                         {
                                 rigidbody.velocity = device.velocity;
                                 rigidbody.angularVelocity = device.angularVelocity;
                         }
                         //最大角速度
                         rigidbody.maxAngularVelocity = rigidbody.angularVelocity.magnitude;
                 }
         }
}

Ok,在下已经注释得非常清楚了,重点在于Trigger/扳机的交互:device.GetTouchDown(SteamVR_Controller.ButtonMask.Trigger)按下扳机时返回真和device.GetTouchUp(SteamVR_Controller.ButtonMask.Trigger)松开扳机时返回真.
更进一步,我们通过解析SteamVR_TrackedController来看看其他按钮事件:
[C#]  纯文本查看  复制代码
?
 
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
using UnityEngine;
using Valve.VR;
 
//结构体,点击事件参数
public struct ClickedEventArgs
{
//控制器索引
public uint controllerIndex;
//标记
public uint flags;
//控制板上的坐标
public float padX, padY;
}
 
//委托,点击事件句柄
public delegate void ClickedEventHandler( object sender, ClickedEventArgs e);
 
public class SteamVR_TrackedController : MonoBehaviour
{
//控制器索引
public uint controllerIndex;
//控制器状态
public VRControllerState_t controllerState;
//按下扳机与否
public bool triggerPressed = false ;
//这个是正面最下方的按钮,对应Steam系统
public bool steamPressed = false ;
//这个是最上方的菜单按钮
public bool menuPressed = false ;
//这个pad控制板是中间的圆形触摸区域,功能比较多
public bool padPressed = false ;
public bool padTouched = false ;
//这个是负责判断是否握住了手柄
public bool gripped = false ;
 
//菜单点击事件句柄
public event ClickedEventHandler MenuButtonClicked;
public event ClickedEventHandler MenuButtonUnclicked;
//扳机扣动事件句柄
public event ClickedEventHandler TriggerClicked;
public event ClickedEventHandler TriggerUnclicked;
//Steam点击事件句柄
public event ClickedEventHandler SteamClicked;
//触摸板点击事件句柄
public event ClickedEventHandler PadClicked;
public event ClickedEventHandler PadUnclicked;
//触摸板触摸事件句柄
public event ClickedEventHandler PadTouched;
public event ClickedEventHandler PadUntouched;
//抓取事件句柄
public event ClickedEventHandler Gripped;
public event ClickedEventHandler Ungripped;
 
// Use this for initialization
void Start()
{
//如果没有SteamVR_TrackedObject组件,则添加该组件
if ( this .GetComponent<SteamVR_TrackedObject>() == null )
{
gameObject.AddComponent<SteamVR_TrackedObject>();
}
 
//索引赋值
this .GetComponent<SteamVR_TrackedObject>().index = (SteamVR_TrackedObject.EIndex)controllerIndex;
//如果有SteamVR_RenderModel组件则对该组件索引进行赋值
if ( this .GetComponent<SteamVR_RenderModel>() != null )
{
this .GetComponent<SteamVR_RenderModel>().index = (SteamVR_TrackedObject.EIndex)controllerIndex;
}
}
 
/// <summary>
/// 引发扳机按下事件
/// </summary>
/// <param name="e">E.</param>
public virtual void OnTriggerClicked(ClickedEventArgs e)
{
if (TriggerClicked != null )
TriggerClicked( this , e);
}
 
/// <summary>
/// 引发扳机松开事件
/// </summary>
/// <param name="e">E.</param>
public virtual void OnTriggerUnclicked(ClickedEventArgs e)
{
if (TriggerUnclicked != null )
TriggerUnclicked( this , e);
}
 
/// <summary>
/// 引发菜单点击事件
/// </summary>
/// <param name="e">E.</param>
public virtual void OnMenuClicked(ClickedEventArgs e)
{
if (MenuButtonClicked != null )
MenuButtonClicked( this , e);
}
 
/// <summary>
/// 引发菜单松开事件
/// </summary>
/// <param name="e">E.</param>
public virtual void OnMenuUnclicked(ClickedEventArgs e)
{
if (MenuButtonUnclicked != null )
MenuButtonUnclicked( this , e);
}
 
/// <summary>
/// 引发系统按钮点击事件
/// </summary>
/// <param name="e">E.</param>
public virtual void OnSteamClicked(ClickedEventArgs e)
{
if (SteamClicked != null )
SteamClicked( this , e);
}
 
/// <summary>
/// 引发触摸板点击事件
/// </summary>
/// <param name="e">E.</param>
public virtual void OnPadClicked(ClickedEventArgs e)
{
if (PadClicked != null )
PadClicked( this , e);
}
 
/// <summary>
/// 引发触摸板未点击事件
/// </summary>
/// <param name="e">E.</param>
public virtual void OnPadUnclicked(ClickedEventArgs e)
{
if (PadUnclicked != null )
PadUnclicked( this , e);
}
 
/// <summary>
/// 引发触摸板触摸事件
/// </summary>
/// <param name="e">E.</param>
public virtual void OnPadTouched(ClickedEventArgs e)
{
if (PadTouched != null )
PadTouched( this , e);
}
 
/// <summary>
/// 引发触摸板没有触摸事件
/// </summary>
/// <param name="e">E.</param>
public virtual void OnPadUntouched(ClickedEventArgs e)
{
if (PadUntouched != null )
PadUntouched( this , e);
}
 
/// <summary>
/// 引发握紧事件
/// </summary>
/// <param name="e">E.</param>
public virtual void OnGripped(ClickedEventArgs e)
{
if (Gripped != null )
Gripped( this , e);
}
 
/// <summary>
/// 引发未握紧事件
/// </summary>
/// <param name="e">E.</param>
public virtual void OnUngripped(ClickedEventArgs e)
{
if (Ungripped != null )
Ungripped( this , e);
}
 
// Update is called once per frame
void Update()
{
//OpenVR是在GitHub上开源的,详情见之前发的资源帖,此处引用
var system = OpenVR.System;
//系统不为空 且 成功获取手柄控制器状态
if (system != null && system.GetControllerState(controllerIndex, ref controllerState))
{
//手柄状态中的无符号64位整数按钮按下 且 扳机按钮左移一个单位(此处要参考OpenVR)
ulong trigger = controllerState.ulButtonPressed & (1UL << (( int )EVRButtonId.k_EButton_SteamVR_Trigger));
//针对扳机点击事件属性的一系列赋值
if (trigger > 0L && !triggerPressed)
{
triggerPressed = true ;
ClickedEventArgs e;
e.controllerIndex = controllerIndex;
e.flags = ( uint )controllerState.ulButtonPressed;
e.padX = controllerState.rAxis0.x;
e.padY = controllerState.rAxis0.y;
OnTriggerClicked(e);
 
}
else if (trigger == 0L && triggerPressed)
{
triggerPressed = false ;
ClickedEventArgs e;
e.controllerIndex = controllerIndex;
e.flags = ( uint )controllerState.ulButtonPressed;
e.padX = controllerState.rAxis0.x;
e.padY = controllerState.rAxis0.y;
OnTriggerUnclicked(e);
}
 
//同上,紧握赋值
ulong grip = controllerState.ulButtonPressed & (1UL << (( int )EVRButtonId.k_EButton_Grip));
//同上,针对紧握事件属性的一系列赋值
if (grip > 0L && !gripped)
{
gripped = true ;
ClickedEventArgs e;
e.controllerIndex = controllerIndex;
e.flags = ( uint )controllerState.ulButtonPressed;
e.padX = controllerState.rAxis0.x;
e.padY = controllerState.rAxis0.y;
OnGripped(e);
 
}
else if (grip == 0L && gripped)
{
gripped = false ;
ClickedEventArgs e;
e.controllerIndex = controllerIndex;
e.flags = ( uint )controllerState.ulButtonPressed;
e.padX = controllerState.rAxis0.x;
e.padY = controllerState.rAxis0.y;
OnUngripped(e);
}
 
//同上,触摸板按下事件赋值
ulong pad = controllerState.ulButtonPressed & (1UL << (( int )EVRButtonId.k_EButton_SteamVR_Touchpad));
if (pad > 0L && !padPressed)
{
padPressed = true ;
ClickedEventArgs e;
e.controllerIndex = controllerIndex;
e.flags = ( uint )controllerState.ulButtonPressed;
e.padX = controllerState.rAxis0.x;
e.padY = controllerState.rAxis0.y;
OnPadClicked(e);
}
else if (pad == 0L && padPressed)
{
padPressed = false ;
ClickedEventArgs e;
e.controllerIndex = controllerIndex;
e.flags = ( uint )controllerState.ulButtonPressed;
e.padX = controllerState.rAxis0.x;
e.padY = controllerState.rAxis0.y;
OnPadUnclicked(e);
}
 
//同上,菜单赋值
ulong menu = controllerState.ulButtonPressed & (1UL << (( int )EVRButtonId.k_EButton_ApplicationMenu));
if (menu > 0L && !menuPressed)
{
menuPressed = true ;
ClickedEventArgs e;
e.controllerIndex = controllerIndex;
e.flags = ( uint )controllerState.ulButtonPressed;
e.padX = controllerState.rAxis0.x;
e.padY = controllerState.rAxis0.y;
OnMenuClicked(e);
}
else if (menu == 0L && menuPressed)
{
menuPressed = false ;
ClickedEventArgs e;
e.controllerIndex = controllerIndex;
e.flags = ( uint )controllerState.ulButtonPressed;
e.padX = controllerState.rAxis0.x;
e.padY = controllerState.rAxis0.y;
OnMenuUnclicked(e);
}
 
//触摸板触摸事件赋值
pad = controllerState.ulButtonTouched & (1UL << (( int )EVRButtonId.k_EButton_SteamVR_Touchpad));
if (pad > 0L && !padTouched)
{
padTouched = true ;
ClickedEventArgs e;
e.controllerIndex = controllerIndex;
e.flags = ( uint )controllerState.ulButtonPressed;
e.padX = controllerState.rAxis0.x;
e.padY = controllerState.rAxis0.y;
OnPadTouched(e);
 
}
else if (pad == 0L && padTouched)
{
padTouched = false ;
ClickedEventArgs e;
e.controllerIndex = controllerIndex;
e.flags = ( uint )controllerState.ulButtonPressed;
e.padX = controllerState.rAxis0.x;
e.padY = controllerState.rAxis0.y;
OnPadUntouched(e);
}
}
}
}

SteamVR_TrackedController是基于OpenVR来实现手柄交互的,虽然是开源的,可惜我的水平有限,希望有大神可以讲讲OpenVR,当然,我们理解OpenVR的话当然是最好,如果不知道也是可以实现交互的,我们只需要去调用就可以了.
我这里也没有解析清楚,大家作为涉猎即可,大概知道有这么回事儿,下一篇我们可以继续研究SteamVR_GazeTracker,SteamVR_LaserPointer,SteamVR_Teleporter,敬请期待!
BTW,如果你有HTC Vive的开源程序希望可以分享一个给我,用于研究学习,在此谢谢了先!
其实我最希望Valve能开源The Lab,或者哪位大神有源码!





unity3d fps 游戏源码;unity3d的fps游戏源码
开发日记 ,  话题
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/lqzixi/article/details/52579546

智能推荐

There was an internal api err.. & The entitlements specified in your application’s Code Signing..._Apachi---CGA的博客-程序员宅基地

自己写的小demo,引入的一些地图库,准备在真机上进行调试的时候。出现错误1:There was an internal API error 错误 搜罗了一些网上的资料记录下来:错误原因:把Product Name作为程序名称,程序名称错乱解决方法:检查Product Name, 不要包含中文以及特殊字符。在info.plist中新增Bundle display

VFP6的报表中如何添加JPG图片_weixin_45653315的博客-程序员宅基地_vfp报表图片批量导入照片

我要做一个运动会的参赛证,需要在每张参赛证上添加并打印运动员的参赛项目及照片,现在运动会的编排都已完成,就差参赛证了,哪位好心的大神帮帮忙,谢谢!// An highlighted blockvar foo = ‘bar’;## 生成一个适合你的列表- 项目 - 项目 - 项目1. 项目12. 项目23. 项目3- [ ] 计划任务- [x] 完成任务...

mysql文件上传漏洞_文件上传漏洞_赖明星的博客-程序员宅基地

文件上传漏洞:一句话木马一句话木马主要由两部分组成:执行函数与 接收被执行代码的变量执行函数:eval()assert()create_function()array_map()array_filter()call_user_func()call_user_func_array()eval()将字符串当作PHP代码执行,注意一定要带分号;将含有这个代码的PHP文件上传到服务器,命名为webshel...

pwgen命令生成密码_点点2B的博客-程序员宅基地_pwgen命令

生成脚本if [ "$1" == "" ];then pwgen -s -c -n -y 12 -B -r \'\\\" else pwgen -s -c -n -y 12 -B -r \'\\\" $1 fi生成的密码中排除了指定的字符,如单引号,双引号等使用方式:./pwgen.sh 密码个数如:[[email protected] ~]# ./pwgen.sh 20g_R]9~w9j}bC 3+//.YP7#%]b [nUF3R-y$&amp;u` |s[Pyr4)=roJ *W

AtCoder Beginner Contest 174 F.Range Set Query_旺 崽的博客-程序员宅基地

AtCoder Beginner Contest 174 F.Range Set Query题目链接第一次碰到不考思维的 FFF,我笑了????这题就是一个主席树的应用,裸题+原题,注意此题比原题数据大了一倍多,所以线段树的大小也要扩大,我因为这个 RE 了好几次????,AC代码如下:#include&lt;bits/stdc++.h&gt;using namespace std;const int maxn = 5e5+5;int n, m;struct Tree { int

题目52:输入两个正整数m和n,判断m和n是否互质(即最大公约数为1),是则输出Yes,否则输出No。_爱吃菜菜的小女孩的博客-程序员宅基地

Python 输入两个正整数m和n,判断m和n是否互质(即最大公约数为1),是则输出Yes,否则输出No。

随便推点

asp.net下使用FCKeditor 2.6.6_weixin_30299709的博客-程序员宅基地

以前一直用lion的。因为现在写一个西班牙语的网站,FCKeditor 有现成的多国语言库,就改用FCKeditor了。1.先从http://www.fckeditor.net/download下载FCKeditor.Net_2.6.4.zip FCKeditor_2.6.6.zip解压FCKeditor.Net_2.6.4.zip,提取FCKeditor.Net_2.6.4\bin...

IBatis学习资料收录__明月的博客-程序员宅基地

 1、易百教程资料: IBatis教程 -- 易百教程 2、博客园博客列表 : IBatis博客列表 -- 作者ngulc   3、ITeye博客:  ibatis中 SqlMapClientDaoSupport,SqlMapClientTemplate ,SqlMapDaoTemplate区别    4、ibatis之——sqlMapConfig.xml配置文件详解:...

自定义Pickle,借助pickle模块来完成简化的dump和load_weixin_34178244的博客-程序员宅基地

import pickle # 导入pickle模块class My_pickle: # 定义pickle类 def __init__(self, path): self.file = path def dump(self, obj): with open(self.file, 'ab') as f: # 以ab模式写入...

矩阵连乘问题--动态规划_slient_love的博客-程序员宅基地

矩阵连乘问题–动态规划题目描述给定n个矩阵{A1A2…An},其中Ai和Ai+1是可乘的,考察这n个矩阵的连乘积A1A2…An。由于矩阵的乘法满足结合律,故计算矩阵的连乘积有许多不同的计算次序,而不同的计算次序,所需要计算的连乘次数也是不同的,求解连乘次数最少的矩阵连乘最优次序。例如:矩阵连乘积A1A2A3,3个矩阵的维数分别为10100,1005和550,连乘时加括号的方式有:((A1A2 )* A3) 数乘次数:101005+10550=7500(A1*(A2A3)) 数乘

java paint里有super有什么区别_在java中使用super.paintComponent(g)或getGraphics()_杰克波比的博客-程序员宅基地

关于一些事情,我有点困惑:示例代码,显示我的问题,这是不可编译的// imageprivate BufferedImage image;private Graphics2D graphic;private changeImage;. . .//thread loopwhile (running) {. . .render();Graphics showGraphic = getGraphics()...

selenium+python面试题目总结_dongdou7287的博客-程序员宅基地

1. WebDriver原理webDriver是按照client/server模式设计,client就是我们的测试代码,发送请求,server就是打开的浏览器来打开client发出的请求并做出响应。具体的工作流程:·webdriver打开浏览器并绑定到指定端口。启动的浏览器作为remote server.·client通过commandExecuter发送httpRequ...

推荐文章

热门文章

相关标签