UGUI案例篇——使用2D图片制作3D轮转图

最后的两个代码段为最终代码,可直接使用(需导入DoTween)

对数据的设置(参考):

先创建物体模板,再创建物体

设置图片的缩放系数和X轴位置,再对其位置信息进行计算:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class RotationDiagram2D : MonoBehaviour
{
    public Vector2 ItemSize;  //图片大小
    public Sprite[] ItemSprites;
    public float Offect; //图片间距
    public float ScaleTimesMin; //最大缩放系数
    public float ScaleTimesMax; //最小缩放系数

    private List<RotationDiagramItem> RotationDiagramItemList;
    private List<ItemPosData> PosDataList;

    void Start()
    {
        PosDataList = new List<ItemPosData>();
        RotationDiagramItemList = new List<RotationDiagramItem>();
        CreateItem();
        CalculateData();
        SetItemPosData();
    }

    /// <summary>
    /// 创建物体模板
    /// </summary>
    GameObject CreateTemplate()
    {
        GameObject template = new GameObject();
        template.AddComponent<RectTransform>();
        template.AddComponent<Image>();
        template.AddComponent<RotationDiagramItem>();
        return template;
    }

    //创建UI物体,设置图片和父物体 并 将其放入RotationDiagramItemList中
    void CreateItem()
    {
        GameObject template = CreateTemplate();
        RotationDiagramItem item = null;
        foreach (Sprite sprite in ItemSprites)
        {
            item = Instantiate(template).GetComponent<RotationDiagramItem>();
            item.SetSprite(sprite);
            item.SetParent(transform);
            RotationDiagramItemList.Add(item);
        }
        Destroy(template);

    }

    //计算位置的方法
    private void CalculateData()
    {
        float length = (ItemSize.x + Offect) * RotationDiagramItemList.Count; // 长度为(图片长度 + 图片间间隔) * 图片数量
        float radioOffect = 1 / (float)RotationDiagramItemList.Count;   // 每个图片的等距间隔

        float radio = 0;
        for (int i = 0; i < RotationDiagramItemList.Count; i++)
        {
            ItemPosData data = new ItemPosData();
            data.X = GetX(radio, length);
            data.ScaleTimes = GetScaleTimes(radio, ScaleTimesMax, ScaleTimesMin);

            radio += radioOffect;
            PosDataList.Add(data);
        }
    }

    /// <summary>
    /// 设置图片的位置数据(将第i个位置信息赋予第i个子物体)
    /// </summary>
    private void SetItemPosData()
    {
        for (int i = 0; i < RotationDiagramItemList.Count; i++)
        {
            RotationDiagramItemList[i].SetPos(PosDataList[i]);
        }
    }


    /// <summary>
    /// 用于获取X轴的位置
    /// </summary>
    /// <param name="radio">图片所在位置比例,如有三张图片,长度为1,则位置为0,0.33,0.66.</param>
    /// <param name="length">length为总长度,length = (图片长度 + 图片间间隔) * 图片数量</param>
    /// <returns></returns>
    private float GetX(float radio, float length) 
    {
        if (radio < 0 || radio > 1)
        {
            Debug.LogWarning("当前比例必须为 0 - 1 的值");
            return 0;
        }
        else if (radio >= 0 && radio < 0.25)
        {
            return radio * length;
        }
        else if (radio >= 0.25 && radio < 0.75)
        {
            return (0.5f - radio) * length;
        }
        else
        {
            return (radio - 1) * length;
        }
    }

    //获取缩放系数
    public float GetScaleTimes(float radio,float max ,float min)
    {
        if (radio < 0 || radio > 1)
        {
            Debug.LogWarning("当前比例必须为 0 - 1 的值");
            return 0;
        }

        float scaleOffect = (max - min) / 0.5f;

        if (radio < 0.5f)
            return max - scaleOffect * radio;
        else
            return max - scaleOffect * (1-radio);
    }
}


