1. 程式人生 > >LEAPMOTION開發UI專題(2)

LEAPMOTION開發UI專題(2)

過了這麼久才來更這篇實在是因為專案工程量實在是不允許

首先宣告我並不是專業的UI設計人員 我們所有的leap UI設計全部來源與專案需求 且因為專案不是商業專案 所以設計方法看起來有一種“邪門歪道”的既視感 但是為VR/AR環境中的互動設計提供了一種思路

特別提示:以下內容並不是針對初學者而言的

如果你是個unity/leap開發的雙面小白 那麼基本可以不用看這篇文章了

整個專案的效果可以去這裡看視訊

OK~

言歸正傳

我們先來看看效果:

sence2

這是一個用手勢控制選擇的介面選擇的時候會根據手勢的揮動方向來進行左右切換
也就是我們上一篇文章中提到的手勢操作互動,這類操作不與UI元素進行交運算(觸發)
所以設計起來相對獨立 也就是說 這類UI元素的製作可以按照普通的UI製作方式來進行

所以 在整個軟體中 這個UI顯得最普通 但是就是普通依舊不是那麼好做

想實現一個類似IPhone音樂的選擇介面並不是那麼容易
像這樣


這種動態效果通過靜態圖片無法很好地展示 但是想試想一個細微的效果實際上需要大量的工作

這個UI整個比較複雜所以我只能不太完整的去講解
目錄
這是它的目錄結構 在某一個版本中 我曾經寫了動態生成這些展示標記 但是由於數量變化會引起後面的縮放係數bug 我就改回了手動新增
text
這是每個UI元素的組成 其中有些元件是不必要的主要是為了原先滑鼠操作而設計
( 後來整體取消了軟體滑鼠操作的功能 但保留了這些元件 比如碰撞器 對於一個手勢操縱的UI型別來說完全是沒必要的)

所以

針對這個UI元素來說 可講的就只剩下

1.怎麼細緻的實現元素轉動效果
2.怎麼用手勢控制旋轉

一,轉動效果 這其實很多demo裡面都會出現 而這個實現的那我們來看看主要的結構
這裡寫圖片描述
命名清晰明瞭 enhance控制器 控制 gameobject(UItexture)
UItexture下放了一個canvas 然後canvas上有三個text元件放置三組文字
sprite是背景控制(這裡的sprite其實就是擷取圖形的外掛)
有了六七列表我們就可以在控制器裡通過程式碼來控制了

這裡寫圖片描述

有兩個類一個 EnhancelScrollView一個 EnhanceItem
首先定義 EnhanceItem指令碼附在每個UItexture上面 設定flag

using UnityEngine;
using System.Collections;

public class EnhanceItem : MonoBehaviour
{

    // 在ScrollViewitem中的索引
    internal int scrollViewItemIndex = 0;

    // 夾角大小
    internal float angla = 0f;
    // 動畫時間值
    internal float dValueTime = 0f;

    // 前後項
    internal EnhanceItem front, back;



    /*
     * 
     * 
     * 
     * 
     *   internal關鍵字是型別和型別成員的訪問修飾符。只有在同一個程式集的檔案中,內部型別或者是成員才可以訪問。
     * 這是msdn上對internal的描述。
     * 型別就是enum(列舉型別),class(類),interface(介面),struct(結構)等型別。
     * 型別成員如函式,成員變數等。
     * 
     * 一個完整的.exe或者是.dll檔案就是一個程式集,一般伴隨著exe程式集產生的還有一個程式集清單
     * ,.exe.config檔案。下面我就用一個例子來說明“internal關鍵字是型別和型別成員的訪問修飾符。
     * 只有在同一個程式集的檔案中,內部型別或者是成員才可以訪問”。
     * 

     * 
     */



    public int flag = 777;//flag


    private Vector3 targetPos = Vector3.one;
    private Vector3 targetScale = Vector3.one;

    private Transform mTrs;
    private UITexture mTexture;

    void Awake()
    {
        mTrs = this.transform;
        mTexture = this.GetComponent<UITexture>();
    }

    void Start()
    {
        UIEventListener.Get(this.gameObject).onClick = OnClickScrollViewItem;
    }

