Unity實現仿3D輪轉圖效果
本文實例為大傢分享瞭Unity實現仿3D輪轉圖效果的具體代碼,供大傢參考,具體內容如下
一、效果演示
二、實現思路
——獲取位置:可以將每個item的運行軌跡看作一個橢圓,分為四段進行觀察,四個黑點視為四個item,觀察四個黑點的位置,比例值為0.125和0.375的位置相同,比例值為0.625和0.875的位置相同,比例值為0.375和0.625的位置相反,可得結論
[0,0.25]:軌跡總長度*當前比例值
(0.25,0.5]:軌跡總長度 * (0.5 – 當前比例值)
(0.5,0.75]:軌跡總長度 * (0.5 – 當前比例值)
(0.75,1]:軌跡總長度 * (當前比例值 – 1)
——獲取縮放值:可以將每個item的運行軌跡看作一個橢圓,分為四段進行觀察,四個黑點視為四個item,觀察四個黑點的位置,比例值為0時縮放值應為最大,比例值為0.5時縮放值應為最小,可得結論
[0-0.5]:縮放最大值 – 比例值 * (縮放最大值 – 縮放最小值) * 2
(0.5-1]:縮放最大值 – (1 – 比例值) * (縮放最大值 – 縮放最小值) * 2
——獲取層級:使用UGUI的自然層級進行排序(越靠下越後渲染),拷貝一份列表item數據列表按照縮放值從小到大的順序排序,再通過SetSiblingIndex依次設置層級
三、使用
——常規使用
SetData:傳入item預制體和列表中item個數
OnSetItem:綁定設置item的方法
SetList:設置列表的顯示
using UnityEngine; using UnityEngine.UI; public class Test : MonoBehaviour { public GameObject prefab; public Rotary3DList rotary3DList; private void Start() { rotary3DList.SetData(prefab, 5); rotary3DList.OnSetItem = SetItem; rotary3DList.SetList(); } void SetItem(Rotary3DList.ListItemData listItemData) { listItemData.go.GetComponent<Text>().txt = listItemData.index.ToString(); } }
——MoveToIndex:移動到某一個下標位置,isScroll表示是否滑動到指定位置
——GetListItemData:獲取到某個下標的item數據
——CenterIndex:當前中心點item下標
四、代碼實現
using System.Collections.Generic; using UnityEngine; using System; using UnityEngine.UI; using UnityEngine.EventSystems; using System.Linq; /// <summary> /// 仿3D輪轉圖組件 /// </summary> [AddComponentMenu("LFramework/UI/Rotary3DList", 50)] [RequireComponent(typeof(Image))] public class Rotary3DList : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler { /// <summary> /// 列表item數據 /// </summary> public class ListItemData { public int index; public GameObject go; public float targetValue;//目標位置長度值 public float tempValue;//臨時位置長度值(每次拖拽結束後才更新數值) } /// <summary> /// 輪轉類型 /// </summary> public enum RotaryType { Horizontal, Vertical, } public RotaryType rotaryType;//輪轉類型 public float spacing;//間隔 public float maxScale = 1;//最大縮放值 public float minScale = 0.5f;//最小縮放值 public float t = 0.1f;//緩動插值 public Action<ListItemData> OnSetItem;//設置item public Action<PointerEventData> OnDragBegin;//拖拽開始 public Action<PointerEventData> OnDragging;//拖拽中 public Action<PointerEventData> OnDragEnd;//拖拽結束 //中心item下標 public int CenterIndex { get { return GetCenterItemIndex(); } } int m_ItemCount;//列表item總數量 float m_TotalValue;//總長度值 float m_DeltaValue;//長度值增量 GameObject m_Prefab; RectTransform m_ItemContainer; List<ListItemData> m_ListItemDataList = new List<ListItemData>(); bool m_InDrag; float m_BeginPos; List<float> m_InitValueList = new List<float>();//初始每個item的位置長度值 /// <summary> /// 設置數據 /// </summary> public void SetData(GameObject prefab, int itemCount) { m_ItemContainer = GetComponent<RectTransform>(); m_ItemCount = itemCount; m_Prefab = prefab; m_DeltaValue = rotaryType == RotaryType.Horizontal ? (spacing + m_Prefab.GetComponent<RectTransform>().rect.width) : (spacing + m_Prefab.GetComponent<RectTransform>().rect.height); m_TotalValue = m_DeltaValue * m_ItemCount; InitData(); } /// <summary> /// 初始化數據 /// </summary> void InitData() { float tempValue = 0; for (int i = 0; i < m_ItemCount; i++) { ListItemData data = new ListItemData(); data.index = i; data.go = Instantiate(m_Prefab, transform, m_ItemContainer); data.targetValue = tempValue; data.tempValue = tempValue; m_ListItemDataList.Add(data); m_InitValueList.Add(tempValue); tempValue += m_DeltaValue; } m_InitValueList.Add(m_TotalValue); } /// <summary> /// 設置列表 /// </summary> public void SetList() { foreach (var data in m_ListItemDataList) { OnSetItem?.Invoke(data); } UpdateItem(true); } /// <summary> /// 移動到某個下標位置 /// </summary> public void MoveToIndex(int index, bool isScroll = true) { if (index < 0 || index >= m_ItemCount) { Debug.LogError("下標超出范圍,index : " + index); return; } int indexOffset = CenterIndex - index; foreach (var data in m_ListItemDataList) { float tempValue = data.tempValue + m_DeltaValue * indexOffset < 0 ? data.tempValue + m_DeltaValue * indexOffset + m_TotalValue : data.tempValue + m_DeltaValue * indexOffset; float targetValue = tempValue % m_TotalValue; data.targetValue = targetValue; data.tempValue = targetValue; } UpdateItem(!isScroll); } public void OnBeginDrag(PointerEventData eventData) { m_InDrag = true; m_BeginPos = rotaryType == RotaryType.Horizontal ? eventData.position.x : eventData.position.y; OnDragBegin?.Invoke(eventData); } public void OnDrag(PointerEventData eventData) { OnDragging?.Invoke(eventData); float endPos = rotaryType == RotaryType.Horizontal ? eventData.position.x : eventData.position.y; float offset = endPos - m_BeginPos; //計算item數據 if (offset > 0) { foreach (var data in m_ListItemDataList) { float tempValue = (data.tempValue + offset) % m_TotalValue; data.targetValue = tempValue; } } else if (offset < 0) { foreach (var data in m_ListItemDataList) { float tempValue = data.tempValue + offset < 0 ? m_TotalValue - Mathf.Abs(data.tempValue + offset) % m_TotalValue : (data.tempValue + offset) % m_TotalValue; data.targetValue = tempValue; } } } public void OnEndDrag(PointerEventData eventData) { m_InDrag = false; OnDragEnd?.Invoke(eventData); foreach (var data in m_ListItemDataList) { float nearlyValue = GetNearlyValue(data.targetValue); data.targetValue = nearlyValue; data.tempValue = nearlyValue; } } private void Update() { UpdateItem(false); } /// <summary> /// 更新item /// </summary> void UpdateItem(bool isForce) { //拖拽中-實時更新 if (m_InDrag) { foreach (var data in m_ListItemDataList) { float ratio = data.targetValue / m_TotalValue; //更新位置 float pos = GetPos(ratio); Vector2 targetPos = rotaryType == RotaryType.Horizontal ? new Vector2(pos, 0) : new Vector2(0, pos); data.go.transform.localPosition = targetPos; //更新縮放值 float scale = GetScale(ratio); Vector2 targetScale = Vector3.one * scale; data.go.transform.localScale = targetScale; } } //非拖拽中-緩動更新 else { foreach (var data in m_ListItemDataList) { float ratio = data.targetValue / m_TotalValue; //更新位置 float pos = GetPos(ratio); Vector2 targetPos = rotaryType == RotaryType.Horizontal ? new Vector2(pos, 0) : new Vector2(0, pos); float targetPosOffset = rotaryType == RotaryType.Horizontal ? data.go.transform.localPosition.x - targetPos.x : data.go.transform.localPosition.y - targetPos.y; data.go.transform.localPosition = Vector2.Lerp(data.go.transform.localPosition, targetPos, isForce ? 1 : t); if (Mathf.Abs(targetPosOffset) <= 0.01f) { data.go.transform.localPosition = targetPos; } //更新縮放值 float scale = GetScale(ratio); Vector2 targetScale = Vector3.one * scale; float targetScaleOffset = data.go.transform.localScale.x - targetScale.x; data.go.transform.localScale = Vector2.Lerp(data.go.transform.localScale, targetScale, isForce ? 1 : t); if (Mathf.Abs(targetScaleOffset) <= 0.01f) { data.go.transform.localScale = targetScale; } } } //更新層級 var listItemDataList = m_ListItemDataList.OrderBy(data => GetScale(data.targetValue / m_TotalValue)).ToList(); for (int i = 0; i < m_ItemCount; i++) { listItemDataList[i].go.transform.SetSiblingIndex(i); } } /// <summary> /// 得到位置 /// </summary> float GetPos(float ratio) { if (ratio < 0 && ratio > 1) { Debug.LogError("比例值錯誤,比例值必須為[0-1],ratio : " + ratio); return 0; } if (ratio >= 0 && ratio <= 0.25f) { return m_TotalValue * ratio; } else if (ratio > 0.25f && ratio <= 0.75f) { return m_TotalValue * (0.5f - ratio); } else { return m_TotalValue * (ratio - 1); } } /// <summary> /// 得到縮放值 /// </summary> float GetScale(float ratio) { if (ratio < 0 && ratio > 1) { Debug.LogError("比例值錯誤,比例值必須為[0-1],ratio : " + ratio); return 0; } float v = (maxScale - minScale) * 2; if (ratio >= 0 && ratio <= 0.5f) { return maxScale - ratio * v; } else { return maxScale - (1 - ratio) * v; } } /// <summary> /// 得到距離最近的位置長度值 /// </summary> float GetNearlyValue(float curValue) { float minDis = Mathf.Abs(curValue - m_InitValueList.First()); float nearlyValue = m_InitValueList.First(); foreach (var value in m_InitValueList) { float tempDis = Mathf.Abs(curValue - value); if (tempDis < minDis) { minDis = tempDis; nearlyValue = value == m_TotalValue ? m_InitValueList.First() : value; } } return nearlyValue; } /// <summary> /// 得到中心item的下標 /// </summary> int GetCenterItemIndex() { int index = 0; float minDis = Mathf.Min(Mathf.Abs(m_ListItemDataList[0].targetValue - m_InitValueList.First()), Mathf.Abs(m_ListItemDataList[0].targetValue - m_InitValueList.Last())); foreach (var data in m_ListItemDataList) { float tempDis = Mathf.Min(Mathf.Abs(data.targetValue - m_InitValueList.First()), Mathf.Abs(data.targetValue - m_InitValueList.Last())); if (tempDis < minDis) { minDis = tempDis; index = data.index; } } return index; } /// <summary> /// 得到列表item數據 /// </summary> public ListItemData GetListItemData(int index) { if (index < 0 || index >= m_ItemCount) { Debug.LogError("下標超出范圍,index : " + index); return null; } return m_ListItemDataList[index]; } }
以上就是本文的全部內容,希望對大傢的學習有所幫助,也希望大傢多多支持WalkonNet。