Unity的图片轮播

  • Post author:
  • Post category:其他


1 效果展示

可以按左右按钮进行图片翻页,也可以鼠标滑动进行图片翻页,下方的文字是图片的名称

2 Unity的界面

注意在左边和右边的按钮上, 添加两个监听,左边按钮是ClickLeft(),右边是ClickRight().也可以在代码中添加监听(这里没写)

option设置

3核心代码

控制图片的滑动,外部导入图片是在StreamingAssets中的image文件夹下,注意文件夹命名一定要正确,否则会找不到,脚本挂载在RollingUI上面

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.UI;
public class Rolling : MonoBehaviour
{
    List<Sprite> imageList = new List<Sprite>();   //加载的图片的列表
    [Header("UI相关")]
    Text[] level;                              //关卡文本
    Image[] border;                            //选项边框
    [ColorUsage(true, false)]
    public Color originColor;                   //初始边框颜色
    [ColorUsage(true, false)]
    public Color firstColor;                    //最前项边框颜色

    [Header("关卡选项")]
    [SerializeField] GameObject optionPrefab;  //选择预制体
    Transform optionChild;                      //预制体的第一个子物体
    [SerializeField] Transform OPtionGroup;    //选择组父对象
    Transform[] options;                       //每个选项
    CanvasGroup[] cg;                          //选项组父对象
    [Range(0, 20)]
    [SerializeField] int optionNum;            //选项总数


    float halfNum;
    //Key 选项  value:选择位置
    Dictionary<Transform, Vector3> optionPos = new Dictionary<Transform, Vector3>();
    //key:选项  value:选项的SiblingIndex
    Dictionary<Transform, int> optionSib = new Dictionary<Transform, int>();
    #region 旋转
    [Header("旋转缩放")]
    Vector3 center = Vector3.zero;  //旋转中心
    float R = 500f;                 //旋转半径
    [Header("旋转速度")]
    [Range(1f, 10f)]
    [SerializeField] float speed = 5;   //旋转速度
    [Range(0, 100)]
    [SerializeField] float yOffest; //y轴偏移量

    #endregion

    [Range(0, 1)]
    [SerializeField] float minAlpha;   //最小的透明度

    #region 缩放
    [Header("缩放")]
    [Range(1, 5)]
    [SerializeField] float firstS;   //选中项的缩放度
    [Range(0, 1)]
    [SerializeField] float minS;     //最小项的缩放度
    [Range(0, 1)]
    [SerializeField] float tempS;    //旋转过程中的缩放度
    [Range(0, 0.5f)]
    [SerializeField] float smothSTime;      //缩放平滑时间
    #endregion


    Coroutine currentPIE;            //当前移动协程
    Coroutine[] SIE2;                //所有缩放的协程

    int first;                  //最前项序号

    [Header("鼠标相关")]
    Vector2 lastPos;//鼠标上次位置
    Vector2 currPos;//鼠标当前位置
    Vector2 offset;//两次位置的偏移值
    public string[] dirs;
    #region 初始化
    private void Awake()
    {
        optionNum = int.Parse(ConfigFile.LoadString("lookNum"));
        Debug.Log(optionNum);
        //从StreamingAssets中加载图片
        string imgtype = "*.JPG|*.PNG";
        string[] ImageType = imgtype.Split('|');
        List<string> filePathList = new List<string>();
        string[] dirs1 = Directory.GetFiles(Application.streamingAssetsPath + "/image/",  ImageType[0]);
        List<string> dirs1List = new List<string>(dirs1);
        string[] dirs2 = Directory.GetFiles(Application.streamingAssetsPath + "/image/",  ImageType[1]);
        //当两种格式都存在时,将第二个数组中的内容加入到第一个数组中去
        if (ImageType[0] != null && ImageType[1] != null)
        {
            for (int m = 0; m < dirs2.Length; m++)
            {
                string dir2 = dirs2[m];
                dirs1List.Add(dir2);
            }
            dirs = dirs1List.ToArray();
        }
        //当只有jpg格式时,将数组内容赋值
        else if (ImageType[0] != null && ImageType[1] == null)
            dirs = dirs1;
        //当只有png格式时,将数组内容赋值
        else if (ImageType[0] == null && ImageType[1] != null)
            dirs = dirs2;

        //排序
        Order(dirs);
            for (int j = 0; j < dirs.Length; j++)
            {
                Texture2D tx = new Texture2D(100, 100);
                tx.LoadImage(getImageByte(dirs[j]));
                Sprite sprite = Sprite.Create(tx, new Rect(0, 0, tx.width, tx.height), new Vector2(-500, -500));
                sprite.name = Path.GetFileNameWithoutExtension(dirs[j]);
                //将获取的图片加到图片列表中
                imageList.Add(sprite);
            
        }
        //生成预制体
        for (int i = 0; i < optionNum; i++)
        {
            GameObject go = Instantiate(optionPrefab, Vector3.zero, Quaternion.identity, OPtionGroup);
            go.name = i.ToString();
            int index = i;
            //当图片的数量小于生成的预制体的数量,让预制体的图片变成第一个
            if (index > imageList.Count - 1)
            {
                index = 0;
            }
            //将图片赋值到预制体上
            go.transform.Find("mask").GetComponent<Image>().sprite = imageList[index];


        }


        //数量得一半,对称处理用
        halfNum = optionNum / 2;
        //设置每一个元素位置
        options = new Transform[optionNum];
        cg = new CanvasGroup[optionNum];
        border = new Image[optionNum];
        SIE2 = new Coroutine[optionNum];
        level = OPtionGroup.GetComponentsInChildren<Text>();

        //设置每个元素的位置
        for (int i = 0; i < optionNum; i++)
        {
            options[i] = OPtionGroup.GetChild(i);
            cg[i] = options[i].GetComponent<CanvasGroup>();
            //设置关卡序号

            //将图片名称改为图片的名字
            level[i].text = imageList[i].name;
            //获取边框并设置颜色
            border[i] = options[i].GetChild(1).GetComponent<Image>();
            SetBorderColor(i, originColor);
        }
        //初始化位置
        InitPos();
        //设置层级
        InitSibling();
        //设置透明度
        SetAlpha();

        SetScale();

        //整体居中
        //StartCoroutine(AlignCenter());

        //对最前项进行特殊设置
        first = 0;
        SetFirst();

    }

