Files
BeatSaber/Assets/VRBeatsKit/Scripts/UI/Scroller.cs
T
whdwo798 4dad9e5d5b feat: SongCreator 씬 완성 — Beat Sage URL 지원, info.dat 메타데이터 자동 추출
- BeatSageUploader: audio_url 지원(UploadFromUrl), PollAndDownload 공통화, ZIP 500 오류 3회 재시도
- BeatSageConverter: info.dat 파싱(SongMetadata), BPM 자동 감지 → 노트 타이밍 변환에 적용
- SongCreatorManager: title/BPM 필수 입력 제거, 난이도 4개 자동 선택, GenerateFlowFromUrl 버그 수정
- NasPublisher: audioPath null 허용(URL 흐름에서 로컬 파일 없는 경우 스킵)
- .gitignore/.gitattributes 초기 설정

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 23:37:34 +09:00

206 lines
5.8 KiB
C#

using System;
using System.Collections.Generic;
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using Platinio.UI;
/// <summary>
/// Code to handle generic scroll list, like leaderboards or achievements
/// </summary>
public class Scroller : MonoBehaviour , IPointerDownHandler , IPointerUpHandler , IPointerExitHandler
{
public enum ScrollMode
{
Horizontal,
Vertical
}
#region INSPECTOR
[SerializeField] protected ScrollRect scroll = null;
[SerializeField] protected GridLayoutGroup gridLayout = null;
[SerializeField] protected RectTransform viewRect = null;
[SerializeField] protected ScrollMode scrollMode = ScrollMode.Horizontal;
[SerializeField] protected bool shouldAling = false;
[SerializeField] protected float scrollVelocityThreshold = 0.2f;
#endregion
#region PRIVATE
private List<GameObject> elements = new List<GameObject>();
private RectTransform contentRect = null;
private Coroutine shouldAlingCoroutine = null;
private float minSize = 0.0f;
private bool userInteraction = false;
#endregion
protected RectTransform scrollRect = null;
protected Action onReachEndOfList = null;
public GridLayoutGroup GridLayout
{
get { return gridLayout; }
}
protected virtual void Awake()
{
scrollRect = scroll.GetComponent<RectTransform>();
contentRect = gridLayout.GetComponent<RectTransform>();
minSize = contentRect.rect.size.y;
SetupScrollingComponets();
//set listener for end of list
scroll.onValueChanged.AddListener( delegate (Vector2 v)
{
if (userInteraction && scroll.velocity.magnitude > scrollVelocityThreshold)
{
if (shouldAlingCoroutine != null)
StopCoroutine( shouldAlingCoroutine );
shouldAlingCoroutine = StartCoroutine( ShouldAlingRoutine() );
}
if (GetNormalizedPosition() <= 0.0f && onReachEndOfList != null)
{
onReachEndOfList();
}
} );
}
private void Start()
{
GetStartingElements();
ResizeContentRect();
}
private void GetStartingElements()
{
for (int n = 0; n < gridLayout.transform.childCount; n++)
{
AddElement( gridLayout.transform.GetChild(n).gameObject );
}
}
public float GetNormalizedPosition()
{
return scrollMode == ScrollMode.Horizontal ? scroll.horizontalNormalizedPosition : scroll.verticalNormalizedPosition;
}
private void SetupScrollingComponets()
{
gridLayout.startAxis = scrollMode == ScrollMode.Horizontal ? GridLayoutGroup.Axis.Horizontal : GridLayoutGroup.Axis.Vertical;
scroll.vertical = scrollMode == ScrollMode.Vertical;
scroll.horizontal = scrollMode == ScrollMode.Horizontal;
}
/// <summary>
/// add element to scroll list
/// </summary>
public void AddElement(GameObject element)
{
element.transform.parent = gridLayout.transform;
element.transform.localScale = Vector3.one;
elements.Add( element );
ResizeContentRect();
}
private IEnumerator ShouldAlingRoutine()
{
while (scroll.velocity.magnitude > 0.00001f)
{
yield return new WaitForEndOfFrame();
}
AlingToCloserElement();
}
private void AlingToCloserElement()
{
userInteraction = false;
Vector2 pos = elements[0].GetComponent<RectTransform>().FromAnchoredPositionToAbsolutePosition(viewRect);
Debug.Log(pos);
}
/// <summary>
/// Resizes the content rect to fit new elements
/// </summary>
protected void ResizeContentRect()
{
float size = 0.0f;
if (scrollMode == ScrollMode.Horizontal)
{
size = ((gridLayout.cellSize.x + gridLayout.spacing.x) * elements.Count) + gridLayout.padding.left;
}
else if (scrollMode == ScrollMode.Vertical)
{
size = (( gridLayout.cellSize.y + gridLayout.spacing.y ) * elements.Count) + gridLayout.padding.top;
}
//resize contentRect to fit new element
if (size > minSize)
{
if (scrollMode == ScrollMode.Vertical)
{
contentRect.SetSizeWithCurrentAnchors( RectTransform.Axis.Vertical, size );
}
else if (scrollMode == ScrollMode.Horizontal)
{
contentRect.SetSizeWithCurrentAnchors( RectTransform.Axis.Horizontal, size );
}
//reposition
Vector2 pos = contentRect.anchoredPosition;
if (scrollMode == ScrollMode.Vertical)
{
contentRect.anchoredPosition = new Vector2( pos.x, pos.y - ( ( gridLayout.cellSize.y + gridLayout.spacing.y ) / 2.0f ) );
}
else if (scrollMode == ScrollMode.Horizontal)
{
contentRect.anchoredPosition = new Vector2( pos.x + ( ( gridLayout.cellSize.x + gridLayout.spacing.x ) / 2.0f ), pos.y );
}
}
}
/// <summary>
/// Deletes all elements from list
/// </summary>
protected void ClearList()
{
for (int n = 0; n < elements.Count; n++)
{
Destroy( elements[n].gameObject );
}
elements = new List<GameObject>();
contentRect.SetSizeWithCurrentAnchors( RectTransform.Axis.Vertical, minSize );
}
public void OnPointerDown(PointerEventData eventData)
{
userInteraction = true;
}
public void OnPointerUp(PointerEventData eventData)
{
userInteraction = false;
}
public void OnPointerExit(PointerEventData eventData)
{
userInteraction = false;
}
}