    // 當點選Item,將該item移動到中間位置
    private void OnClickScrollViewItem(GameObject obj)
    {
        EnhancelScrollView.GetInstance().SetHorizontalTargetItemIndex(scrollViewItemIndex);
    }

    /// <summary>
    /// 更新該Item的縮放和位移
    /// </summary>
    public void UpdateScrollViewItems(float xValue, float yValue, float scaleValue)
    {
        targetPos.x = xValue;
        targetPos.y = yValue;
        targetScale.x = targetScale.y = scaleValue;

        mTrs.localPosition = targetPos;
        mTrs.localScale = targetScale;
    }

    public void SetSelectColor(bool isCenter)
    {
        if (mTexture == null)
            mTexture = this.GetComponent<UITexture>();

        if (isCenter)
            mTexture.color = Color.white;
        else
            mTexture.color = Color.gray;
    }
}

EnhancelScrollView指令碼作為控制指令碼附著在控制物體上
他就相當於每個物體

那麼手勢控制:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Leap;

// [ExecuteInEditMode]
public class EnhancelScrollView : MonoBehaviour
{
    // 含有滑動專案的面板
    public GameObject enhanceScrollView;

    // 縮放曲線
    public AnimationCurve scaleCurve;
    // 位移曲線
    public AnimationCurve positionCurve;
    // 動畫時間
    public float duration = 0.2f;

    // 寬度
    public float width = 800.0f;
    // y軸座標固定值(所有的item的y座標一致)
    public float yPositionValue = 46.0f;

    // 中間顯示目標時間線(0顯示第一個,0.5顯示中間一個)
    public float horizontalTargetValue = 0.0f;

    // 滑動起始力
    public float touchStartPower = 0.5f;
    // 滑動阻力
    public int touchForce = 120;

    // 目標物件列表
    private List<EnhanceItem> scrollViewItems;
    // 目標物件Widget指令碼,用於depth排序
    private List<UITexture> textureTargets;

    // 開始X座標
    private float startXPos = 0f;
    // 當前處於中間的item
    public EnhanceItem centerItem;
   // 當前出移動中,不能進行點選切換
    private bool isInChange = false;   

    // 位置動畫的中間位置時間
    private float positionCenterTime = 0.5f;
    // 當前精度小數位
    private int curACC = 4;

    // 橫向變數值
    private float horizontalValue = 0.0f;

    // 移動動畫引數
    private float originHorizontalValue = 0.0f;
    private float currentDuration = 0.0f;

    private static EnhancelScrollView instance;
    internal static EnhancelScrollView GetInstance()
    {
        return instance;
    }
     //內部型別或成員才是可訪問的


    //__________________________________________________________
//  動作識別部分定義
    public HandController hc;


    Hand lefthand=null;
    Hand righthand = null;
    Hand last_lefthand = null;
    Hand last_righthand = null;
    Frame currentFrame = null;//定義當前幀


    bool lefthandexist = false;//判斷左右手是否在場景中存在
    bool righthandexist = false;

    float sweptAngle = 0;//初始化角度為零

    int mark=0;//標記  與下面的函式作用
//  0代表不轉動,-1代表向左轉,1代表向右轉
    //____________________________________________________________

    void checkmark(int sign)
    {
        if (sign == 1) {
            SetHorizontalTargetItemIndex(centerItem.front.scrollViewItemIndex);
        }
        if (sign == -1) {
            SetHorizontalTargetItemIndex(centerItem.back.scrollViewItemIndex);
        }
        mark = 0;
    }
    //執行轉動的真正操作  檢查標記從而確保只執行一次否則一次揮動將轉動多次


    void Awake()
    {
        instance = this;
    }

    void Start()
    {

        hc.GetLeapController().EnableGesture(Gesture.GestureType.TYPECIRCLE);
        hc.GetLeapController().EnableGesture(Gesture.GestureType.TYPESWIPE);
        hc.GetLeapController().EnableGesture(Gesture.GestureType.TYPE_SCREEN_TAP);
        hc.GetLeapController ().EnableGesture (Gesture.GestureType.TYPEKEYTAP);
        hc.GetLeapController ().EnableGesture (Gesture.GestureType.TYPEINVALID);
        //開啟所有手勢  其實在這個指令碼中只開啟typeswipe就夠了


//      ____________________________________________


        InitData();//初始化資料

        // 設定第一個為選中狀態
        SetHorizontalTargetItemIndex(0);
    }