    #endregion

    void Update()
    {
        //鼠标的位置的记录
        if (Input.GetMouseButtonDown(0))
        {
            lastPos = Input.mousePosition;
        }

        if (Input.GetMouseButtonUp(0))
        {
            currPos = Input.mousePosition;
            offset = currPos - lastPos;
            DoMatch(offset);
        }
    }
    public bool isASC ; //true 升序 

    #region 排序
    void Order(string[] dirs)
    {
        isASC = bool.Parse(ConfigFile.LoadString("sort"));
        //*正则表达式连接:https://learn.microsoft.com/zh-cn/dotnet/standard/base-types/substitutions-in-regular-expressions*/

        /*说明:
         * Array.Sort数组排序
         * x1.CompareTo(x2) x1与x2比较
         * Regex.Match 正则表达式匹配
         * Path.GetFileNameWithoutExtension 获取文件路径中文件名称(不带后缀)
         * @"\d+"在正则表达式中匹配1个或多个十进制数字
         */
        if (isASC)
            Array.Sort(dirs, (x1, x2) => int.Parse(Regex.Match(Path.GetFileNameWithoutExtension(x1), @"\d+").Value).CompareTo(int.Parse(Regex.Match(Path.GetFileNameWithoutExtension(x2), @"\d+").Value)));
        else
            Array.Sort(dirs, (x2, x1) => int.Parse(Regex.Match(Path.GetFileNameWithoutExtension(x1), @"\d+").Value).CompareTo(int.Parse(Regex.Match(Path.GetFileNameWithoutExtension(x2), @"\d+").Value)));
    }
    #endregion

    #region 鼠标移动控制图片的左右切换
    //鼠标移动
    void DoMatch(Vector2 _offset)
    {
        //水平移动
        if (Mathf.Abs(offset.x) > Mathf.Abs(offset.y))
        {
            if (offset.x > 0)
            {
                Debug.Log("左");
                //执行图片左移
                StartCoroutine(MoveLeft());

            }
            else
            {
                Debug.Log("右");
                //执行图片右移
                StartCoroutine(MoveRight());
            }
        }
        else//垂直移动
        {
            if (offset.y > 0)
            {
                Debug.Log("上");
            }
            else
            {
                Debug.Log("下");
            }
        }
    }
    #endregion

    //设置整体居中的协程
    IEnumerator AlignCenter()
    {
        //确保缩放完成
        for (int i = 0; i < optionNum; i++)
        {
            if (SIE2[i] != null)
            {
                yield return SIE2[i];
            }
        }

        float a = options[0].GetComponent<RectTransform>().rect.height * options[0].localScale.x / 2f;
        //偶数
        if (optionNum % 2 == 0)
        {
            float b = options[(int)halfNum].GetComponent<RectTransform>().rect.height * options[(int)halfNum].localScale.x / 2f;
            OPtionGroup.localPosition = new Vector3(0, (-halfNum * yOffest + a - b) / 2f, 0);
        }
        //奇数
        else
        {
            int temp = (optionNum - 1) / 2;
            float b = options[temp].GetComponent<RectTransform>().rect.height * options[temp].localScale.x / 2f;
            OPtionGroup.localPosition = new Vector3(0, (-temp * yOffest + a - b) / 2f, 0);
        }
    }

