效果图:
优缺点:
优点:
1.一条曲线完美解决旋转问题
2. 解决了超速的问题,现在速度再快也不会乱了
3.快速停止的时候进行了进度区分,后面的会等前面的停了再停
缺点:
1.停止节奏上会有细微差距,导致音频节奏不好,可以通过其他方式解决
代码结构:
简述:
ScrollElement:控制元素的图片切换,并记录自己是第几个进入Rect的号码
ScrollRect: 负责管理自己的旋转状态、曲线,堆排序等
(堆排序的目的是 单帧没办法判断有多少个元素从底部到达了顶部,这些元素都需要从列表中拿元素值,为了保证数据顺序,需要进行一次排序)
ScrollManager: 管理所有ScrollRect,并且与其他模块接口
代码:
/***************************************
Editor: Tason
Version: v1.0
Last Edit Date: 2018-XX-XX
Tel: 328791554@qq.com
Function Doc:
旋转元素
***************************************/ using UnityEngine; namespace DYHT
{
public class MyScrollRectElement : MonoBehaviour
{
//入队列的序号
private int m_realIndex;
public int RealIndex
{
get { return m_realIndex; }
set { m_realIndex = value; }
} //当前序号
public int m_currentElementId; public UISprite m_sprite; public string[] m_spritesNameList; //打开第几组图片
public void SetDisplayID(int _id)
{
//安全校验
if (_id < || _id > m_spritesNameList.Length - )
return; m_currentElementId = _id; m_sprite.spriteName = m_spritesNameList[_id];
}
}
}
/***************************************
Editor: Tason
Version: v1.1
Last Edit Date: 2018-XX-XX
Tel: 328791554@qq.com
Function Doc:
执行顺序 注意:
1.速度参数不要超过目标帧数,速度不要过快,就是1帧m_offsetValue 变化量不能超过1,按照30帧算 就是
0.0333f * scrollSpeed < 1 所以 scroll speed 就要小于30。
2.元素个数请务必保持偶数!!
3.位置曲线的 末节点 Key-Value值 Key(0.9999)Value(1) * ┌-┐ ┌-┐
* ┌--┘ ┴-------┘ ┴--┐~~
* │ 神 │ ~~
* │ --- │
* ████───████ │ ~~~~~
* │ │
* │ \/ │~~~
* │ │
* └--┐ ┌------┘
* │ │ ~~~~~~
* │ │ ~~~
* │ │ ~~~
* │ └-----------------┐
* │ │
* │ ├-┐
* │ ┌-┘
* │ │
* └-┐ ┐ ┌-------┬--┐ ┌┘~~~~~~
* │ -┤ -┤ │ -┤ -┤~~~~~
* └--┴--┘ └--┴--┘
* 神兽保佑
* 代码无BUG!
***************************************/ using UnityEngine;
using System.Collections.Generic; namespace DYHT
{
public class MyScrollRect : MonoBehaviour
{
#region Variable //滚动的n个阶段
[System.Serializable]
public enum ScrollState
{
Init, //初始化
Idle, //空闲
StartAni, //起步动画阶段
AddAndKeep, //加速&保持最高速阶段
ReduceSpeed, //减速阶段
SetFinal, //设置最终显示值阶段
EndAni, //最终动画播放阶段
}
[Header("显示部分")]
public ScrollState m_currentState; //当前旋转器序号
public int m_index; //动画结束委托
public delegate void EndScrollCallBack(int _index);
public EndScrollCallBack endScrollCallBack; //一条位移曲线 “\ \ \ \ \ 的曲线”
[Header("可调参数")]
public AnimationCurve m_positionCurve; //模拟列表
private List<int> m_simulationList; //最终选择列表
private List<int> m_finialList; //元素个数(必须为偶数且大于等于2)
public int m_elementCount = ; //模拟的元素个数
public int m_simulationCount = ; //模拟列表下标计数
private int m_simulationTempIndex; //总高度
private float m_verticalLength; //卡片的实际间隔
public float m_cellInterval = ; //起始位置
private float m_startPosition; //当前偏移值
private float m_offsetValue; //取号序列记录
private int m_keepScrollTemp; //目标值
private float m_targetValue; //一条加速度曲线 末尾一定要是1
public AnimationCurve m_scrollAddSpeedCurve; //当前速度 - 最大速度 - 最小速度 - 耗时 - 计时器 - 减速阶段目标速度
public float m_currentSpeed = ;
public float m_maxSpeed = ;
public float m_minSpeed = ;
public float m_addSpeedTime = 0.6f;
public float m_subSpeedTime = 0.6f;
private float m_addTimer = ;
private float m_subTimer = ; //元素间曲线间隔
private float m_curvalInterval; //最终展示的图片索引
private List<int> m_finalDisplayArray; //设置初始播放阶段动画偏移值 - 曲线 - 总时间 - 计时器 - 目标值 - 原值
public float m_startOffsetValue = 0.15f;
public AnimationCurve m_startAniCurve;
public float m_startAniTime = 0.1f;
private float m_startAniTimer = ;
private float m_startTargetValue;
private float m_startOrginalValue; //设置最终播放阶段动画偏移值 - 曲线 - 总时间 - 计时器 - 目标值 - 原值
public float m_finialOffsetValue = 0.1f;
public AnimationCurve m_finialAniCurve;
public float m_finialAniTime = 0.3f;
private float m_finialAniTimer = ;
private float m_finialTargetValue;
private float m_finialOrginalValue; //开始动画结束后进入速停的标记
private bool m_isStartEndToQuickStop = false; #endregion #region Quote //最大堆
private MaxHeap<MyScrollRectElement> m_maxHeap; //存放的元素
[HideInInspector]
public List<MyScrollRectElement> m_elements; #endregion #region SystemFunction
private void Awake()
{
//开堆
m_elements = new List<MyScrollRectElement>();
m_finalDisplayArray = new List<int>();
m_simulationList = new List<int>();
m_finialList = new List<int>();
m_maxHeap = new MaxHeap<MyScrollRectElement>(JudgeFunc0);
//m_sortElements = new List<MyScrollRectElement>();
} void Update()
{
float temp;
switch (m_currentState)
{
case ScrollState.StartAni:
if (m_startAniTimer + Time.deltaTime >= m_startAniTime)
{
m_offsetValue = m_startTargetValue;
ChangePosition();
m_currentState = ScrollState.AddAndKeep;
m_currentSpeed = ;
m_addTimer = ;
}
else
{
m_startAniTimer += Time.deltaTime;
ChangePosition();
}
break;
case ScrollState.AddAndKeep:
if (m_currentSpeed < m_maxSpeed)
{
m_addTimer += Time.deltaTime;
m_currentSpeed = JWTools.Remap(, m_addSpeedTime, , m_maxSpeed, m_addTimer);
m_currentSpeed = m_currentSpeed > m_maxSpeed ? m_maxSpeed : m_currentSpeed;
} temp = Time.deltaTime * m_currentSpeed;
temp -= Mathf.Floor(temp);
m_offsetValue += temp;
ChangePosition(); if (m_isStartEndToQuickStop)
{
m_isStartEndToQuickStop = false;
StopScroll();
}
break;
case ScrollState.ReduceSpeed:
if (m_currentSpeed > m_minSpeed)
{
m_subTimer = (m_subTimer + Time.deltaTime) > m_subSpeedTime ? m_subSpeedTime : (m_subTimer + Time.deltaTime);
m_currentSpeed = JWTools.Remap(, m_subSpeedTime, m_maxSpeed, m_minSpeed, m_subTimer);
m_currentSpeed = m_currentSpeed < m_minSpeed ? m_minSpeed : m_currentSpeed;
}
else
{
StopScroll();
} temp = Time.deltaTime * m_currentSpeed;
temp -= Mathf.Floor(temp);
m_offsetValue += temp; m_offsetValue += Time.deltaTime * m_currentSpeed;
ChangePosition();
break;
case ScrollState.SetFinal:
temp = Time.deltaTime * m_currentSpeed;
temp -= Mathf.Floor(temp);
m_offsetValue += temp;
ChangePosition();
break;
case ScrollState.EndAni:
if (m_finialAniTimer + Time.deltaTime >= m_finialAniTime)
{
m_offsetValue = m_finialTargetValue;
ChangePosition();
m_currentState = ScrollState.Idle; if (endScrollCallBack != null)
endScrollCallBack(m_index);
}
else
{
m_finialAniTimer += Time.deltaTime;
ChangePosition();
}
break;
default:
break;
}
} #endregion #region Other
public void Init(int _index, IList<int> _l)
{
m_index = _index; //安全校验
if (_l == null || _l.Count < m_elementCount || m_elementCount < )
{
WDebug.WDebugLog("Error Initialization!");
return;
} if (m_maxSpeed > Application.targetFrameRate)
WDebug.WDebugLog("Error : ScroolSpeed is too quick!"); //变量初始化
m_verticalLength = m_cellInterval * m_elementCount;
m_curvalInterval = 1.0f / m_elementCount;
m_offsetValue = 0f;
m_startPosition = -m_elementCount / * m_cellInterval;
m_targetValue = ;
m_simulationTempIndex = ;
m_currentState = ScrollState.Init; //生成元素
m_elements.Clear();
for (int i = ; i < m_elementCount; ++i)
{
AddElements(i, _l[i]);
} //添加模拟列表
m_simulationList.Clear();
for (int i = ; i < m_simulationCount; ++i)
{
m_simulationList.Add(Random.Range(, Const_DYHT.m_elementCount));
} //修正一次位置
ChangePosition();
} //设置元素 参0 当前序列编号 参1 初始图片下标
public void AddElements(int _num, int _picIndex)
{
//对象池生成一个实例
GameObject obj = JWABObjectPool.Instance.Spawn("ScrollElement") as GameObject;
obj.name = string.Format("Select{0}", _num);
obj.transform.SetParent(transform);
obj.transform.localScale = Vector3.one;
obj.transform.localPosition = new Vector3(, , ); obj.GetComponent<MyScrollRectElement>().SetDisplayID(_picIndex); //放入队列
m_elements.Add(obj.transform.GetComponent<MyScrollRectElement>());
obj.transform.GetComponent<MyScrollRectElement>().RealIndex = _num; //由于NGUI Panel关联的特性,还需要重新激活一次物体渲染
obj.SetActive(false);
obj.SetActive(true);
} //修改位置 & 检测是否需要修正图片
void ChangePosition()
{
switch (m_currentState)
{
case ScrollState.Init:
for (int i = ; i < m_elements.Count; ++i)
{
Vector3 nP = new Vector3(, m_startPosition + m_positionCurve.Evaluate(m_offsetValue + (m_elements[i].RealIndex + ) * m_curvalInterval) * m_verticalLength, );
m_elements[i].transform.localPosition = nP;
}
m_currentState = ScrollState.Idle;
break;
case ScrollState.StartAni:
//修正位置
for (int i = ; i < m_elements.Count; ++i)
{
if (m_startAniTime != )
m_offsetValue = Mathf.Lerp(m_startOrginalValue, m_startTargetValue, m_startAniCurve.Evaluate(m_startAniTimer / m_startAniTime));
else
m_offsetValue = m_startTargetValue;
m_elements[i].transform.localPosition = new Vector3(, m_startPosition + m_positionCurve.Evaluate(m_offsetValue + (m_elements[i].RealIndex + ) * m_curvalInterval) * m_verticalLength, );
}
break;
case ScrollState.ReduceSpeed:
//修正位置
for (int i = ; i < m_elements.Count; ++i)
{
Vector3 nP = new Vector3(, m_startPosition + m_positionCurve.Evaluate(m_offsetValue + (m_elements[i].RealIndex + ) * m_curvalInterval) * m_verticalLength, );
m_elements[i].transform.localPosition = nP;
}
break; case ScrollState.AddAndKeep:
case ScrollState.SetFinal:
//是否需要修正值
bool correctValueFlag = false; //引入最大堆排序 Step0 清空最大堆
m_maxHeap.Clear(); //修正位置
for (int i = ; i < m_elements.Count; ++i)
{
Vector3 nP = new Vector3(, m_startPosition + m_positionCurve.Evaluate(m_offsetValue + (m_elements[i].RealIndex + ) * m_curvalInterval) * m_verticalLength, ); //逻辑判断(★★★)
if (nP.y > m_elements[i].transform.localPosition.y)
{
//入堆前设置当前值
m_elements[i].transform.localPosition = nP;
if (m_currentState == ScrollState.AddAndKeep)
{
//切换图片
if (m_simulationList.Count > )
{
int n = (m_simulationTempIndex++) % m_simulationList.Count; m_elements[i].SetDisplayID(m_simulationList[n]);
}
else
WDebug.WDebugLog("Error!");
}
else if (m_currentState == ScrollState.SetFinal)
{
//入堆
m_maxHeap.Push(m_elements[i]);
}
}
else
m_elements[i].transform.localPosition = nP;
} //最大堆排序
while (m_maxHeap.Size > && m_keepScrollTemp > )
{
//Debug.Log(m_index + "-" + m_keepScrollTemp + " ---> " + m_offsetValue + "");
if (m_keepScrollTemp > )
{
m_maxHeap.Pop().SetDisplayID(m_finialList[m_finialList.Count - m_keepScrollTemp--]);
}
else
{
m_maxHeap.Pop().SetDisplayID(m_finialList[m_finialList.Count - m_keepScrollTemp--]); //修正位置 多向下走1/4的曲线距离
m_offsetValue -= m_offsetValue % m_curvalInterval + m_curvalInterval * m_maxHeap.Size - m_finialOffsetValue * m_curvalInterval;
//Debug.Log(m_index + " !!!! " + m_offsetValue); //记录位置 准备最后一段动画
m_currentState = ScrollState.EndAni;
m_finialAniTimer = ;
m_finialTargetValue = m_offsetValue - m_finialOffsetValue * m_curvalInterval;
m_finialOrginalValue = m_offsetValue; //值修正
correctValueFlag = true;
}
}
//值修正
if (correctValueFlag)
{
correctValueFlag = false;
for (int i = ; i < m_elements.Count; ++i)
{
m_elements[i].transform.localPosition = new Vector3(, m_startPosition + m_positionCurve.Evaluate(m_offsetValue + (m_elements[i].RealIndex + ) * m_curvalInterval) * m_verticalLength, );
}
}
break;
case ScrollState.EndAni:
//修正位置
for (int i = ; i < m_elements.Count; ++i)
{
if (m_finialAniTime != )
m_offsetValue = Mathf.Lerp(m_finialOrginalValue, m_finialTargetValue, m_finialAniCurve.Evaluate(m_finialAniTimer / m_finialAniTime));
else
m_offsetValue = m_finialTargetValue;
m_elements[i].transform.localPosition = new Vector3(, m_startPosition + m_positionCurve.Evaluate(m_offsetValue + (m_elements[i].RealIndex + ) * m_curvalInterval) * m_verticalLength, );
}
break;
}
} //设置最终List
public void SetFinalList(List<int> _l)
{
m_finialList.Clear();
for (int i = ; i < _l.Count; ++i)
m_finialList.Add(_l[i]);
} //更新模拟列表
public void SetSimulationList(List<int> _l)
{
m_simulationList.Clear();
for (int i = ; i < _l.Count; ++i)
{
m_simulationList.Add(_l[i]);
}
} //开始旋转
public void StartScroll()
{
//状态修改
m_currentState = ScrollState.StartAni; //参数设置
m_startAniTimer = ;
m_startOrginalValue = m_offsetValue;
m_startTargetValue = m_startOrginalValue - m_startOffsetValue * m_curvalInterval;
}
//停止旋转
public void StopScroll()
{
if (m_currentState == ScrollState.StartAni)
{
m_isStartEndToQuickStop = true;
}
else if (m_currentState == ScrollState.AddAndKeep || m_currentState == ScrollState.ReduceSpeed)
{ //参数设置
if (m_currentState == ScrollState.AddAndKeep)
m_currentSpeed = m_maxSpeed; //状态修改
m_currentState = ScrollState.SetFinal; m_addTimer = m_addSpeedTime;
m_keepScrollTemp = m_finialList.Count;
}
else if (m_currentState == ScrollState.SetFinal)
{
m_currentSpeed = m_maxSpeed;
}
} //减速
public void SubSpeed()
{
if (m_currentState == ScrollState.AddAndKeep)
{
//状态修改
m_currentState = ScrollState.ReduceSpeed; //参数设置
m_subTimer = ;
}
} //最大堆比较方法
private int JudgeFunc0(MyScrollRectElement e0, MyScrollRectElement e1)
{
if (e0.gameObject.transform.localPosition.y < e1.gameObject.transform.localPosition.y)
return ;
else if (e0.gameObject.transform.localPosition.y == e1.gameObject.transform.localPosition.y)
return ;
else
return -;
} //添加完成回调函数
public void AddEndCallBackFunc(EndScrollCallBack _func)
{
endScrollCallBack += _func;
} //清空完成回调
public void ClearEndCallBackFunc()
{
endScrollCallBack = null;
} ////排序后的元素 用于隐藏元素
//List<MyScrollRectElement> m_sortElements; ////隐藏几个元素 (从上到下排列 先排序再隐藏!)
//public void HideElementsPre()
//{
// m_maxHeap.Clear(); // for (int i = 0; i < m_elements.Count; ++i)
// m_maxHeap.Push(m_elements[i]); // m_sortElements.Clear(); // for (int i = 0; i < m_elements.Count; ++i)
// {
// m_sortElements.Add(m_maxHeap.Pop());
// }
//} //public void HideElement(int _index)
//{
// if (_index < 0 || _index > m_sortElements.Count - 1)
// return; // m_sortElements[_index].gameObject.SetActive(false);
//} ////打开所有元素
//public void ShowElements()
//{
// for (int i = 0; i < m_elements.Count; ++i)
// m_elements[i].gameObject.SetActive(true);
//} #endregion
}
}
/***************************************
Editor: Tason
Version: v1.0
Last Edit Date: 2018-XX-XX
Tel: 328791554@qq.com
Function Doc:
旋转窗口管理器
***************************************/ using UnityEngine;
using System.Collections;
using System.Collections.Generic; namespace DYHT
{
public class ScrollRectsManager : MonoBehaviour
{
#region variable
[System.Serializable]
public enum ScrollManagerState
{
Idle, //停止状态
Run, //正常停止状态
QuickStop //快停
}
public ScrollManagerState m_currentState; //第几个在运行
public int m_runIndex;
#endregion #region Quote public MyScrollRect[] m_mr; #endregion #region Function private void Start()
{
//初始化
List<int> temp = new List<int>();
for (int i = ; i < m_mr.Length; ++i)
{
temp.Clear();
for (int n = ; n < m_mr[].m_elementCount; n++)
{
int r = Random.Range(, Const_DYHT.m_elementCount);
temp.Add(r);
} if (m_mr[i] != null)
{
m_mr[i].Init(i, temp);
}
} //参数设置
m_currentState = ScrollManagerState.Idle;
m_runIndex = -; //事件添加
for (int i = ; i < m_mr.Length; ++i)
{
m_mr[i].ClearEndCallBackFunc();
m_mr[i].AddEndCallBackFunc(ReportState);
}
} //滚完回调
public void ReportState(int _index)
{
if (_index == m_mr.Length - )
{
m_currentState = ScrollManagerState.Idle;
m_runIndex = -; SceneService_DYHT.m_instance.m_bottomUIManager.OneTurnOver(); return;
}
else
m_runIndex = _index; if (m_currentState == ScrollManagerState.QuickStop)
{
//关闭所有协程
StopAllCoroutines(); //后续滚轮开始速停
for (int i = _index + ; i < m_mr.Length; ++i)
m_mr[i].StopScroll();
}
} #endregion
//全部滚轮速停
public void QuickStop()
{
if (m_currentState == ScrollManagerState.Run)
{
m_currentState = ScrollManagerState.QuickStop;
if (m_runIndex == -)
{
for (int i = ; i < m_mr.Length; ++i)
m_mr[i].StopScroll();
}
else
m_mr[((m_runIndex + ) > (m_mr.Length - )) ? m_mr.Length - : m_runIndex + ].StopScroll();
}
} //设置滚轮最终选项
public void SetFinialValue(List<int> _data)
{
if (_data.Count == )
{
List<int> final0 = new List<int>();
List<int> final1 = new List<int>();
List<int> final2 = new List<int>();
List<int> final3 = new List<int>();
List<int> final4 = new List<int>(); //这里注意顺序
final0.Add(_data[]); final0.Add(_data[]); final0.Add(_data[]); final0.Add();
final1.Add(_data[]); final1.Add(_data[]); final1.Add(_data[]); final1.Add();
final2.Add(_data[]); final2.Add(_data[]); final2.Add(_data[]); final2.Add();
final3.Add(_data[]); final3.Add(_data[]); final3.Add(_data[]); final3.Add();
final4.Add(_data[]); final4.Add(_data[]); final4.Add(_data[]); final4.Add(); m_mr[].SetFinalList(final0);
m_mr[].SetFinalList(final1);
m_mr[].SetFinalList(final2);
m_mr[].SetFinalList(final3);
m_mr[].SetFinalList(final4);
}
else
WDebug.WDebugLog("Error DataLength");
} //设置旋转
private IEnumerator SetSub(float _time, int _index)
{
yield return new WaitForSeconds(_time);
m_mr[_index].SubSpeed();
} //协程慢停
public void NormalStop(float _t)
{
StopAllCoroutines();
StartCoroutine(RealNormalStop(_t));
} //真.协程慢停
private IEnumerator RealNormalStop(float _t)
{
yield return new WaitForSeconds(_t); if (m_currentState != ScrollManagerState.Run)
yield break; float m_intervalTime = 0.15f; for (int i = ; i < m_mr.Length; ++i)
{
StartCoroutine(SetSub(m_intervalTime * i, i));
}
} //全部滚轮开始旋转
public void AllStartScroll()
{
StopAllCoroutines();
if (m_currentState == ScrollManagerState.Idle)
{
for (int i = ; i < m_mr.Length; ++i)
{
m_mr[i].StartScroll();
} m_currentState = ScrollManagerState.Run;
m_runIndex = -;
}
}
}
}
资源索引:
这个暂时不提供,有公司的美术资源和框架,后面离开公司以后再发上来