public classItemPosData
{
    public float X;
    public float ScaleTimes; // 缩放系数
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class RotationDiagramItem : MonoBehaviour
{

    //不用Start的原因:防止初始化时,别的地方调用时,该项时还未进行初始化
    private Image image;
    public Image Image
    {
        get
        {
            if (image == null)
                image = GetComponent<Image>();
            return image;
        }
    }

    private RectTransform rect;
    public RectTransform Rect
    {
        get
        {
            if (rect == null)
                rect = GetComponent<RectTransform>();
            return rect;
        }
    }

    //设置父物体
    public void SetParent(Transform parent)
    {
        this.transform.SetParent(parent);
    }

    //设置图片
    public void SetSprite(Sprite sprite)
    {
        Image.sprite = sprite;
    }

    //将位置数据赋予子物体
    public void SetPos(ItemPosData data)
    {
        Rect.anchoredPosition = data.X * Vector2.right;
        Rect.localScale = data.ScaleTimes * Vector3.one;
        Debug.Log(gameObject.name + ":" + Rect.anchoredPosition + ":" + Rect.localScale);
    }

}

我们会发现,后面的图片把前面的图片盖住了

所以我们需要对图片的层级进行排序

修改思路:

声明 结构体 ItemData ,用PosID来获取对应位置的数据,ItemPosData 中声明 order 来表示图片的层级
    在CalculateData 方法中, 声明List集合管理ItemData并在第一次for循环中对ItemData的PosID初始化并添加入集合,再对itemDatas中的PosID进行升序排序,利用for循环对PosDataList的order赋值(i= 0 → order = 2 → PosDataList[2].order = 0),再到子类RotationDiagramItem的SetPos方法中设置子类图片的层级(通过transform.SetSiblingIndex方法)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Linq;

public class RotationDiagram2D : MonoBehaviour
{
    public Vector2 ItemSize;  //图片大小
    public Sprite[] ItemSprites;
    public float Offect; //图片间距
    public float ScaleTimesMin; //最大缩放系数
    public float ScaleTimesMax; //最小缩放系数

    private List<RotationDiagramItem> RotationDiagramItemList;
    private List<ItemPosData> PosDataList;

    void Start()
    {
        PosDataList = new List<ItemPosData>();
        RotationDiagramItemList = new List<RotationDiagramItem>();
        CreateItem();
        CalculateData();
        SetItemPosData();
    }

    /// <summary>
    /// 创建物体模板
    /// </summary>
    GameObject CreateTemplate()
    {
        GameObject template = new GameObject();
        template.AddComponent<RectTransform>();
        template.AddComponent<Image>();
        template.AddComponent<RotationDiagramItem>();
        return template;
    }

    //创建UI物体,设置图片和父物体 并 将其放入RotationDiagramItemList中
    void CreateItem()
    {
        GameObject template = CreateTemplate();
        RotationDiagramItem item = null;
        foreach (Sprite sprite in ItemSprites)
        {
            item = Instantiate(template).GetComponent<RotationDiagramItem>();
            item.SetSprite(sprite);
            item.SetParent(transform);
            RotationDiagramItemList.Add(item);
        }
        Destroy(template);

    }

    //计算位置的方法
    private void CalculateData()
    {
        List<ItemData> itemDatas = new List<ItemData>();

        float length = (ItemSize.x + Offect) * RotationDiagramItemList.Count; // 长度为(图片长度 + 图片间间隔) * 图片数量
        float radioOffect = 1 / (float)RotationDiagramItemList.Count;   // 每个图片的等距间隔

        float radio = 0;
        for (int i = 0; i < RotationDiagramItemList.Count; i++)
        {
            ItemData itemData = new ItemData();
            itemData.PosID = i;
            itemDatas.Add(itemData);

            ItemPosData data = new ItemPosData();
            data.X = GetX(radio, length);
            data.ScaleTimes = GetScaleTimes(radio, ScaleTimesMax, ScaleTimesMin);

            radio += radioOffect;
            PosDataList.Add(data);
        }
        itemDatas = itemDatas.OrderBy(a => PosDataList[a.PosID].ScaleTimes).ToList(); //对itemDatas中的PosID进行升序排序

        for (int i = 0; i < itemDatas.Count; i++)
        {
            // i= 0 → order = 2 → PosDataList[2].order = 0 
            // 在前面的图片不能被后面的挡住,所以层级要更大
            PosDataList[itemDatas[i].PosID].order = i;
        }
    }

    /// <summary>
    /// 设置图片的位置数据(将第i个位置信息赋予第i个子物体)
    /// </summary>
    private void SetItemPosData()
    {
        for (int i = 0; i < RotationDiagramItemList.Count; i++)
        {
            RotationDiagramItemList[i].SetPos(PosDataList[i]);
        }
    }


    /// <summary>
    /// 用于获取X轴的位置
    /// </summary>
    /// <param name="radio">图片所在位置比例,如有三张图片,长度为1,则位置为0,0.33,0.66.</param>
    /// <param name="length">length为总长度,length = (图片长度 + 图片间间隔) * 图片数量</param>
    /// <returns></returns>
    private float GetX(float radio, float length) 
    {
        if (radio < 0 || radio > 1)
        {
            Debug.LogWarning("当前比例必须为 0 - 1 的值");
            return 0;
        }
        else if (radio >= 0 && radio < 0.25)
        {
            return radio * length;
        }
        else if (radio >= 0.25 && radio < 0.75)
        {
            return (0.5f - radio) * length;
        }
        else
        {
            return (radio - 1) * length;
        }
    }

    //获取缩放系数
    public float GetScaleTimes(float radio,float max ,float min)
    {
        if (radio < 0 || radio > 1)
        {
            Debug.LogWarning("当前比例必须为 0 - 1 的值");
            return 0;
        }

        float scaleOffect = (max - min) / 0.5f;

        if (radio < 0.5f)
            return max - scaleOffect * radio;
        else
            return max - scaleOffect * (1-radio);
    }
}


public class ItemPosData
{
    public float X;
    public float ScaleTimes; // 缩放系数
    public int order; //层级
}

public struct ItemData
{
    public int PosID;
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class RotationDiagramItem : MonoBehaviour
{
    public int PosID;



    //不用Start的原因:防止初始化时,别的地方调用时,该项时还未进行初始化
    private Image image;
    public Image Image
    {
        get
        {
            if (image == null)
                image = GetComponent<Image>();
            return image;
        }
    }

    private RectTransform rect;
    public RectTransform Rect
    {
        get
        {
            if (rect == null)
                rect = GetComponent<RectTransform>();
            return rect;
        }
    }

    //设置父物体
    public void SetParent(Transform parent)
    {
        this.transform.SetParent(parent);
    }

    //设置图片
    public void SetSprite(Sprite sprite)
    {
        Image.sprite = sprite;
    }

    //将位置数据赋予子物体
    public void SetPos(ItemPosData data)
    {
        Rect.anchoredPosition = data.X * Vector2.right;
        Rect.localScale = data.ScaleTimes * Vector3.one;
        transform.SetSiblingIndex(data.order);  //通过参数index决定物体在父类的第几个孩子
    }

}

图片的拖动与切换以及补间动画效果(DoTween)

通过IDragHandler, IEndDragHandler实现图片拖动的效果:

通过委托传递X轴偏移量来改变图片的PosID,再更新其位置信息 SetPos() 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System;
using DG.Tweening;

public class RotationDiagramItem : MonoBehaviour, IDragHandler, IEndDragHandler
{
    public int PosID;
    private float mOffsetX;
    private Action<float> mMoveAction;
    private float mAniTime = .5f;

    //不用Start的原因:防止初始化时,别的地方调用时,该项时还未进行初始化
    private Image image;
    public Image Image
    {
        get
        {
            if (image == null)
                image = GetComponent<Image>();
            return image;
        }
    }

    private RectTransform rect;
    public RectTransform Rect
    {
        get
        {
            if (rect == null)
                rect = GetComponent<RectTransform>();
            return rect;
        }
    }

    //设置父物体
    public void SetParent(Transform parent)
    {
        this.transform.SetParent(parent);
    }

    //设置图片
    public void SetSprite(Sprite sprite)
    {
        Image.sprite = sprite;
    }

    //将位置数据赋予子物体
    public void SetPos(ItemPosData data)
    {
        Rect.DOAnchorPos(data.X * Vector2.right, mAniTime);
        Rect.DOScale(data.ScaleTimes * Vector3.one, mAniTime);
        transform.SetSiblingIndex(data.order);  //通过参数index决定物体在父类的第几个孩子
    }

    public void OnDrag(PointerEventData eventData)
    {
        mOffsetX += eventData.delta.x;
    }

    public void OnEndDrag(PointerEventData eventData)
    {
        mMoveAction(mOffsetX);
        mOffsetX = 0;
    }

    public void AddMoveListener(Action<float> action)
    {
        mMoveAction = action;
    }

    public void ChangeID(int symbol,int total)
    {
        int id = PosID;
        id += symbol;
        if (id < 0)
        {
            id += total;
        }
        PosID = id % total;
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Linq;

public class RotationDiagram2D : MonoBehaviour
{
    public Vector2 ItemSize;  //图片大小
    public Sprite[] ItemSprites;
    public float Offect; //图片间距
    public float ScaleTimesMin; //最大缩放系数
    public float ScaleTimesMax; //最小缩放系数

    private List<RotationDiagramItem> RotationDiagramItemList;
    private List<ItemPosData> PosDataList;

    void Start()
    {
        PosDataList = new List<ItemPosData>();
        RotationDiagramItemList = new List<RotationDiagramItem>();
        CreateItem();
        CalculateData();
        SetItemPosData();
    }

    /// <summary>
    /// 创建物体模板
    /// </summary>
    GameObject CreateTemplate()
    {
        GameObject template = new GameObject();
        template.AddComponent<RectTransform>();
        template.AddComponent<Image>();
        template.AddComponent<RotationDiagramItem>();
        return template;
    }

    //创建UI物体,设置图片和父物体 并 将其放入RotationDiagramItemList中
    void CreateItem()
    {
        GameObject template = CreateTemplate();
        RotationDiagramItem item = null;
        foreach (Sprite sprite in ItemSprites)
        {
            item = Instantiate(template).GetComponent<RotationDiagramItem>();
            item.SetSprite(sprite);
            item.SetParent(transform);
            item.AddMoveListener(Change); //添加监听
            RotationDiagramItemList.Add(item);
        }
        Destroy(template);
    }

    private void Change(float offsetX)
    {
        //判断符号是正还是负
        int symbol = offsetX > 0 ? 1 : -1;
        Change(symbol);
    }

    //改变图片的PosID
    private void Change(int symbol)
    {
        //为每个子类切换自身ID
        foreach (RotationDiagramItem item in RotationDiagramItemList)
        {
            item.ChangeID(symbol, RotationDiagramItemList.Count);
        }

        for (int i = 0; i < PosDataList.Count; i++)
        {
            RotationDiagramItemList[i].SetPos(PosDataList[RotationDiagramItemList[i].PosID]);
        }
    }

    //计算位置的方法
    private void CalculateData()
    {
        List<ItemData> itemDatas = new List<ItemData>();

        float length = (ItemSize.x + Offect) * RotationDiagramItemList.Count; // 长度为(图片长度 + 图片间间隔) * 图片数量
        float radioOffect = 1 / (float)RotationDiagramItemList.Count;   // 每个图片的等距间隔

        float radio = 0;
        for (int i = 0; i < RotationDiagramItemList.Count; i++)
        {
            ItemData itemData = new ItemData();
            itemData.PosID = i;
            itemDatas.Add(itemData);

            RotationDiagramItemList[i].PosID = i;

            ItemPosData data = new ItemPosData();
            data.X = GetX(radio, length);
            data.ScaleTimes = GetScaleTimes(radio, ScaleTimesMax, ScaleTimesMin);

            radio += radioOffect;
            PosDataList.Add(data);
        }
        itemDatas = itemDatas.OrderBy(a => PosDataList[a.PosID].ScaleTimes).ToList(); //对itemDatas中的PosID进行升序排序

        for (int i = 0; i < itemDatas.Count; i++)
        {
            // i= 0 → order = 2 → PosDataList[2].order = 0 
            // 在前面的图片不能被后面的挡住,所以层级要更大
            PosDataList[itemDatas[i].PosID].order = i;
        }
    }

    /// <summary>
    /// 设置图片的位置数据(将第i个位置信息赋予第i个子物体)
    /// </summary>
    private void SetItemPosData()
    {
        for (int i = 0; i < RotationDiagramItemList.Count; i++)
        {
            RotationDiagramItemList[i].SetPos(PosDataList[i]);
        }
    }

    /// <summary>
    /// 用于获取X轴的位置
    /// </summary>
    /// <param name="radio">图片所在位置比例,如有三张图片,长度为1,则位置为0,0.33,0.66.</param>
    /// <param name="length">length为总长度,length = (图片长度 + 图片间间隔) * 图片数量</param>
    /// <returns></returns>
    private float GetX(float radio, float length)
    {
        if (radio < 0 || radio > 1)
        {
            Debug.LogWarning("当前比例必须为 0 - 1 的值");
            return 0;
        }
        else if (radio >= 0 && radio < 0.25)
        {
            return radio * length;
        }
        else if (radio >= 0.25 && radio < 0.75)
        {
            return (0.5f - radio) * length;
        }
        else
        {
            return (radio - 1) * length;
        }
    }

    //获取缩放系数
    public float GetScaleTimes(float radio, float max, float min)
    {
        if (radio < 0 || radio > 1)
        {
            Debug.LogWarning("当前比例必须为 0 - 1 的值");
            return 0;
        }

        float scaleOffect = (max - min) / 0.5f;

        if (radio < 0.5f)
            return max - scaleOffect * radio;
        else
            return max - scaleOffect * (1 - radio);
    }
}


public class ItemPosData
{
    public float X;
    public float ScaleTimes; // 缩放系数
    public int order; //层级
}

public struct ItemData
{
    public int PosID;
}


 


版权声明:本文为weixin_44003309原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。