    /// <summary>
    /// 初始化資料
    /// </summary>
    private void InitData()
    {
        startXPos = -(width / 2);

        scrollViewItems = new List<EnhanceItem>();
        scrollViewItems.AddRange(enhanceScrollView.GetComponentsInChildren<EnhanceItem>());

        if (textureTargets == null)
            textureTargets = new List<UITexture>();

        float anglaDValue = 360 / scrollViewItems.Count;

        int centerIndex = scrollViewItems.Count / 2;
        for (int i = 0; i < scrollViewItems.Count; i++)
        {
            scrollViewItems[i].scrollViewItemIndex = i;
            scrollViewItems[i].angla = anglaDValue * i;
            scrollViewItems[i].dValueTime = GetCurveTimePos(scrollViewItems[i].angla);

            // 構造環形鏈
            scrollViewItems[i].front = i == 0 ? scrollViewItems[scrollViewItems.Count - 1] : scrollViewItems[i - 1];
            scrollViewItems[i].back = i == (scrollViewItems.Count - 1) ? scrollViewItems[0] : scrollViewItems[i + 1];

            UITexture tmpTexture = scrollViewItems[i].gameObject.GetComponent<UITexture>();
            textureTargets.Add(tmpTexture);

            scrollViewItems[i].SetSelectColor(false);//設為選中狀態 呼叫了enhanceitem中的方法將mtexture中的混合顏色設為白色以顯示高亮
        }
    }
    //_____________________________________________________________________________________________
    void Update()
    {

        if (!isInChange)
        {
            touch();
            return;
        }

        currentDuration += Time.deltaTime;
        float percent = currentDuration / duration;
        horizontalValue = Mathf.Lerp(originHorizontalValue, horizontalTargetValue, percent);
        UpdateEnhanceScrollView(horizontalValue);

        SortDepth();

        if (currentDuration > duration)
        {
            centerItem = textureTargets[textureTargets.Count - 1].gameObject.GetComponent<EnhanceItem>();
            centerItem.SetSelectColor(true);
            isInChange = false;
        }
    }

    /// <summary>
    /// 更新水平滾動
    /// </summary>
    /// <param name="fValue"></param>
    private void UpdateEnhanceScrollView(float fValue)
    {
        for (int i = 0; i < scrollViewItems.Count; i++)
        {
            EnhanceItem itemScript = scrollViewItems[i];
            float xValue = GetXPosValue(fValue, itemScript.dValueTime);
            float scaleValue = GetScaleValue(fValue, itemScript.dValueTime);

            itemScript.UpdateScrollViewItems(xValue, yPositionValue, scaleValue);
        }
    }

    //滑動X軸增量位置
    float xMoved;
    private void touch()
    {
        // 記錄滑動位置
        if (Input.touchCount == 1 && Input.GetTouch(0).phase == TouchPhase.Moved)
        {
            //獲取手指自最後一幀的移動
            float x = Input.GetTouch(0).deltaPosition.x;
            xMoved = x;
        }

        // 滑動結束時判斷故事翻頁
        if (Input.touchCount == 1 && Input.GetTouch(0).phase == TouchPhase.Ended)
        {
            if (centerItem == null || Mathf.Abs(xMoved) < touchStartPower)
                return;

            int count = (int)(Mathf.Abs(xMoved * scrollViewItems.Count/ touchForce)) + 1;   
            int minHalfCount = Mathf.CeilToInt((float)scrollViewItems.Count / 2) - 1;
            if (count > minHalfCount)
            {
                count = minHalfCount;
            }

            if(xMoved > 0)
            {
                SetHorizontalTargetItemIndex(GetMoveIndex(centerItem, -count));
            }
            else if (xMoved < 0)
            {
                SetHorizontalTargetItemIndex(GetMoveIndex(centerItem, count));
            }
            xMoved = 0;
        }
    }

    /// <summary>
    /// 縮放曲線模擬當前縮放值
    /// </summary>
    private float GetScaleValue(float sliderValue, float added)
    {
        float scaleValue = scaleCurve.Evaluate(positionCenterTime + sliderValue + added);
        return scaleValue;
    }

