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 版权协议,转载请附上原文出处链接和本声明。