• Unity 为NGUI增加体感输入方式


    背景

    NGUI在处理UI和输入方面确实做的不错,但是现在的问题是公司引入体感之后,是通过手的位置来实现按钮的点击操作,前提我不想改变原先设计好的NGUI界面和机制,怎么破?

    image

    NGUI的输入底层机制

    NGUI对鼠标或者触摸的位置是通过Camera对NGUI层进行射线检测来获得,然后检测按钮事件、触摸屏Press事件来实现UI的操作,从事件机制上而言,NGUI虽然提供了几种事件机制,但底层还是通过Camera的SendMessage来通知被检测到的控件完成某个事件,OK ,看看代码。

    NGUI UICamera中进行射线检测的片段 ,利用当前坐标位置转换到世界坐标系射线

    static public bool Raycast (Vector3 inPos)
        {
            for (int i = 0; i < list.size; ++i)
            {
                UICamera cam = list.buffer[i];
                
                // Skip inactive scripts
                if (!cam.enabled || !NGUITools.GetActive(cam.gameObject)) continue;
    
                // Convert to view space
                currentCamera = cam.cachedCamera;
                Vector3 pos = currentCamera.ScreenToViewportPoint(inPos);
                ..
    ..
            }
         }

     NGUI UICamera 中ProcessMouse()方法利用射线检测当前响应的按钮,并通过Notify()通知控件。

    // No need to perform raycasts every frame
            if (isPressed || posChanged || mNextRaycast < RealTime.time)
            {
                mNextRaycast = RealTime.time + 0.02f;
                if (!Raycast(Input.mousePosition)) hoveredObject = fallThrough;
                if (hoveredObject == null) hoveredObject = genericEventHandler;
                for (int i = 0; i < 3; ++i) mMouse[i].current = hoveredObject;
            }
    
    // The button was released over a different object -- remove the highlight from the previous
            if ((justPressed || !isPressed) && mHover != null && highlightChanged)
            {
                currentScheme = ControlScheme.Mouse;
                if (mTooltip != null) ShowTooltip(false);
                Notify(mHover, "OnHover", false);
                mHover = null;
            }

    NGUI UICamera中封装的通知方法,Camera通过逻辑判断发送不同类型,如在点击的时候发送 :Notify(currentTouch.pressed, "OnClick", null);,在按下时候发送:Notify(currentTouch.current, "OnHover", true);

    static public void Notify (GameObject go, string funcName, object obj)
        {
            if (mNotifying) return;
            mNotifying = true;
    
            if (NGUITools.GetActive(go))
            {
                go.SendMessage(funcName, obj, SendMessageOptions.DontRequireReceiver);
    
                if (genericEventHandler != null && genericEventHandler != go)
                {
                    genericEventHandler.SendMessage(funcName, obj, SendMessageOptions.DontRequireReceiver);
                }
            }
            mNotifying = false;
        }

    NGUI 中 UIButton中Onclick处理OnClick ()与OnDragOver()事件,是对Notify(currentTouch.pressed, "OnClick", null)的响应,同时通过EventDelegate.Execute(onClick); 来实现委托。

    /// <summary>
        /// Call the listener function.
        /// </summary>
    
        protected virtual void OnClick ()
        {
            if (current == null && isEnabled)
            {
                current = this;
                EventDelegate.Execute(onClick);
                current = null;
            }
        }

    OK, 说到底,NGUI底层还是通过SendMessage来实现,那增加一种输入方式怎么破?

    解决方案:引入新的射线检测

    好的方式是直接修改NGUI底层UICamera代码逻辑,增加一种体感输入,不过涉及到的太多,倒不如再UIROOT或者UICamera增加一个专门用于体感输入方法,把手的位置作为鼠标,增加一个对NGUI层的射线检测机制,对检测到的按钮发送SendMessage消息,当然发送的内容和NGUI中的一样,就可以保证不修改NGUI的UI脚本等等,实现体感输入。代码示例:

    /// <summary>
        /// 作者:细雨淅淅,地址:http://www.cnblogs.com/zsb517/
        /// 摄像机射线检测,对检测到的控件重新设置状态,但不影响鼠标状态
        /// </summary>
        private void OnRayCollision()
        {
    
            if (UICamera == null || CursorGrp == null)
            {
                return;
            }
    
            Vector3 realPos     = ScreentoWorldPoint();
            Ray     rayCamera   = UICamera.ScreenPointToRay(realPos);
            RaycastHit hit;
    
            if (Physics.Raycast(rayCamera, out hit, 1000f, LayMaskCollis.value))
            {
                if (hit.collider == null || hit.collider.gameObject == null)
                {
                    return ;
                }
    
                if (curButton == null )
                {
                   // Debug.Log(hit.collider.name);
                    curButton = hit.collider.gameObject.GetComponent<UIButton>();
                    if (curButton != null)
                    {
                        curButton.SetState(UIButtonColor.State.Hover,false);
                      //  curButton.SendMessage("OnHover", true, SendMessageOptions.DontRequireReceiver);
                    }
    
                    return;
                }
                else if (curButton != null)
                {
                    if (curButton != hit.transform.gameObject.GetComponent<UIButton>())
                    {
                        //Restore previous button ,and make new button to hover status
                        //curButton.SendMessage("OnPress", false, SendMessageOptions.DontRequireReceiver);
                        curButton.SetState(UIButtonColor.State.Normal, false);
                        curButton = hit.transform.gameObject.GetComponent<UIButton>();
                        if (curButton)
                        {
                            curButton.SetState(UIButtonColor.State.Hover, false);
                            //curButton.SendMessage("OnHover", true, SendMessageOptions.DontRequireReceiver);
                        }
    
                        return;
                    }
                    else
                    {
    
                    }
                }
            }
            else
            {
                if (curButton != null)
                {
                    curButton.SetState(UIButtonColor.State.Hover, false);
    
                    //curButton.SendMessage("OnPress", false, SendMessageOptions.DontRequireReceiver);
                    //curButton.SendMessage("OnHover", false, SendMessageOptions.DontRequireReceiver);
                    curButton = null;
                    return;
                }
            }
    
            OnReset();
        }
    
        private Vector3 ScreentoWorldPoint()
        {
            Vector3 wPos = Vector3.zero;
            if (IsMouseOrKinect)
            {
                wPos = new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0);
            }
            else
            {
                if (NIHandInput.GetInstance() != null)
                {
                    Vector2 pos = NIHandInput.GetInstance().ScreenPos;  //体感输入中手的位置
                    wPos = new Vector3(pos.x, pos.y, 0);
                }
            }
    
            Debuger.Log(wPos);
            return wPos;
        }

    结论

    对于输入问题,Unity Input自身还提供了一种机制,不过没做太多研究,但是想把各种输入柔和在一起,确实是一件很纠结的事情,还要多考虑,不仅仅是代码问题,而是改变了整个游戏的体验。

    细雨标记:
  • 相关阅读:
    岛田庄司《占星术杀人魔法》读后感
    OutputCache祥解
    ZOJ Monthly, June 2014 月赛BCDEFGH题题解
    接口和抽象类有什么差别
    C语言指针的初始化和赋值
    深入探讨this指针
    郁 繁体为“鬰” 古同 “鬱”
    socketpair的使用
    Android的FrameLayout使用要注意的问题
    下确界和上确界
  • 原文地址:https://www.cnblogs.com/zsb517/p/4126216.html
Copyright © 2020-2023  润新知