/// Credit setchi (https://github.com/setchi) /// Sourced from - https://github.com/setchi/FancyScrollView using System.Collections.Generic; namespace UnityEngine.UI.Extensions { public class FancyScrollView<TData, TContext> : MonoBehaviour where TContext : class { [SerializeField, Range(float.Epsilon, 1f)] float cellInterval; [SerializeField, Range(0f, 1f)] float cellOffset; [SerializeField] bool loop; [SerializeField] GameObject cellBase; float currentPosition; readonly List<FancyScrollViewCell<TData, TContext>> cells = new List<FancyScrollViewCell<TData, TContext>>(); protected TContext context; protected List<TData> cellData = new List<TData>(); protected void Awake() { cellBase.SetActive(false); } /// <summary> /// コンテキストを設定します /// </summary> /// <param name="context"></param> protected void SetContext(TContext context) { this.context = context; for (int i = 0; i < cells.Count; i++) { cells[i].SetContext(context); } } /// <summary> /// セルを生成して返します /// </summary> /// <returns></returns> FancyScrollViewCell<TData, TContext> CreateCell() { var cellObject = Instantiate(cellBase); cellObject.SetActive(true); var cell = cellObject.GetComponent<FancyScrollViewCell<TData, TContext>>(); var cellRectTransform = cell.transform as RectTransform; // 親要素の付け替えをおこなうとスケールやサイズが失われるため、変数に保持しておく var scale = cell.transform.localScale; var sizeDelta = Vector2.zero; var offsetMin = Vector2.zero; var offsetMax = Vector2.zero; if (cellRectTransform) { sizeDelta = cellRectTransform.sizeDelta; offsetMin = cellRectTransform.offsetMin; offsetMax = cellRectTransform.offsetMax; } cell.transform.SetParent(cellBase.transform.parent); cell.transform.localScale = scale; if (cellRectTransform) { cellRectTransform.sizeDelta = sizeDelta; cellRectTransform.offsetMin = offsetMin; cellRectTransform.offsetMax = offsetMax; } cell.SetContext(context); cell.SetVisible(false); return cell; } #if UNITY_EDITOR float prevCellInterval, prevCellOffset; bool prevLoop; void LateUpdate() { if (prevLoop != loop || prevCellOffset != cellOffset || prevCellInterval != cellInterval) { UpdatePosition(currentPosition); prevLoop = loop; prevCellOffset = cellOffset; prevCellInterval = cellInterval; } } #endif /// <summary> /// セルの内容を更新します /// </summary> /// <param name="cell"></param> /// <param name="dataIndex"></param> void UpdateCellForIndex(FancyScrollViewCell<TData, TContext> cell, int dataIndex) { if (loop) { dataIndex = GetLoopIndex(dataIndex, cellData.Count); } else if (dataIndex < 0 || dataIndex > cellData.Count - 1) { // セルに対応するデータが存在しなければセルを表示しない cell.SetVisible(false); return; } cell.SetVisible(true); cell.DataIndex = dataIndex; cell.UpdateContent(cellData[dataIndex]); } /// <summary> /// 円環構造の index を取得します /// </summary> /// <param name="index"></param> /// <param name="length"></param> /// <returns></returns> int GetLoopIndex(int index, int length) { if (index < 0) { index = (length - 1) + (index + 1) % length; } else if (index > length - 1) { index = index % length; } return index; } /// <summary> /// 表示内容を更新します /// </summary> protected void UpdateContents() { UpdatePosition(currentPosition); } /// <summary> /// スクロール位置を更新します /// </summary> /// <param name="position"></param> protected void UpdatePosition(float position) { currentPosition = position; var visibleMinPosition = position - (cellOffset / cellInterval); var firstCellPosition = (Mathf.Ceil(visibleMinPosition) - visibleMinPosition) * cellInterval; var dataStartIndex = Mathf.CeilToInt(visibleMinPosition); var count = 0; var cellIndex = 0; for (float pos = firstCellPosition; pos <= 1f; pos += cellInterval, count++) { if (count >= cells.Count) { cells.Add(CreateCell()); } } count = 0; for (float pos = firstCellPosition; pos <= 1f; count++, pos += cellInterval) { var dataIndex = dataStartIndex + count; cellIndex = GetLoopIndex(dataIndex, cells.Count); if (cells[cellIndex].gameObject.activeSelf) { cells[cellIndex].UpdatePosition(pos); } UpdateCellForIndex(cells[cellIndex], dataIndex); } cellIndex = GetLoopIndex(dataStartIndex + count, cells.Count); for (; count < cells.Count; count++, cellIndex = GetLoopIndex(dataStartIndex + count, cells.Count)) { cells[cellIndex].SetVisible(false); } } } public sealed class FancyScrollViewNullContext { } public class FancyScrollView<TData> : FancyScrollView<TData, FancyScrollViewNullContext> { } }