HTC Vive之Unity3d开发日记——手柄交互编程

来源:互联网 发布:猫和老鼠胡桃夹子知乎 编辑:程序博客网 时间:2024/04/30 03:52

目录:
       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进行解析.
>0       HTC Vive 开箱 + 简易评测(从购买到游戏调试安装)上
>1       HTC Vive 开箱 + 简易评测(从购买到游戏调试安装)下
这两篇算是一个前期准备工作指南!
>2       【浸入VIVE开发】三分钟用Unity3D开发第一个VR程序
这一篇算是一个基础教学!
>3       基于Unity+HTC VIVE的VR游戏开发
这一篇算是一个中级教学,我从中学到了不少知识,但是缺乏对代码的深度解析,而这个将是我接下来要做的.当然,我希望阁下可以抽时间对这些资源进行深入的理解,这将有利于我们之后可以一起来探讨!
 
如图,我们要实现的是:通过代码来实现对手柄的全面掌控,重点在于交互,至于如何开发一款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
usingUnityEngine;
usingSystem.Collections;
 
[RequireComponent(typeof(SteamVR_TrackedObject))]
publicclass SteamVR_TestThrow : MonoBehaviour
{
        //预设,用于投掷的物体
        publicGameObject prefab;
        //位于手柄上的刚体,也就是预设物体出现的地方
        publicRigidbody attachPoint;
 
        //追踪的设备,这里是我们的手柄
        SteamVR_TrackedObject trackedObj;
        //固定关节
        FixedJoint joint;
 
        voidAwake()
        {
                //获取追踪的设备,即手柄
                trackedObj = GetComponent<SteamVR_TrackedObject>();
        }
 
        voidFixedUpdate()
        {
                //获取手柄的输入,也就是用户的输入
                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松开的时候
                elseif (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
usingUnityEngine;
usingValve.VR;
 
//结构体,点击事件参数
publicstruct ClickedEventArgs
{
//控制器索引
publicuint controllerIndex;
//标记
publicuint flags;
//控制板上的坐标
publicfloat padX, padY;
}
 
//委托,点击事件句柄
publicdelegate void ClickedEventHandler(objectsender, ClickedEventArgs e);
 
publicclass SteamVR_TrackedController : MonoBehaviour
{
//控制器索引
publicuint controllerIndex;
//控制器状态
publicVRControllerState_t controllerState;
//按下扳机与否
publicbool triggerPressed = false;
//这个是正面最下方的按钮,对应Steam系统
publicbool steamPressed = false;
//这个是最上方的菜单按钮
publicbool menuPressed = false;
//这个pad控制板是中间的圆形触摸区域,功能比较多
publicbool padPressed = false;
publicbool padTouched = false;
//这个是负责判断是否握住了手柄
publicbool gripped = false;
 
//菜单点击事件句柄
publicevent ClickedEventHandler MenuButtonClicked;
publicevent ClickedEventHandler MenuButtonUnclicked;
//扳机扣动事件句柄
publicevent ClickedEventHandler TriggerClicked;
publicevent ClickedEventHandler TriggerUnclicked;
//Steam点击事件句柄
publicevent ClickedEventHandler SteamClicked;
//触摸板点击事件句柄
publicevent ClickedEventHandler PadClicked;
publicevent ClickedEventHandler PadUnclicked;
//触摸板触摸事件句柄
publicevent ClickedEventHandler PadTouched;
publicevent ClickedEventHandler PadUntouched;
//抓取事件句柄
publicevent ClickedEventHandler Gripped;
publicevent ClickedEventHandler Ungripped;
 
// Use this for initialization
voidStart()
{
//如果没有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>
publicvirtual void OnTriggerClicked(ClickedEventArgs e)
{
if(TriggerClicked != null)
TriggerClicked(this, e);
}
 
/// <summary>
/// 引发扳机松开事件
/// </summary>
/// <param name="e">E.</param>
publicvirtual void OnTriggerUnclicked(ClickedEventArgs e)
{
if(TriggerUnclicked != null)
TriggerUnclicked(this, e);
}
 
/// <summary>
/// 引发菜单点击事件
/// </summary>
/// <param name="e">E.</param>
publicvirtual void OnMenuClicked(ClickedEventArgs e)
{
if(MenuButtonClicked != null)
MenuButtonClicked(this, e);
}
 
/// <summary>
/// 引发菜单松开事件
/// </summary>
/// <param name="e">E.</param>
publicvirtual void OnMenuUnclicked(ClickedEventArgs e)
{
if(MenuButtonUnclicked != null)
MenuButtonUnclicked(this, e);
}
 
/// <summary>
/// 引发系统按钮点击事件
/// </summary>
/// <param name="e">E.</param>
publicvirtual void OnSteamClicked(ClickedEventArgs e)
{
if(SteamClicked != null)
SteamClicked(this, e);
}
 
/// <summary>
/// 引发触摸板点击事件
/// </summary>
/// <param name="e">E.</param>
publicvirtual void OnPadClicked(ClickedEventArgs e)
{
if(PadClicked != null)
PadClicked(this, e);
}
 
/// <summary>
/// 引发触摸板未点击事件
/// </summary>
/// <param name="e">E.</param>
publicvirtual void OnPadUnclicked(ClickedEventArgs e)
{
if(PadUnclicked != null)
PadUnclicked(this, e);
}
 
/// <summary>
/// 引发触摸板触摸事件
/// </summary>
/// <param name="e">E.</param>
publicvirtual void OnPadTouched(ClickedEventArgs e)
{
if(PadTouched != null)
PadTouched(this, e);
}
 
/// <summary>
/// 引发触摸板没有触摸事件
/// </summary>
/// <param name="e">E.</param>
publicvirtual void OnPadUntouched(ClickedEventArgs e)
{
if(PadUntouched != null)
PadUntouched(this, e);
}
 
/// <summary>
/// 引发握紧事件
/// </summary>
/// <param name="e">E.</param>
publicvirtual void OnGripped(ClickedEventArgs e)
{
if(Gripped != null)
Gripped(this, e);
}
 
/// <summary>
/// 引发未握紧事件
/// </summary>
/// <param name="e">E.</param>
publicvirtual void OnUngripped(ClickedEventArgs e)
{
if(Ungripped != null)
Ungripped(this, e);
}
 
// Update is called once per frame
voidUpdate()
{
//OpenVR是在GitHub上开源的,详情见之前发的资源帖,此处引用
var system = OpenVR.System;
//系统不为空 且 成功获取手柄控制器状态
if(system != null&& system.GetControllerState(controllerIndex, refcontrollerState))
{
//手柄状态中的无符号64位整数按钮按下 且 扳机按钮左移一个单位(此处要参考OpenVR)
ulongtrigger = 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);
 
}
elseif (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);
}
 
//同上,紧握赋值
ulonggrip = 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);
 
}
elseif (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);
}
 
//同上,触摸板按下事件赋值
ulongpad = 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);
}
elseif (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);
}
 
//同上,菜单赋值
ulongmenu = 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);
}
elseif (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);
 
}
elseif (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游戏源码
开发, 日记 , 话题
本主题由 朱迪 于 2016-5-9 0
0 0
原创粉丝点击