這篇文章主要介紹“Unity ScrollRect怎么實現軌跡滑動效果”,在日常操作中,相信很多人在Unity ScrollRect怎么實現軌跡滑動效果問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Unity ScrollRect怎么實現軌跡滑動效果”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
以下內容是根據Unity 2020.1.01f版本進行編寫的
工作中遇到有需要實現軌跡滑動的滑動列表,通常的做法是計算貝塞爾曲線得出軌跡,但是我覺得計算貝塞爾曲線太麻煩了,或許有沒有更簡單的方法。
軌跡滑動可以分兩種情況:
第一種是軌跡滑動是比較簡單的橫向(或縱向)滑動,其中軌跡不會蜿蜒盤旋,也不涉及列表格子之間的重疊關系,這時可以分區間來對Y軸(或X軸)進行設置以達到格子沿著軌跡滑動的效果
例如,軌跡是這樣的波浪型的,其中,軌跡點的數量可以無限增加,點越多軌跡可以設置得越平滑,但是消耗的性能也越多:

像這樣,類似波浪型的軌跡,可以用一個Vector列表記錄下每個點的位置和順序,然后根據每個格子所在的位置,先由小到大循環判斷在哪個區間,例如格子3在pos_5和pos_6中間,所以第三個格子的區間就是5,然后通過pos_5和pos_6兩個點相減,得到一個從pos_5指向pos_6的向量vector,通過vector3.distance函數得到pos_5和pos_6兩個點的橫向距離distanceX1,然后再次通過vector3.distance函數得到pos_5與格子3得橫向距離distanceX2,那么,格子3的位置 = pos_5的位置 + distanceX2 / distanceX1 * vector,得到位置后再把位置設置回去就可以了?
第二種就是更為復雜的軌跡滑動,例如蜿蜒盤旋式的軌跡,其中還包括有層級關系
例如,軌跡是蜿蜒盤旋的,同樣是軌跡點越多,曲線越平滑,也越消耗性能:

