我是靠谱客的博主 隐形大树,这篇文章主要介绍《图说VR》——HTC Vive控制器按键事件解耦使用,现在分享给大家,希望可以做个参考。


本文章由cartzhang编写,转载请注明出处。 所有权利保留。
文章链接:http://blog.csdn.net/cartzhang/article/details/53915229
作者:cartzhang

Unity的 Steam VR插件本身也带有事件处理。但是我还想把事件给解耦出来,这样方便在各个项目中,不用关心硬件的各种处理而只用关心使用的,且可以任意的通过接受事件来触发相应的操作。

项目的参考图片可下载地址:https://github.com/cartzhang/ImgSayVRabc/tree/master/ViveEventDemo/Img

今天我们说谈论的就是下面这个东西:

这里写图片描述
图1.1

一、所需资源


所需资源,很少。
需要用Steam VR插件 ,可以从Untiy商店下载。当然你可以使用文章后面给出本工程的导出包,文章后面有下载地址:

这里写图片描述
图0

但是电脑还是需要安装steam的,这个暂时还是需要翻墙的。你懂的,翻墙是一项技能。
安装后:

这里写图片描述
图4
点击右上角的VR字样,链接你的Vive设备,然后就可以看到他们的状态了。
这里设备的各种设置方法和使用就不逐个说明讲解了。网上搜索下吧,或去官方最正宗的。

这里写图片描述
图6

当然也可以使用桌面的快捷方式,当然前提是你有:

这里写图片描述
图5

二、制作Demo


首先,打开Unity 导入插件

这里写图片描述
图1

这里写图片描述
图2

然后,可以打开其给点样例来看看:

这里写图片描述
图3

接着就是,添加代码,给Controller添加控制代码:

这里写图片描述
图7


再然后就是需要自己写代码。

三、消息解耦


先说下,这个代码来自于其他同事,我基本没太多修改。但是确实很好用,非常感谢!!若有问题,请及时告知。


消息发送机制:

