《图说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

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

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

图1.1

一、所需资源

所需资源,非常少。

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

图0

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

你懂的。FQ是一项技能。

安装后:

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

时间: 2024-10-11 07:16:01

《图说VR》——HTC Vive控制器按键事件解耦使用的相关文章

Unity HTC VIVE手柄 按键说明

一.HTC VIVE手柄按键图说明 1 - 菜单键 6 - 系统键(按下后手柄断开连接,再次按下手柄再次连接上) 7 - 扳机键 8 - 握持键 9 - 触摸板键 二.按键的监听方式 1.按键监听方式一:(SteamVR的方式,封装了OpenVR) (1)点击触发:通过Device.GetPressDown / GetPressUp / GetPress获取按键事件 (2)触摸触发:通过device .GetTouchDown / GetTouchUp / GetTouch获取按键事件 2.按键

HTC VIVE生态链揭晓,多款VR大作即将登陆

原文标题:HTC VIVE生态链揭晓,多款VR大作即将登陆   2017世界移动大会上海站正式的落下了帷幕,该会由全球通信标准组织于6月28日至7月1日在上海的新国际博览中心举行. 作为VR头显的重量级企业HTC Vive在展会现场也展示了其在VR生态链的最新研究成果,同时展示了Vive丰富的周边硬件,流行的IP大作,以及各种行业应用范围覆盖娱乐.教育.医疗.体育等垂直领域,为在场的上万名的观众呈现了最前端的科技成果,也为VR产业的发展指明了新的风向. "移动"是MWC的核心主题,Vi

Htc Vive VR 手势识别插件教程 1.1 版本(附1.0版本教程PDF)

VR手势识别插件教程1.1.1版本       VR手势识别插件教程1.1.1版本内容提纲 HtcVive VR手势识别插件教程1.1.1版本..................................................................... 3 0x00 前言....................................................................................................

HTC vive开发:关于手柄按键

一.关于左右手柄的对应关系 两个手柄和SteamVR_TrackedObject.EIndex是对应的,一个是EIndex.Device2,另一个是EIndex.Device3(有编号的那个) 在场景中手柄先后连入,先后激活的是Controller(right).Controller(left),并添加SteamVR_TrackedObject组件 二.手柄按键 1 - 菜单键 6 - 系统键(按下后手柄断开连接,再次按下手柄再次连接上) 7 - 扳机键 8 - 握持键 9 - 触摸板键 (1)

HTC Vive Unity 教程

原文:HTC Vive Tutorial for Unity 作者:Eric Van de Kerckhove 译者:kmyhy HTC Vive 是一个虚拟现实头盔,由 HTC 和 Valve 公司制造.它提供一种在虚拟世界中的浸入式体验,而不是屏幕头像. 如果你是一个 Unity 开发者,在虚拟现实游戏中使用 HTC Vive 非常简单--你可以认为 HTC Vive 和 Unity 是天生一对. 在这篇 HTC Vive 教程中,你会学习如何在 Unity 游戏中集成 HTC Vive.包

理解HTC Vive更新——控制相机旋转和位移

本文章由cartzhang编写,转载请注明出处. 所有权利保留. 文章链接:http://blog.csdn.net/cartzhang/article/details/72188658 作者:cartzhang 一.写在前面 在HTC的vive 头盔中, 一旦Vive头盔连接都unity游戏中,就会控制所有Camera的旋转和位置. 这对于有需要的控制非头盔相机带来了烦恼. 比方说,上篇博客中,在VR中,对某个特点位置截图,就会由于头盔控制所有相机的旋转, 造成截图不精确和出现偏移. 地址:

HTC Vive会是HTC的下一个增长引擎吗?

2014年初,Facebook以20亿美元收购虚拟现实技术公司Oculus的大手笔点燃了世界对虚拟现实的热情.时至今日,虚拟现实玩家已经不仅仅是Facebook.Facebook之外,微软.谷歌.HTC.三星.索尼等国际巨头公司均已推出自己的虚拟现实设备. 而据美国主流网络新闻媒体BusinessInsider旗下的市场研究咨询部"BusinessInsider-Intelligence"日前发布的虚拟现实市场的研究预测报告显示,未来五年,虚拟现实头盔市场,将每年增长一倍,另外在游戏之

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

http://blog.csdn.net/ystistheking/article/details/51553237 想交流的朋友我们可以微博互粉,我的微博黑石铸造厂厂长 ,缺粉丝啊 .....求粉求粉 研究了几天htc vive的接口,总算是把基本的按键功能研究出来了,这里分享一下,一来当做笔记,二来也希望对大家有所帮助. 如何导入Steam_VR那个包什么的我就不说了,网上有几个前辈已经教了,蛮牛论坛啥的上面都有,这里只把比较详细的按键功能分享一下,不知啥高端的东西,也算一段时间劳动成果啦,

HTC VIVE

HTC VIVE HTC Vive是一款最具有划时代意义.让人惊叹的产品. HTC Vive 由 HTC 与 Valve 联合开发. 著名游戏公司 Valve给予 HTC 相关的技术支持,而由HTC研发硬件和组装销售      主体芯片包括了核心处理芯片.图形处理 SoC.USB 控制器.图传接口控制器以及闪存芯片等. 这块主板上拥有一些六轴 MEMS 动作追踪芯片等传感器. HTC Vive 的显示屏幕使用的是两块三星供应的AMOLED 屏幕面板,统一规格,分辨率为2,160×1,200,刷新