博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
《图说VR》——HTC Vive控制器按键事件解耦使用
阅读量:6862 次
发布时间:2019-06-26

本文共 7604 字,大约阅读时间需要 25 分钟。

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

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

项目的參考图片可下载地址:
今天我们说谈论的就是以下这个东西:

这里写图片描写叙述

图1.1

一、所需资源

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

这里写图片描写叙述

图0
可是电脑还是须要安装steam的。这个临时还是须要FQ的。

你懂的。FQ是一项技能。

安装后:

这里写图片描写叙述

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

这里写图片描写叙述

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

这里写图片描写叙述

图5

二、制作Demo

首先。打开Unity 导入插件

这里写图片描写叙述

图1

这里写图片描写叙述

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

这里写图片描写叙述

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

这里写图片描写叙述

图7

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

三、消息解耦

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

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

消息发送机制:

namespace SLQJ{    ///     /// 消息分发,解耦    ///     public class NotificationManager    {        public static NotificationManager Instance { get { return SingletonProvider
.Instance; } } public delegate void MsgCallback(MessageObject eb); ///
/// 回调队列 /// private Dictionary
> registedCallbacks = new Dictionary
>(); ///
/// 延迟消息队列 /// private readonly List
delayedNotifyMsgs = new List
(); ///
/// 主消息队列 /// private readonly List
realCallbacks = new List
(); 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
> systemMsg = new Dictionary
>(); foreach (KeyValuePair
> item in this.registedCallbacks) { if (item.Key.StartsWith("_")) { systemMsg.Add(item.Key, item.Value); } } this.registedCallbacks = systemMsg; } public void Destroy() { Reset(); } ///
/// 订阅消息 /// ///
///
public void Subscribe(string msgName, MsgCallback msgCallback) { lock (this) { if (!registedCallbacks.ContainsKey(msgName)) { registedCallbacks.Add(msgName, new List
()); } { //防止反复订阅消息回调 List
list = registedCallbacks[msgName]; for (int i = 0; i < list.Count; i++) { if (list[i].Equals(msgCallback)) { return; } } list.Add(msgCallback); } } } ///
/// 取消订阅 /// ///
///
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
> 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"; } } } } ///
/// 派发消息 /// ///
///
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消息触发

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

/// /// 能够自定義加入事件,然後實現消息的傳遞。/// // 實現手柄的案件事件功能public class ViveEvent : MonoBehaviour{    void Start()    {        var trackedController = GetComponent
(); if (trackedController == null) { trackedController = gameObject.AddComponent
(); } 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接受事件:

这里写图片描写叙述

图7.1
消息触发解耦代码:

这里写图片描写叙述

图7.2
对应消息脚本:

这里写图片描写叙述

图7.3
这样基本就搞定了。

五、结果

一图胜千言:

这里写图片描写叙述

图8
就这样。

六、下载地址

project下载地址:github

steam 插件project导出地址:

2017-01-09更新,给事件加入触发手柄ID。

七、參考

[1]

[2]

[3]

你可能感兴趣的文章
每天一个linux命令(6):dos2unix unix2dos
查看>>
ObjectQuery查询及方法
查看>>
使用jemeter手工编写注册、登陆脚本 运用 fiddler (三)
查看>>
uva 10288 Coupons (分数模板)
查看>>
使用docker的kms服务器激活office2016专业增强版
查看>>
Redis
查看>>
程序员需要淡定
查看>>
大整数算法[11] Karatsuba乘法
查看>>
为什么可以用while(cin)?
查看>>
Cisco 交换机与路由器故障处理方法分享
查看>>
linux运行级别
查看>>
Debian(Linux)+XAMPP(LAMPP)+Zend Studio + PHP +XDebug 完整的开发环境配置方法。
查看>>
Python字符集编码和文件读写 [转]
查看>>
COGS728. [网络流24题] 最小路径覆盖问题
查看>>
Python----切片
查看>>
处理浏览器兼容性(持续更新)
查看>>
常用数据库 JDBC URL 格式
查看>>
c#在不安装Oracle客户端的情况下与服务器上的Oracle数据库交互
查看>>
Java回环变位和倒置字符串
查看>>
前景检测算法_1(codebook和平均背景法)
查看>>