像這樣,蜿蜒盤旋的軌跡,就不能使用第一種方法了,因為有些位置,一個X值對應下來有多個Y值,無法區分當前需要的是哪個Y值,所以,需要使用另一種方法
我們可以通過獲取所有的軌跡點,計算出每兩個相鄰軌跡點之間的距離,通過距離確定格子應該在哪個區間內
例如,格子3的位置是95,假設pos_1和pos_2的距離為50,pos_2和pos_3的距離為也是50,那么pos_1到pos_3的總距離就是100,所以格子3應該在區間pos_2和pos_3中間。接下來計算實際位置,因為此時格子的X軸和Y軸都會變化,同樣無法使用第一種方法來計算格子的位置,需要通過格子的位置減去前面區間距離的和,在例子中,就是格子3的位置減去pos_1到pos_2的距離,即95 – 50 = 45,再通過剩余的距離除以當前區間的距離得出比例ratio,然后就是通過當前的兩個區間點相減得到pos_2指向pos_3的向量vector,那么格子3的位置就是,pos_2的位置 + ratio * vector
最后,還有層級的問題,也就是哪個格子遮擋哪個格子,一般是后面的格子遮擋前面的格子,如果需要前面的遮擋后面的格子,只需要動態修改層級就行,unity提供了這個函數:Transform.SetAsFirstSibling()
在實際使用中,我發現使用這種方法后,content的長度有時候會過長,有時候會過短,所以需要確定好格子的數量,設置好content的正確長度
注意:以上都是最基礎的思想,因為滑動時改變的是content節點,格子的位置實際上是不變的,或者是由代碼設置的,所以實際代碼中還需要考慮scrollrect的寬高以及content節點的滑動值(即posX值,滑動時此值會變化)
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
[RequireComponent(typeof(ScrollRect))]
public class CurveSliding : MonoBehaviour
{
public Transform posListRoot;
public float contentwidth; //必須自己設置的content實際長度
public bool isChangeHierarchy = false;
private ScrollRect scrollRect;
private RectTransform content;
private List<Transform> posTransformList = new List<Transform>();
private List<RectTransform> item_transformList = new List<RectTransform>();
private List<float> posX_List = new List<float>();
private List<float> distanceList = new List<float>();
private float scrollRectWidth;
private float scrollRectHeight;
private float posTotalDistance;
private float spacing = 0;
private void Start()
{
scrollRect = GetComponent<ScrollRect>();
content = scrollRect.content.transform as RectTransform;
HorizontalLayoutGroup contentLayoutGroup = content.GetComponentsInChildren<HorizontalLayoutGroup>(true)[0];
if(contentLayoutGroup != null && contentLayoutGroup.name == content.name)
{
spacing = contentLayoutGroup.spacing;
}
scrollRectWidth = scrollRect.GetComponent<RectTransform>().sizeDelta.x;
scrollRectHeight = scrollRect.GetComponent<RectTransform>().sizeDelta.y;
for (int i = 0; i < content.childCount; i++)
{
RectTransform item_transform = content.GetChild(i) as RectTransform;
if (item_transform.gameObject.activeInHierarchy)
{
item_transformList.Add(item_transform);
posX_List.Add(item_transform.localPosition.x);
}
else
{
continue;
}
}
float totalDistance = 0;
for(int i = 0;i < posListRoot.childCount;i++)
{
Transform posTransform = posListRoot.GetChild(i);
if (i > 0)
{
Transform previousPosTransform = posListRoot.GetChild(i - 1);
totalDistance += Vector3.Distance(posTransform.localPosition, previousPosTransform.localPosition);
}
posTransformList.Add(posTransform);
distanceList.Add(totalDistance);
}
posTotalDistance = distanceList[distanceList.Count - 1];
content.sizeDelta = new Vector2(contentwidth, content.sizeDelta.y);
OnValueChange(Vector2.zero);
scrollRect.onValueChanged.AddListener(OnValueChange);
}
public void OnValueChange(Vector2 vector)
{
for(int i = 0;i < item_transformList.Count;i++)
{
float localPosX = posX_List[i];
float posX = localPosX + content.anchoredPosition.x;
//如果當前節點的位置 - content的X軸偏移值 > 滑動列表的寬度,則說明當前item在可視范圍外
if (posX > posTotalDistance + 200 || posX < 0)
{
continue;
}
int index = -1;
foreach(var totalDistance in distanceList)
{
if(posX < totalDistance)
{
break;
}
else
{
index++;
}
}
//如果index+1小于位置列表的數量,則其在位置區間內,否則應該在位置區間外
if (index + 1 < posListRoot.childCount && index + 1 > 0)
{
float ratio = (posX - distanceList[index]) / (distanceList[index + 1] - distanceList[index]);
Vector3 newPos = posTransformList[index].localPosition - ratio * (posTransformList[index].localPosition - posTransformList[index + 1].localPosition);
item_transformList[i].localPosition = new Vector3(newPos.x + scrollRectWidth/2 - content.anchoredPosition.x, -scrollRectHeight / 2 + newPos.y, 0);
}
else if(index <= -1)
{
item_transformList[i].localPosition = new Vector3(item_transformList[i].localPosition.x, -scrollRectHeight / 2 + posListRoot.GetChild(0).localPosition.y, 0);
}
else if(index >= posListRoot.childCount - 1)
{
if (i < 1)
{
continue;
}
RectTransform previousItem_RectTransform = item_transformList[i - 1];
item_transformList[i].localPosition = new Vector3(previousItem_RectTransform.localPosition.x + spacing + previousItem_RectTransform.sizeDelta.x/2 + item_transformList[i].sizeDelta.x/2, -scrollRectHeight / 2 + posListRoot.GetChild(posListRoot.childCount - 1).localPosition.y, 0);
}
if (isChangeHierarchy)
{
item_transformList[i].SetAsFirstSibling();
}
}
}
}1)、 兩種方法都不能使用排序組件,因為排序組件會控死格子的位置,通過代碼也無法修改
2)、 第二種方法比第一種方法更復雜,同時消耗的性能也更多,但是能實現更復雜的效果
3)、第二種方法需要設置conten的長度,即格子的數量無法動態變化,是一大缺點
第一種:

第二種:

到此,關于“Unity ScrollRect怎么實現軌跡滑動效果”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。