    /// <summary>
    /// 位置曲線模擬當前x軸位置
    /// </summary>
    private float GetXPosValue(float sliderValue, float added)
    {
        float evaluateValue = startXPos + positionCurve.Evaluate(positionCenterTime + sliderValue + added) * width;
        return evaluateValue;
    }

    /// <summary>
    /// 計算位置動畫中的時間點
    /// </summary>
    /// <param name="anga">角度值,360度=1</param>
    /// <returns></returns>
    private float GetCurveTimePos(float anga)
    {
        // 設定0.5為位置中間
        return Round(anga / 360f, curACC);
    }

    // 獲取專案A到專案B之間最小的時間差值(圓形角度計算,1=360度)
    private float GetCurveTimeDValue(EnhanceItem itemA, EnhanceItem itemB)
    {
        return Round((Mathf.DeltaAngle(itemA.angla, itemB.angla)) / 360f, curACC);
    }

    private void SortDepth()
    {
        textureTargets.Sort(new CompareDepthMethod());
        for (int i = 0; i < textureTargets.Count; i++)
            textureTargets[i].depth = i;
    }

    /// <summary>
    /// 用於層級對比介面
    /// </summary>
    private class CompareDepthMethod : IComparer<UITexture>
    {
        public int Compare(UITexture left, UITexture right)
        {
            if (left.transform.localScale.x > right.transform.localScale.x)
                return 1;
            else if (left.transform.localScale.x < right.transform.localScale.x)
                return -1;
            else
                return 0;
        }
    }



    //核心滾動函式




    /// <summary>
    /// 設定橫向軸引數,根據縮放曲線和位移曲線更新縮放和位置
    /// </summary>

    internal void SetHorizontalTargetItemIndex(int itemIndex)
    {
        if (isInChange)
            return;

        EnhanceItem item = scrollViewItems[itemIndex];//_____________新場景中根據這個來判斷
        if (centerItem == item)
            return;
        Debug.Log ("item = " + item.name);
        if (item.name == "Texture01") {
            //Debug.Log("YES");
        }
//      _________________________________________________________________________________________




        float dvalue = centerItem == null ? 0 : GetCurveTimeDValue(centerItem, item);
        // 更改target數值,平滑移動,設負數倒著轉
        horizontalTargetValue += -dvalue;
        beginScroll(horizontalValue, horizontalTargetValue);
    }



    void FixedUpdate()
    {
//      ___________________________________________________________________________________手勢swipe模組
        this.currentFrame = hc.GetFrame ();
        Frame frame = hc.GetFrame ();
        Frame lastframe = hc.getlastframe ();
        GestureList gestures = this.currentFrame.Gestures ();
        Vector swipedirection=null;

        foreach (Gesture g in gestures) {


            if(g.Type==Gesture.GestureType.TYPE_SWIPE)
            {
                SwipeGesture swipe=new SwipeGesture(g);
                swipedirection=swipe.Direction;
                //Debug.Log("direction is "+swipedirection);

            }



        }
        if (swipedirection.x > 0) {//判斷手勢向左還是向右引數向左則小於0向右則大於0
            Debug.Log("right");     
            mark=1;
        }
        if (swipedirection.x < 0) {
            Debug.Log("left");
            mark=-1;
        }


        checkmark (mark);//檢查引數以完成UI的旋轉

//      ————————————————————————————————————————————————————————————————————————————————————————————————————————————
    }

    /// <summary>
    /// 開始滾動
    /// </summary>
    /// <param name="startTime"></param>
    /// <param name="endTime"></param>
    private void beginScroll(float startTime, float endTime)
    {
        if (isInChange)
            return;

        foreach (EnhanceItem item in scrollViewItems)
        {
            item.SetSelectColor(false);
        }

        originHorizontalValue = Round(startTime, curACC);
        horizontalTargetValue = Round(endTime, curACC);
        currentDuration = 0.0f;

        isInChange = true;
    }


    /// <summary>
    /// 向右選擇角色按鈕
    /// </summary>
    public void OnBtnRightClick()
    {
        if (isInChange)
            return;
        SetHorizontalTargetItemIndex(centerItem.back.scrollViewItemIndex);

    }

