• 《图说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商店下载。当然你能够使用文章后面给出本project的导出包,文章后面有下载地址:

    这里写图片描写叙述
    图0

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

    你懂的。翻墙是一项技能。
    安装后:

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

    这里写图片描写叙述
    图6

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

    这里写图片描写叙述
    图5

    二、制作Demo


    首先。打开Unity 导入插件

    这里写图片描写叙述
    图1

    这里写图片描写叙述
    图2

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

    这里写图片描写叙述
    图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 + "
    ";
                        for (int i = 0; i < total; i++)
                        {
                            content += "	" + registedCallback.Value[i].Method.Name + "--" + registedCallback.Value[i].Target + "
    ";
                        }
                    }
                }
            }
    
            /// <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接受事件:

    这里写图片描写叙述
    图7.1

    消息触发解耦代码:

    这里写图片描写叙述
    图7.2

    对应消息脚本:

    这里写图片描写叙述
    图7.3

    这样基本就搞定了。

    五、结果


    一图胜千言:

    这里写图片描写叙述
    图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

  • 相关阅读:
    golang 常见疑惑总结
    golang 详解defer
    golang调试工具Delve
    ACE的源码划分
    通过#define连接字符串的特殊方法[转]
    转:extern "C"的用法解析
    转:3d max 2013 安装教程,凭着一种互联网精神提供给广大朋友
    转:Bullet物理引擎不完全指南(Bullet Physics Engine not complete Guide)
    转:折腾一晚上Bullet及Ogre相关工具的成果 -- 3Ds Max,Maya, blender, GameKit
    转:CMake快速入门教程-实战
  • 原文地址:https://www.cnblogs.com/zhchoutai/p/7352747.html
Copyright © 2020-2023  润新知