    //设置边框颜色
    void SetBorderColor(int i, Color c)
    {
        border[i].color = c;
    }


    //对当前选项做特殊处理
    void SetFirst()
    {
        //设置边框颜色
        SetBorderColor(first, firstColor);
        //启用互动
        cg[first].blocksRaycasts = true;
    }

    //按下按钮后先重置
    void Re()
    {
        //旋转过程中禁用互动
        cg[first].blocksRaycasts = false;
        //重置缩放
        ResetScale();
        //重置边框颜色
        SetBorderColor(first, firstColor);
    }

    //初始化位置
    void InitPos()
    {

        for (int i = 0; i < optionNum; i++)
        {
            float angle = (360f / (float)optionNum) * i * Mathf.Deg2Rad;

            float x = Mathf.Sin(angle) * R;
            float z = -Mathf.Cos(angle) * R;

            float y = 0;
            if (i != 0)
            {

                if (i > halfNum)
                {
                    //将大于中间数的元素的y值与其对称的那个元素一样
                    y = (optionNum - i) * yOffest;
                }
                else
                {
                    y = i * yOffest;
                }
            }

            //初始化位置和字典
            Vector3 temp = options[i].localPosition = new Vector3(x, y, z);
            optionPos.Add(options[i], temp);
        }
    }

    //设置元素的层级关系
    void InitSibling()
    {
        //设置顺序
        for (int i = 0; i < optionNum; i++)
        {
            //未过半
            if (i <= halfNum)
            {
                //偶数
                if (optionNum % 2 == 0)
                {
                    options[i].SetSiblingIndex((int)halfNum - i);
                }
                //奇数
                else
                {
                    options[i].SetSiblingIndex((int)((optionNum  / 2)) - i);
                }
            }

            //过半
            else
            {
                options[i].SetSiblingIndex(options[optionNum - i].GetSiblingIndex());
            }
        }

        //添加到字典
        for (int i = 0; i < optionNum; i++)
        {
            optionSib.Add(options[i], options[i].GetSiblingIndex());
        }
    }

    //根据深度(z)设置透明度
    void SetAlpha()
    {
        //计算z值起点,即当前选项,透明度最大
        float startZ = center.z - R;
        for (int i = 0; i < optionNum; i++)
        {
            cg[i].alpha = 1 - Mathf.Abs(options[i].localPosition.z - startZ) / (2 * R) * (1 - minAlpha);
        }
    }

    #region 设置大小

    //获取当前的index
    public int GetFirst()
    {
        for (int i = 0; i < optionNum; i++)
        {
            if (options[i].GetSiblingIndex() == optionNum - 1)
            {
                return i;
            }
        }
        //未找到标识
        return 233;
    }

    //重置缩放度
    void ResetScale()
    {
        foreach (Transform tf in options)
        {
            tf.localScale = Vector3.one * tempS;
        }
    }
    //设置大小
    void SetScale()
    {
        //  int first = GetFirst();
        float startZ = center.z - R;

        for (int i = 0; i < optionNum; i++)
        {
            //当前选项的放大率另行设置
            if (i == first)
            {
                SIE2[i] = StartCoroutine(SmoothScale(options[i], firstS));
            }
            else
            {
                float val = 1 - Mathf.Abs(options[i].localPosition.z - startZ) / (2 * R) * (1 - minS);
                // options[i].localScale = Vector3.one * val;
                SIE2[i] = StartCoroutine(SmoothScale(options[i], val));
            }
        }

    }

    //改变大小的协程
    IEnumerator SmoothScale(Transform tf, float targetS)
    {
        float temp = 0;
        while (Mathf.Abs(tf.localScale.x - targetS) > 0.001)
        {
            float s = Mathf.SmoothDamp(tf.localScale.x, targetS, ref temp, smothSTime);
            tf.localScale = Vector3.one * s;
            yield return null;
        }
    }
    #endregion

    #region 按住按钮左右移动
    //向左移动
    public void ClickLeft()
    {
        StartCoroutine(MoveLeft());
    }

    //向右移动
    public void ClickRight()
    {
        StartCoroutine(MoveRight());
    }