    /// <summary>
    /// 向左選擇按鈕
    /// </summary>
    public void OnBtnLeftClick()
    {
        if (isInChange)
            return;
        SetHorizontalTargetItemIndex(centerItem.front.scrollViewItemIndex);
    }



    /// <summary>
    /// 獲取移動後的專案索引
    /// </summary>
    /// <param name="item">當前專案</param>
    /// <param name="count">移動位數,負數表示倒移</param>
    /// <returns></returns>
    private int GetMoveIndex(EnhanceItem item, int count)
    {
        EnhanceItem curItem = item;
        for (int i = 0; i < Mathf.Abs(count); i++)
        {
            curItem = count > 0 ? curItem.back : curItem.front;

        }

        return curItem.scrollViewItemIndex;
    }

    /// <summary>
    /// 按指定小數位舍入
    /// </summary>
    /// <param name="f"></param>
    /// <param name="acc"></param>
    /// <returns></returns>
    private float Round(float f, int acc)
    {
        float temp = f * Mathf.Pow(10, acc);
        return Mathf.Round(temp) / Mathf.Pow(10, acc);
    }

    /// <summary>
    /// 擷取小數
    /// </summary>
    /// <param name="f"></param>
    /// <returns></returns>
    private float CutDecimal(float f) {
        return f - (int)f;
    }
}

以縮放曲線來決定 元素在位置發生轉動後 大小的變化規則
以位移曲線來規定 元素的運動位置軌跡
兩條曲線分別為:
scaleposition

可見一個小功能的實現竟然用了500+line
雖然不難 但是這個排除bug 保證邏輯正確的過程是在太令人頭疼 構造環形鏈的程式碼也比較抽象

           scrollViewItems = new List<EnhanceItem>();
           scrollViewItems.AddRange(enhanceScrollView.GetComponentsInChildren<EnhanceItem>());

而之後的程式碼將手勢識別的程式碼也包括進去了 主要思路就是判斷揮動手勢的方向 根據這個引數來確定UItexture整體轉動的方向

至此我們的這個型別的UI程式碼講解也結束了
總結一下手勢控制類UI:

1,此類UI構建方式和普通UI區別較小(幾乎沒區別)無論是平面,立體。
2,此類UI的控制方式最好生成一個統一的操作函式例如turnleft(),turnright();之後直接用手勢觸發就好
3,如果使用單一手勢觸發的話最好設定一定的時延 否則 誤操作將會非常多

最後

也就是這個系列的最後一些內容
我來講講印射式UI(或者說印射操作)

這裡用到的就是

射線

leap和射線可以說是非常蛋疼的組合 我本身對這個功能的探索時間長達一個月
我就細細說一下這個奇葩的功能的由來:

首先

面對一個模型我們想把它散開 再在每個子物體上加上盒觸發器 這種演算法在unity裡簡直好像一坨屎
結果就如下圖:
這裡寫圖片描述
boxcollider不能很好的契合模型
Meshcollider又因為面數限制而不能使用
那隻能用box湊活了 這就導致一個問題用手來觸發這一坨屎一樣的東西自然是很不精確的
並且一旦觸及百萬面的工業模型 效能就迅速下降到崩潰邊緣:

這裡寫圖片描述

像這樣 於是我們怎麼才能解決這個精確觸發的問題呢?這是問題一

其二

leapmotion的手的大小和識別區域是繫結的 所以放大識別區域的結果是必然要放大手的模型
比如我想增大在空間內控制器的操作範圍就會產生這種畫面
hand

手的模型變大嚴重阻礙了軟體的使用,所以又要保證手的大小適中作指示
而手的操作範圍被限制在控制器識別範圍之內。這就誕生了一個設計上的矛盾。
畫面上要求手變小 操作範圍上要求手變大。
於是
就有了射線

初次寫射線功能其實和leap並不能很好的一起工作,因為有太多的隱含引數數值需要去探索

ray

用這種方法可以進行精確化的觸發,選擇。在這個基礎上進行設計可以很多leap官方demo完成不了的操作
那下面我們先來看看程式碼 這個指令碼掛camera上

