Unity3D問題之EnhanceScollView選擇角色3D迴圈滾動效果實現

damenhanter發表於2016-02-02

需求

  • 呈現3D效果(2D素材)選擇角色效果
  • 滾動保證層級,縮放比例,間距正常跟隨
  • 迴圈滾動
  • 這個介面需求一般也會有遊戲會採用(貌似有挺多)

如何實現

實現技術關鍵點

(3D迴圈效果,根據數學函式和仔細研究下具體效果很容易通過計算方式直接實現,本文主要目的是介紹下AnimationCurve工具,通過這個工具能夠幫助我們實現一些需求當然也包括實現酷炫的3D滾動效果)

  1. 如何控制每個Item之間的間隔(位置),縮放比例,差值平滑
  2. 如何實現item層級關係正確顯示("離" 螢幕近的層級高)
  3. 如何實現迴圈滾動

下面一一講述當前Demo採用的方法
說到實現的核心,需要知道Unity3D中提供的一個叫做AnimationCurve的元件,這個不僅僅是表面上美術可以使用的元件,也不只是單純的動畫曲線的概念,當然它就是動畫曲線,但是我們可以賦予AnimationCurve不同的意義,則可以藉助Curve實現不同的功能,(AnimationCurve定義了一個變化趨勢或者曲線,在不同的時間點,我們可以得到當前時間點下該曲線對應的y軸資訊,這個資訊可以是角色跳躍的高度,模型縮放的一個係數,攝像機距離目標的長度,一個角色當前的心情數值等等,曲線可以表示很多的意義)
沒用過AnimationCurve的朋友,直接去官網看下介紹就明白如何使用
下面簡單說下使用AnimationCurve可以完成的一些功能(上面已經介紹了一部分場景)

  1. 角色2D跳躍
  2. 攝像機移動
  3. 角色心情指數
  4. 縮放係數
  5. 距離係數
  6. ......

我們也賦予AnimationCurve不同的意義,實現我們核心目標(控制位移,控制縮放 當然也可以控制層級)

控制位移,縮放(3D效果的關鍵),差值過度動畫平滑

  1. 建立兩個AnimationCurve一個是scaleAnimationCurve和positionXAnimationCurve,分別控制縮放和位移
  2. 時間流水線控制(我們把所有的Item設定好自己對應的時間流位置即可,每次只要一動時間流水線,然後從兩個曲線內獲得當前流水線對應的縮放係數,位移係數,然後設定item的位移和縮放即可)
  3. 如何製作動畫(這個其實就是簡單的時間流水線的差值處理,一定時間時間流水值達到目標值即可)
下面放上兩張曲線截圖和具體實現:


[csharp] view plain copy
  1. /// <summary>  
  2.     /// 縮放曲線模擬當前縮放值  
  3.     /// </summary>  
  4.     private float GetScaleValue(float sliderValue, float added)  
  5.     {  
  6.         float scaleValue = scaleCurve.Evaluate(sliderValue + added);  
  7.         return scaleValue;  
  8.     }  
  9.   
  10.     /// <summary>  
  11.     /// 位置曲線模擬當前x軸位置  
  12.     /// </summary>  
  13.     private float GetXPosValue(float sliderValue, float added)  
  14.     {  
  15.         float evaluateValue = positionCurve.Evaluate(sliderValue + added) * posCurveFactor;  
  16.         return evaluateValue;  
  17.     }  
  18.   
  19. public void UpdateEnhanceScrollView(float fValue)  
  20.     {  
  21.         for (int i = 0; i < scrollViewItems.Count; i++)  
  22.         {  
  23.             EnhanceItem itemScript = scrollViewItems[i];  
  24.             float xValue = GetXPosValue(fValue, dHorizontalValues[itemScript.scrollViewItemIndex]);  
  25.             float scaleValue = GetScaleValue(fValue, dHorizontalValues[itemScript.scrollViewItemIndex]);  
  26.             itemScript.UpdateScrollViewItems(xValue, yPositionValue, scaleValue);  
  27.         }  
  28.     }  
  29.   
  30.     void Update()  
  31.     {  
  32.         currentDuration += Time.deltaTime;  
  33.         if (currentDuration > duration)  
  34.         {  
  35.             // 更新完畢設定選中item的物件即可  
  36.             currentDuration = duration;  
  37.             if(centerItem != null)  
  38.                 centerItem.SetSelectColor(true);  
  39.             if(preCenterItem != null)  
  40.                 preCenterItem.SetSelectColor(false);  
  41.             canChangeItem = true;  
  42.         }  
  43.   
  44.         SortDepth();  
  45.         float percent = currentDuration / duration;  
  46.         horizontalValue = Mathf.Lerp(originHorizontalValue, horizontalTargetValue, percent);  
  47.         UpdateEnhanceScrollView(horizontalValue);  
  48.     }  

