栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 游戏开发 > 其他

Unity中实现仿3D轮转图

其他 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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().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;

/// 
/// 仿3D轮转图组件
/// 
[AddComponentMenu("LFramework/UI/Rotary3DList", 50)]
[RequireComponent(typeof(Image))]
public class Rotary3DList : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
    protected Rotary3DList()
    {

    }

    /// 
    /// 列表item数据
    /// 
    public class ListItemData
    {
        public int index;
        public GameObject go;
        public float targetValue;//目标位置长度值
        public float tempValue;//临时位置长度值(每次拖拽结束后才更新数值)
    }

    /// 
    /// 轮转类型
    /// 
    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;
    //设置item
    public Action OnSetItem;
    //拖拽开始
    public Action OnDragBegin;
    //拖拽中
    public Action OnDragging;
    //拖拽结束
    public Action OnDragEnd;

    //中心item下标
    public int CenterIndex
    {
        get { return GetCenterItemIndex(); }
    }
    //列表item总数量
    int m_ItemCount;
    //总长度值
    float m_TotalValue;
    //长度值增量
    float m_DeltaValue;
    GameObject m_Prefab;
    RectTransform m_ItemContainer;
    List m_ListItemDataList = new List();

    //是否在拖拽中
    bool m_InDrag;
    //每次拖拽的起始位置
    float m_BeginPos;
    //初始每个item的位置长度值
    List m_InitValueList = new List();

    /// 
    /// 设置数据
    /// 
    public void SetData(GameObject prefab, int itemCount)
    {
        m_ItemContainer = GetComponent();
        m_ItemCount = itemCount;
        m_Prefab = prefab;
        m_DeltaValue = rotaryType == RotaryType.Horizontal
            ? (spacing + m_Prefab.GetComponent().rect.width)
            : (spacing + m_Prefab.GetComponent().rect.height);
        m_TotalValue = m_DeltaValue * m_ItemCount;

        InitData();
    }

    /// 
    /// 初始化数据
    /// 
    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);
    }

    /// 
    /// 设置列表
    /// 
    public void SetList()
    {
        foreach (var data in m_ListItemDataList)
        {
            OnSetItem?.Invoke(data);
        }

        UpdateItem(true);
    }

    /// 
    /// 移动到某个下标位置
    /// 
    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 targetValue = data.tempValue + m_DeltaValue * indexOffset < 0
                ? m_TotalValue - Mathf.Abs(data.tempValue + m_DeltaValue * indexOffset) % m_TotalValue
                : (data.tempValue + m_DeltaValue * indexOffset) % 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数据
        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(m_InDrag);
    }

    /// 
    /// 更新item
    /// 
    /// isForce : 是否强制复位
    void UpdateItem(bool isForce)
    {
        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);
            Vector3 targetScale = Vector3.one * scale;
            float targetScaleOffset = data.go.transform.localScale.x - targetScale.x;
            data.go.transform.localScale = Vector3.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);
        }
    }

    /// 
    /// 得到位置
    /// 
    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);
        }
    }

    /// 
    /// 得到缩放值
    /// 
    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;
        }
    }

    /// 
    /// 得到距离最近的位置长度值
    /// 
    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;
    }

    /// 
    /// 得到中心item的下标
    /// 
    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;
    }

    /// 
    /// 得到列表item数据
    /// 
    public ListItemData GetListItemData(int index)
    {
        if (index < 0
           || index >= m_ItemCount)
        {
            Debug.LogError("下标超出范围,index : " + index);
            return null;
        }

        return m_ListItemDataList[index];
    }
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/902319.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号