using UnityEngine;
using System.Collections;
using Leap;
public class ray : MonoBehaviour {
    public hand_script2 control_script;//控制指令碼定義
    public HandController hc=null;
    public RaycastHit hit; 
    public Vector3 handdir;
    public Vector3 handpos;
    //public GameObject cube = null;
    public GameObject highlight=null;

    float hx, hy, hz=0;
    float dx, dy, dz = 0;
    public GameObject particle_light=null;
    float xr,yr,zr;
    private float index=0;
    Hand lefthand=null;
    Hand righthand=null;


    public GameObject color_apply=null;

    Color origin=Color.black;
    public Color apply_color;


//  ______________________________________________________________
    bool squize(float radius)//判斷手勢是否為握持  閥值為35
    {
        if (radius < 37)
            return true;
        else
            return false;
    }
//  ---------------------------------------------------------------------------
    private bool IsHand(Collider collider)
    {
        return collider.transform.parent && collider.transform.parent.parent && collider.transform.parent.parent.GetComponent<HandModel>();
    }//判斷觸發器是否是手


    void Awake()
    {

    }


    // Use this for initialization
    void Start () {
        Debug.Log ("Start");
        //空間兩點距離計算:sqrt(pow(x1-x2,2)+pow(y1-y2,2)+pow(z1-z2,2))

        index = hc.transform.localScale.x / 1000;//計算後面的縮放指數








    }

    // Update is called once per frame
    void Update () {
        bool lefthandexit = false;
        bool righthandexit = false;
        //Debug.Log ("Update");
        //Vector3 fwd = new Vector3 (0, 0,10 );
        Frame frame = hc.GetFrame ();
        Frame lastframe = hc.getlastframe ();
        apply_color = color_apply.gameObject.GetComponent<RGB> ().rgb;//獲取color混合介面的顏色值可去掉


        foreach (Hand hand in frame.Hands) {
            if (hand.IsLeft) {
                lefthandexit = true;
                lefthand = hand;
            }

            if (hand.IsRight) {
                righthandexit = true;
                righthand = hand;


                foreach (Finger finger in righthand.Fingers) {
                    Finger.FingerType type=finger.Type();
                    if (type == Finger.FingerType.TYPE_INDEX) {
                        dx = finger.Direction.x;
                        dy = finger.Direction.y;
                        dz = finger.Direction.z;
                        hx = finger.TipPosition.x;
                        hy = finger.TipPosition.y;
                        hz = finger.TipPosition.z;
                    }
                  //獲取中指的資訊finger_INDEX
                }
                //dx = hand.Direction.x;
                //dy = hand.Direction.y;
                //dz = hand.Direction.z;
                handdir = new Vector3 (dx*index,dy*index,-dz*index);//必須乘指數

                //              ----------------------------------------------------------------------


                //hx = hand.PalmPosition.x;
                //hy = hand.PalmPosition.y;
                //hz = hand.PalmPosition.z;
                handpos = new Vector3 (hx*index, hy*index,-hz*index);//同樣乘指數

                //              ----------------------------------------------------------
                //Debug.Log ("righthand_position is " + handpos + "righthand dir is " + handdir);
                //Debug.Log ("reall date is" + hand.PalmPosition + " /// " + hand.Direction);
                xr = hand.RotationAngle (lastframe, Vector.XAxis);
                yr = hand.RotationAngle (lastframe, Vector.YAxis);
                zr = hand.RotationAngle (lastframe, Vector.ZAxis);

            }
        }
        //cube.transform.Rotate (new Vector3(xr*index,yr*index,-zr*index),Space.World);

        //原先用cube表示手的資訊與射線做對照
        /*
        float x = this.transform.rotation.x;
        float y = this.transform.rotation.y;
        float z = this.transform.rotation.z;
        Vector3 rota = new Vector3 (x, y, z);
        Debug.Log ("this.position=" + this.transform.position); 
        dx = this.transform.position.x;
        dy = this.transform.position.y;
        dz = this.transform.position.z;
        Vector3 position = new Vector3 (dx,dy+100,dz);

   */



        if (squize(lefthand.SphereRadius) && (!squize (righthand.SphereRadius))) {
        //如果左手握持且右手不握持
        //bool Physics.Raycast(Ray ray, Vector3 direction, RaycastHit out hit, float distance, int layerMask)
        if (Physics.Raycast(handpos,handdir,out hit,10000,1)) {
         //(Physics.Raycast(射出點,射出方向向量,輸出觸發資訊儲存點,長度,1))
            particle_light.transform.position =new Vector3(hit.point.x,hit.point.y,hit.point.z-2);//此處為粒子系統  用粒子系統跟隨觸發點以顯示觸發位置


                if ((highlight.gameObject.name.ToString() != hit.collider.gameObject.name.ToString()) &&
                    origin == Color.black&&
                    (!IsHand(hit.collider))&&
                    (hit.collider.gameObject.GetComponent<Renderer>()!=null)) {
              //判斷是否第一次觸發

                Debug.Log ("first");
                highlight = hit.collider.gameObject;
                control_script.main_son = hit.collider.gameObject;

                origin = highlight.gameObject.GetComponent<Renderer> ().material.color;
                highlight.gameObject.GetComponent<Renderer> ().material.color = Color.gray;


                }




                if ((highlight.gameObject.name.ToString () != hit.collider.gameObject.name.ToString ()) &&
                    origin != Color.black&&
                    (!IsHand(hit.collider))&&
                    ((hit.collider.gameObject.GetComponent<Renderer>()!=null))) {
                    //判斷是否變更觸發物體
                Debug.Log ("change");
                control_script.main_son = hit.collider.gameObject;

                highlight.gameObject.GetComponent<Renderer> ().material.color = origin;
                highlight = hit.collider.gameObject;
                origin = highlight.gameObject.GetComponent<Renderer> ().material.color;
                highlight.gameObject.GetComponent<Renderer> ().material.color = Color.gray;
            }

//以上用來交換資訊  坐到觸發高亮

            /*
            if((highlight.gameObject.name.ToString()==hit.collider.gameObject.name.ToString())&&origin!=Color.black)
            {
                highlight = hit.collider.gameObject;
                highlight.gameObject.GetComponent<Renderer> ().material.color = Color.red;
            }
            */


            //hit.collider.gameObject.GetComponent<Renderer> ().material.color = Color.red;
            Debug.Log ("OBJ-name is " + hit.collider.gameObject);
            //Debug.Log ("hit point is " + hit.point + " distance = " + hit.distance);
            //Debug.Log ("Success");
            Debug.DrawLine (handpos, hit.point, Color.red);
            //在sence中顯示射線並設定為紅色
                //Debug.Break ();

        }



        if (lefthandexit==true) {
            Debug.Log ("________________");
            //Debug.Break();
        }

    }
    }
}

    //射線指令碼bug彙總:
    /*
     * 1.發射點以世界座標為準則:
     * 如handcontroller的放大倍率為100則數乘index指數為0.1
     * 2.必須將控制器的位置考慮進去
     * 3.射線距離儘可能大 
     */