控制層級

只有正確的層級控制,才能夠保證"不穿幫",上文也說過,也可以通過AnimationCurve做一個層級曲線,在當前item的時間下面該item的depth或者層級應該是多少,該demo採用的是比較粗暴的list排序方法,按照每個item距離"螢幕的遠近"其實就是scale係數,判斷哪個item在前,哪個在後面,當然也有些問題,如果距離相同,可能存在item相互打架的可能(這個可以通過控制scaleCurve進行控制)
該Demo使用的UITexture控制層級(其他的任何方式原理一樣,只是處理物件不一樣,用mesh實現,那就是z軸等等)
具體實現如下:

[csharp] view plain copy
  1. public void SortDepth()  
  2.     {  
  3.         textureTargets.Sort(new CompareDepthMethod());  
  4.         for (int i = 0; i < textureTargets.Count; i++)  
  5.             textureTargets[i].depth = i;  
  6.     }  
  7.     /// <summary>  
  8.     /// 用於層級對比介面  
  9.     /// </summary>  
  10.     public class CompareDepthMethod : IComparer<UITexture>  
  11.     {  
  12.         public int Compare(UITexture left, UITexture right)  
  13.         {  
  14.             if (left.transform.localScale.x > right.transform.localScale.x)  
  15.                 return 1;  
  16.             else if (left.transform.localScale.x < right.transform.localScale.x)  
  17.                 return -1;  
  18.             else  
  19.                 return 0;  
  20.         }  
  21.     }  

實現滾動迴圈

說道迴圈滾動,因為我們使用到了AnimationCurve,先天性的動畫曲線會有三種模式一種是pingpong,loop,一種是clamp,其中我們需要的是LOOP,沒聽錯這就是滾動迴圈的關鍵點(我們的縮放曲線,位移係數曲線從0到1的效果模擬完畢,如果我們繼續向前增加時間流水值,那麼進入到下一個曲線的時候,所有的item都會反過來進行取樣曲線值,就能夠巧妙的實現迴圈效果(縮放係數,位移係數))如果不理解的,可以自己設定一個AnimationCurve,研究下,下面截圖示意:

程式碼部分只是需要知道,如果點選了一個Item將該item移動到中心對應的時間流應該往前或者往後走多少

[csharp] view plain copy
  1. /// <summary>  
  2. /// 獲得當前要移動到中心的Item需要移動的factor間隔數  
  3. /// </summary>  
  4. private int GetMoveCurveFactorCount(float targetXPos)  
  5. {  
  6.     int centerIndex = scrollViewItems.Count / 2;  
  7.     for (int i = 0; i < scrollViewItems.Count;i++ )  
  8.     {  
  9.         float factor = (0.5f - dFactor * (centerIndex - i));  
  10.   
  11.         float tempPosX = positionCurve.Evaluate(factor) * posCurveFactor;  
  12.         if (Mathf.Abs(targetXPos - tempPosX) < 0.01f)  
  13.             return Mathf.Abs(i - centerIndex);  
  14.     }  
  15.     return -1;  
  16. }  

注意問題

  1. 製作曲線,記得保證0-1時間軸填充完畢,這樣在進行迴圈處理的時候才不會出現偏差
  2. 額,如果自己用這種方法嘗試的朋友,如果有問題,請仔細檢視Demo中的引數即可......(主要就是曲線製作問題)

該Demo使用的NGUI,雖然筆者沒有用過UGUI,我想任何一個介面Tools都可以通過該方法實現,因為共同點一樣,只是層級處理,縮放處理有區別而已

實現效果






改進目標

該專案還有許多需要改進的地方,以後花時間繼續完善

  • 支援Editor模式下的編輯,不用執行即可檢視效果(這個應該是最關鍵的功能)
  • 支援偶數個Item進行滑動
  • 支援Drag操作
  • 支援和NGUI類似的DragScrollView和CenterOnChild功能
  • 優化每個Item的層級設定演算法效率
  • 優化更新每個Item位置,縮放演算法效率

問題:
1. 在處理曲線迴圈的地方,由於精確度不夠,導致持續點按過後會造成卡片大小發生變化,後續直接使用單一曲線或者通過程式控制曲線精確~

GitHub地址:後續更新會直接在版本庫中更新

https://github.com/tinyantstudio/EnhancedScrollView


APK測試安裝地址:http://pan.baidu.com/s/1bnfPasJ

總結

所有的內容都講述完畢,如果這篇文章能夠幫助到您獲得對看到結束的朋友有一個簡單的啟發,請支援下~,文中存在錯誤或者描述不清楚的也請指正,共同交流學習,最好的方法就是直接下載Demo,然後看下邏輯和動畫曲線的設定引數

歡迎轉載,請註明出處~

By NPC燕(AndyKun)

相關文章