    //向左移动的协程
    IEnumerator MoveLeft()
    {
        if (currentPIE != null)
        {
            yield return currentPIE;
        }
        for (int i = 0; i < optionNum; i++)
        {
            if (SIE2[i] != null)
            {
                yield return SIE2[i];
            }
        }

        Re();

        //获取第零项的信息
        Vector3 p = optionPos[options[0]];
        int s = optionSib[options[0]];
        Vector3 targetP;

        for (int i = 0; i < optionNum; i++)
        {
            //当是最后一个元素的时候
            if (i == optionNum - 1)
            {
                targetP = p;
                optionSib[options[i]] = s;
            }
            else
            {
                targetP = options[(i + 1) % optionNum].localPosition;
                optionSib[options[i]] = optionSib[options[(i + 1) % optionNum]];
            }

            options[i].SetSiblingIndex(optionSib[options[i]]);
            currentPIE = StartCoroutine(MoveToTarget(options[i], targetP));

        }

        //设置最前项
        if (first == 0)
        {
            first = optionNum - 1;
        }
        else
        {
            first--;
        }
        SetFirst();

        //确保移动完成,设置缩放和透明度
        if (currentPIE != null)
        {
            yield return currentPIE;
        }
        SetAlpha();
        SetScale();
    }

    //向右移动的协程
    IEnumerator MoveRight()
    {
        if (currentPIE != null)
        {
            yield return currentPIE;
        }

        for (int i = 0; i < optionNum; i++)
        {
            if (SIE2[i] != null)
            {
                yield return SIE2[i];
            }
        }
        Re();
        //获取最后一项的信息
        Vector3 p = optionPos[options[optionNum - 1]];
        int s = optionSib[options[optionNum - 1]];
        Vector3 targetP;
        //从最后一个开始循环
        for (int i = optionNum - 1; i >= 0; i--)
        {
            //0->n
            if (i == 0)
            {
                //确定移动过目标的位置
                targetP = p;
                //更新SiblingIndex
                optionSib[options[i]] = s;
            }
            else
            {
                targetP = options[(i - 1) % optionNum].localPosition;
                optionSib[options[i]] = optionSib[options[(i - 1) % optionNum]];
            }

            options[i].SetSiblingIndex(optionSib[options[i]]);
            currentPIE = StartCoroutine(MoveToTarget(options[i], targetP));

        }

        //设置最前端
        if (first == optionNum - 1)
        {
            first = 0;
        }
        else
        {
            first++;
        }
        SetFirst();
        if (currentPIE != null)
        {
            yield return currentPIE;
        }
        SetAlpha();
        SetScale();
    }

    //穿入物体往目标位置移动的方法
    IEnumerator MoveToTarget(Transform tf, Vector3 target)
    {
        //物体离目标位置越远,移动的越快
        float tempSpeed = (tf.localPosition - target).magnitude * speed;
        //当物体与目标物体位置不同时开始移动
        while (tf.localPosition != target)
        {
            tf.localPosition = Vector3.MoveTowards(tf.localPosition, target, tempSpeed * Time.deltaTime);
            yield return null;
        }

        //更新字典里的位置Value
        optionPos[tf] = target;

    }
    #endregion

    public GameObject Background;

    #region 根据图片路径返回图片的字节流byte[]  

    private static byte[] getImageByte(string imagePath)
    {
        FileStream files = new FileStream(imagePath, FileMode.Open);
        byte[] imgByte = new byte[files.Length];
        files.Read(imgByte, 0, imgByte.Length);
        files.Close();
        return imgByte;
    }
    #endregion


}

新代码中,将图片的个数开放出来,然后通过xml文件外部控制图片数量,然后这个文件是放在StreamingAssets文件夹下面的,命名必须是ConfigFile.xml。

configFile代码  不需要挂载再游戏物体上

using System.Xml.Linq;
using UnityEngine;
using System;

/// <summary>
/// xml文件读取工具
/// </summary>
public class ConfigFile
{
    static string path = Application.dataPath + "/StreamingAssets/ConfigFile.xml";

    public static DateTime LoadDeadline()
    {
        XDocument document = XDocument.Load(path);
        //获取到XML的根元素进行操作
        XElement root = document.Root;
        XElement ele = root.Element("WarningDeadline");
        DateTime deadline = Convert.ToDateTime(ele.Value);  //string格式有要求,必须是yyyy - MM - dd hh: mm: ss
        return deadline;
    }

    //获取数据
    public static string LoadString(string str)
    {
        XDocument document = XDocument.Load(path);
        //获取到XML的根元素进行操作
        XElement root = document.Root;
        XElement ele = root.Element(str);
        return ele.Value;
    }

    //更新数据
    public static void UpdateDate(string name, string value)
    {
        XDocument document = XDocument.Load(path);
        //获取到XML的根元素进行操作
        XElement root = document.Root;
        root.SetElementValue(name, value);
        document.Save(path);
    }
}

这个也是我在b站比着教程做的,大家还有不懂的话,可以去看一下这个小姐姐的视频。


https://www.bilibili.com/video/BV1La411v7T2?spm_id_from=333.337.search-card.all.clickh

4、下载链接


unity图片轮播,图片可以外部修改,图片个数也可以外部修改_unity3d – Unity3D模型_U3D_免费下载-爱给网



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