复制代码
1
2
3
4
5
6
7
8
9
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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
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
namespace SLQJ { /// <summary> /// 消息分发,解耦 /// </summary> public class NotificationManager { public static NotificationManager Instance { get { return SingletonProvider<NotificationManager>.Instance; } } public delegate void MsgCallback(MessageObject eb); /// <summary> /// 回调队列 /// </summary> private Dictionary<string, List<MsgCallback>> registedCallbacks = new Dictionary<string, List<MsgCallback>>(); /// <summary> /// 延迟消息队列 /// </summary> private readonly List<MessageObject> delayedNotifyMsgs = new List<MessageObject>(); /// <summary> /// 主消息队列 /// </summary> private readonly List<MessageObject> realCallbacks = new List<MessageObject>(); private static bool isInCalling = false; public void Init() { } public void Update() { lock (this) { if (realCallbacks.Count == 0) { //主消息隊列處理完時,加入延時消息到主消息列表 foreach (MessageObject eb in delayedNotifyMsgs) { realCallbacks.Add(eb); } delayedNotifyMsgs.Clear(); return; } //調用主消息處理隊列 isInCalling = true; foreach (MessageObject eb in realCallbacks) { if (registedCallbacks.ContainsKey(eb.MsgName)) { for (int i = 0; i < registedCallbacks[eb.MsgName].Count; i++) { MsgCallback ecb = registedCallbacks[eb.MsgName][i]; if (ecb == null) { continue; } #if UNITY_EDITOR ecb(eb); #else try { ecb(eb); } catch (Exception e) { Debug.LogError("CallbackError:" + eb.MsgName + " : " + e.ToString()); } #endif } } else { Debug.Log("MSG_ALREADY_DELETED:" + eb.MsgName); } } realCallbacks.Clear(); } isInCalling = false; } public void Reset() { Dictionary<string, List<MsgCallback>> systemMsg = new Dictionary<string, List<MsgCallback>>(); foreach (KeyValuePair<string, List<MsgCallback>> item in this.registedCallbacks) { if (item.Key.StartsWith("_")) { systemMsg.Add(item.Key, item.Value); } } this.registedCallbacks = systemMsg; } public void Destroy() { Reset(); } /// <summary> /// 订阅消息 /// </summary> /// <param name="msgName"></param> /// <param name="msgCallback"></param> public void Subscribe(string msgName, MsgCallback msgCallback) { lock (this) { if (!registedCallbacks.ContainsKey(msgName)) { registedCallbacks.Add(msgName, new List<MsgCallback>()); } { //防止重复订阅消息回调 List<MsgCallback> list = registedCallbacks[msgName]; for (int i = 0; i < list.Count; i++) { if (list[i].Equals(msgCallback)) { return; } } list.Add(msgCallback); } } } /// <summary> /// 取消订阅 /// </summary> /// <param name="msgName"></param> /// <param name="msgCallback"></param> public void UnSubscribe(string msgName, MsgCallback msgCallback) { lock (this) { if (!registedCallbacks.ContainsKey(msgName)) { return; } //Debug.Log(msgName + ":-s-" + registedCallbacks[msgName].Count); registedCallbacks[msgName].Remove(msgCallback); //Debug.Log(msgName + ":-e-" + registedCallbacks[msgName].Count); } } public void PrintMsg() { string content = ""; foreach (KeyValuePair<string, List<MsgCallback>> registedCallback in registedCallbacks) { int total = registedCallback.Value.Count; if (total > 0) { content += registedCallback.Key + ":" + total + "n"; for (int i = 0; i < total; i++) { content += "t" + registedCallback.Value[i].Method.Name + "--" + registedCallback.Value[i].Target + "n"; } } } } /// <summary> /// 派发消息 /// </summary> /// <param name="MsgName"></param> /// <param name="MsgParam"></param> public void Notify(string MsgName, params object[] MsgParam) { object msgValueParam = null; if (MsgParam != null) { if (MsgParam.Length == 1) { msgValueParam = MsgParam[0]; } else { msgValueParam = MsgParam; } } lock (this) { if (!registedCallbacks.ContainsKey(MsgName)) { return; } if (isInCalling) { delayedNotifyMsgs.Add(new MessageObject(MsgName, msgValueParam)); } else { realCallbacks.Add(new MessageObject(MsgName, msgValueParam)); } } } } public class MessageObject { public object MsgValue; public string MsgName; public MessageObject() { MsgName = this.GetType().FullName; } public MessageObject(string msgName, object ev) { MsgValue = ev; MsgName = msgName; } } }

你可以看到原著者写的还是很严谨的,使用消息队列来实现的,然后在unity某组件的Update中实现轮询调用。

先看看这个消息机制的启动,特别简单:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class main : MonoBehaviour { // Use this for initialization void Awake () { NotificationManager.Instance.Init(); } // Update is called once per frame void Update () { NotificationManager.Instance.Update(); } }


与上面说的一模一样,初始化,然后update。

至于说机制怎么用,这个在后面会接实战给出。

四、手柄Controller消息触发


手柄的事件很多,我就捡了几个常用的来做个例子来说明问题,若需要,你们自己可以来添加自己的需要。

复制代码
1
2
3
4
5
6
7
8
9
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
/// <summary> /// 可以自定義添加事件,然後實現消息的傳遞。 /// </summary> // 實現手柄的案件事件功能 public class ViveEvent : MonoBehaviour { void Start() { var trackedController = GetComponent<SteamVR_TrackedController>(); if (trackedController == null) { trackedController = gameObject.AddComponent<SteamVR_TrackedController>(); } trackedController.TriggerClicked += new ClickedEventHandler(OnTriggerClicked); trackedController.TriggerPressDown += new ClickedEventHandler(OnTriggerPressDn); trackedController.TriggerUnclicked += new ClickedEventHandler(OnTriggerUnclicked); trackedController.PadClicked += new ClickedEventHandler(OnPadClicked); trackedController.PadUnclicked += new ClickedEventHandler(OnPadUnclicked); } void OnTriggerClicked(object sender, ClickedEventArgs e) { Debug.Log(e.controllerIndex + "trigger clicked"); // 开火 NotificationManager.Instance.Notify(NotificationType.Gun_Fire.ToString()); } void OnTriggerPressDn(object sender, ClickedEventArgs e) { Debug.Log(e.controllerIndex + "trigger press down"); // NotificationManager.Instance.Notify(NotificationType.Gathering_Stength.ToString()); } void OnTriggerUnclicked(object sender, ClickedEventArgs e) { Debug.Log(e.controllerIndex + "trigger unclicked"); NotificationManager.Instance.Notify(NotificationType.Gun_KeyUp.ToString()); } void OnPadClicked(object sender, ClickedEventArgs e) { // 扔雷 NotificationManager.Instance.Notify(NotificationType.Throw_Bomb.ToString()); Debug.Log(e.controllerIndex + "pad clicked"); } void OnPadUnclicked(object sender, ClickedEventArgs e) { Debug.Log(e.controllerIndex + "padd un clicked"); } }


主要写了按键Trigger 按下,按住和弹起和Pad的按下和弹起事件。

然后是触发事件的接受,这里就体现了解耦事件的好处。这里真的不止于使用在vive按键处理这里。

复制代码
1
2
3
4
5
6
7
8
9
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
public class ControlButtonAns : MonoBehaviour { // Use this for initialization void Start() { NotificationManager.Instance.Subscribe(NotificationType.Gun_Fire.ToString(), GunFire); NotificationManager.Instance.Subscribe(NotificationType.Gathering_Stength.ToString(), GatheringStength); NotificationManager.Instance.Subscribe(NotificationType.Throw_Bomb.ToString(), ThrowBomb); NotificationManager.Instance.Subscribe(NotificationType.Gun_KeyUp.ToString(), GunKeyUp); } void GunFire(MessageObject obj) { Debug.Log("response gun fire , trigger button click"); } void GatheringStength(MessageObject obj) { Debug.Log("response gathering stength, trigger button hold"); } void GunKeyUp(MessageObject obj) { Debug.Log("response key up, trigger button unclicked"); } void ThrowBomb(MessageObject obj) { Debug.Log("response throw bomb , pad button click"); } }


这个就根据个人的需要来添加自己的代码。这里仅仅是举例说明。

代码写完了,添加吧!!

手柄contorller接受事件:

这里写图片描述
图7.1

消息触发解耦代码:

这里写图片描述
图7.2

相应消息脚本:

这里写图片描述
图7.3

这样基本就搞定了。

五、结果


一图胜千言:

这里写图片描述
图8

就这样。

六、下载地址


工程下载地址:github

https://github.com/cartzhang/ImgSayVRabc/tree/master/ViveEventDemo

steam 插件工程导出地址:

https://github.com/cartzhang/ImgSayVRabc/blob/master/ViveEventDemo/SteamViveControllerEventDemoCartzhang.unitypackage

2017-01-09更新,给事件添加触发手柄ID。
https://github.com/cartzhang/ImgSayVRabc/blob/master/ViveEventDemo/HTVVive_event_add_controller_inedex%20_Cartzhang.unitypackage

七、参考

[1] http://www.cnblogs.com/czaoth/p/5610883.html

[2] http://www.htc.com/managed-assets/shared/desktop/vive/Vive_PRE_User_Guide.pdf

[3] http://blog.csdn.net/qiaochaoqc/article/details/52086790

最后

以上就是隐形大树最近收集整理的关于《图说VR》——HTC Vive控制器按键事件解耦使用的全部内容,更多相关《图说VR》——HTC内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(53)

评论列表共有 0 条评论

立即
投稿
返回
顶部