本文章由cartzhang编写,转载请注明出处。 全部权利保留。

文章链接:http://blog.csdn.net/cartzhang/article/details/53915229

作者:cartzhang

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

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



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

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY2FydHpoYW5n/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="这里写图片描写叙述" title="">

图1.1

一、所需资源

所需资源,非常少。

须要用Steam VR插件 ,能够从Untiy商店下载。当然你能够使用文章后面给出本project的导出包,文章后面有下载地址:



图0



可是电脑还是须要安装steam的。这个临时还是须要翻墙的。

你懂的。翻墙是一项技能。

安装后:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY2FydHpoYW5n/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="这里写图片描写叙述" title="">

图4

点击右上角的VR字样。链接你的Vive设备,然后就能够看到他们的状态了。

这里设备的各种设置方法和使用就不逐个说明解说了。网上搜索下吧,或去官方最正宗的。



图6



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



图5

二、制作Demo

首先。打开Unity 导入插件



图1



图2



然后。能够打开其给点例子来看看:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY2FydHpoYW5n/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="这里写图片描写叙述" title="">

图3



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



图7

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

三、消息解耦

先说下。这个代码来自于其它同事。我基本没太多改动。可是确实非常好用,非常感谢!

。若有问题,请及时告知。

消息发送机制:


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中实现轮询调用。

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

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消息触发

手柄的事件非常多。我就捡了几个经常使用的来做个例子来说明问题,若须要。你们自己能够来加入自己的须要。


/// <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按键处理这里。


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接受事件:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY2FydHpoYW5n/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="这里写图片描写叙述" title="">

图7.1



消息触发解耦代码:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY2FydHpoYW5n/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="这里写图片描写叙述" title="">

图7.2



对应消息脚本:



图7.3



这样基本就搞定了。

五、结果

一图胜千言:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY2FydHpoYW5n/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="这里写图片描写叙述" title="">

图8



就这样。

六、下载地址

project下载地址:github

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



steam 插件project导出地址:

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控制器按键事件解耦使用的更多相关文章

  1. 《图说VR入门》——入门汇总

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

  2. 用 Unity 和 HTC Vive 实现高级 VR 机制(1)

    原文:Advanced VR Mechanics With Unity and the HTC Vive Part 1 作者:Eric Van de Kerckhove 译者:kmyhy VR 从来没 ...

  3. Unity正式发布首个“实验性”VR编辑器,支持HTC Vive和Oculus Rift

    Unity今天正式推出"实验性"VR编辑器.据悉,EditorVR是Unity游戏引擎中的一个组件,可让开发者在虚拟现实环境中开发游戏.为何要称之为"实验性"? ...

  4. HTC vive VR设备软硬件安装+运行unity开发的VR程序

    总结在HTC vive VR开发过程中的HTC vive的安装调试 1.首先确保电脑的配置满足要求: 进入官网,测试电脑是否满足要求 链接:https://www.vive.com/us/produc ...

  5. HTC vive开发:关于手柄按键

    一.关于左右手柄的对应关系 两个手柄和SteamVR_TrackedObject.EIndex是对应的,一个是EIndex.Device2,另一个是EIndex.Device3(有编号的那个) 在场景 ...

  6. 头显HTC Vive北美直降100美元,中国区降价活动今日公布

    如果你现在想要购买一台VR头显,591ARVR资讯网www.591arvr.com的小编提醒大家可以等一等,在即将到来的年末促销中各种VR设备都将迎来大力度降价.目前北美市场的HTC Vive已经直降 ...

  7. HTC Vive开发笔记之SteamVR插件集成

    重要组件 SteamVR_Camera VR摄像机,主要功能是将Unity摄像机的画面进行变化,形成Vive中的成像画面 使用方法: l 在任一个摄像机上增加脚本 l 点击Expand按钮 完成以上操 ...

  8. Unity的HTC VIVE SDK研究(手柄按键功能的研究,比较详细)

    http://blog.csdn.net/ystistheking/article/details/51553237 想交流的朋友我们可以微博互粉,我的微博黑石铸造厂厂长 ,缺粉丝啊 .....求粉求 ...

  9. HTC Vive 基础入门 基于Unreal Engine 4引擎

    主要以讲解介绍HTC Vive设备以及Unreal继承的Steam VR Plugin为主 使用最新的虚幻引擎与Plugin完成VR环境的搭建 然后完成一个基本的VR Games. 任务5: 04-配 ...

随机推荐

  1. 原生js大总结七

    061.如何获取父级节点.上一个子级节点.下一个子级节点    nextElementSibling  后一个兄弟元素  (如果没有是null)    previousElementSibling   ...

  2. 位数(digits)的处理

    主要针对:二进制表示法,以及十进制表示法: 1. 获取位数 已知该数 n 采用十进制进行表示 二进制形式的位数:⌊log2n⌋+1 十进制形式的位数:⌊log10n⌋+1 2. 截断(保留前/后 m ...

  3. 如何在anaconda中切换python2

    如果你不切换可能是默认的python3环境. 下面是在python27版本下下载qt5

  4. Android Studio插件推荐-GsonFormat,ButterKnifeZelezny

    原创文章.转载请注明 http://blog.csdn.net/leejizhou/article/details/50557786 本篇介绍的仅仅适用android studio和 Intellij ...

  5. Vue.js组件的重要选项

    Vue.js组件的重要选项 实例化Vue对象一些很重要的选项,Vue的所有数据都是放在data里面的,Vue的参数是个对象,对象里面的字段叫做data,data里面也是对象,data也可以写作是thi ...

  6. if..... if..... 和if..... else if.....

    曾经一度认为没有区别,,在有的时候是没有区别的,,但是有些时候则不可相互替换 这两个是有区别的 if..... if..... 是不相关的.只要各自判断两部分的条件即可,两个都会执行 if.... e ...

  7. python3 序列

    python中有很多内置序列 列表 元组 字符串 python中容器的概念 列表 元组 字符串 字典 集合 是可以改变的,元组不可改变 几乎可以在所有情况下用列表代替元组,只有一种情况下,是不可以的, ...

  8. Maven项目中mvn clean后找不到測试类问题

    在Maven项目中进行单元測试,但mvn clean后又一次mvn install项目,再次进行单元測试.会有下面的错误. <span style="font-family:KaiTi ...

  9. Helloworld之Spring依赖注入/控制反转(DI/IoC)版

    Helloworld之Spring依赖注入/控制反转(DI/IoC)版 作者:雨水, 日期:2014-10-29 摘要:本文主要用于培训刚開始学习的人理解Spring中的依赖注入的基本概念. 先介绍依 ...

  10. linux下FAT32格式u盘只读的问题及解决方法

    以下是网上看到的解决办法:http://blog.csdn.net/heqiuya/article/details/7870554 其实是掉电保护,之前挂在的SD变成了制度文件,只需要将SD卡重新挂载 ...