指令碼畢來說一些特殊的地方
那些隱含的值最後確定為

index = hc.transform.localScale.x / 1000;//計算後面的縮放指數

而以上都基於 控制器在原點也就是(0,0,0)
而針對 非原點的情況

要把控制器的座標也算進去才能保證你的射線是從指尖發出去的

如果想更改射線發射的位置 那麼判斷進其他的手指型別就好了 程式碼裡面非常清楚

同時 在射線的位置用一個粒子特效做一個觸發位置提示 效果像這樣

light

在這個體系之下 你就可以把手指當做一個三維環境裡的滑鼠 對任意一個點進行觸發操作以進一步的執行其他部分

所以我們的觸發UI可以不用再設計在控制器的範圍之內 我們可以用射線功能觸發幾乎無限遠的元素

比如在一個環境中我們通過一定對映關係去觸發區域之外的按鈕
不同
如圖所示

至此

所有LeapUI設計專題所有內容都講完了
洋洋灑灑的三萬多字
再次:

本人水平有限,有錯誤請聯絡qq:785929784
也歡迎有興趣的開發者加入qq群:343074971
共同交流共同進步。

感謝群友對本部落格的支援

後續將更新

1.open cv專題系列
2.c++與演算法導論的愛情故事系列
3.計算幾何常用演算法專題
4.unity shader專題系列
5.unity AI專題系列
6.cuda並行運算入門

歡迎支援