수정된 설명과 코드, 이미지를 추가하여 새롭게 글을 작성했습니다.
재사용 스크롤 뷰의 경우 이곳에서 확인해주세요.
https://unity-programming-study.tistory.com/51
재사용 스크롤 뷰(Recyclable Scroll View)란?
유니티에서 재사용 스크롤 뷰(Recyclable Scroll View)는 많은 수의 아이템을 포함하는 스크롤 리스트를 효율적으로 관리하기 위한 기법입니다. 이 기술은 메모리 사용량을 줄이고 성능을 향상시키기 위해 사용됩니다.
1. 재사용 스크롤 뷰의 개념
일반적인 스크롤 뷰에선 리스트에 있는 모든 아이템을 UI 요소로 생성합니다. 만약 수백 개 이상의 아이템이 있는 경우, 모든 아이템을 한 번에 생성하면 많은 메모리와 CPU자원을 소모하며 버벅임이 발생할 수 있습니다. 재사용 스크롤 뷰는 이러한 문제를 해결하기 위해 사용됩니다.
2. 왜 재사용 스크롤 뷰를 사용하는가?
유니티에서 재사용 스크롤 뷰를 사용하는 주요 이유는 성능 최적화입니다. 다음은 재사용 스크롤 뷰를 사용해야 하는 몇가지 이유에 대해 서술하겠습니다.
1. 메모리 절약: 보이는 영역에만 필요한 만큼의 UI 요소를 생성하므로 메모리 사용량을 줄일 수 있습니다.
2. 성능 향상: 생성되는 UI 요소의 수가 적어지기 때문에 CPU 사용량이 줄어들고, 프레임률이 향상됩니다.
3. 부드러운 스크롤링: 적은 수의 UI 요소만을 관리하므로 많은 수 대비 스크롤링이 더 부드럽고 빠르게 동작합니다.
구현 코드
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class ReusableScrollList : MonoBehaviour
{
[SerializeField] private ScrollRect _scrollRect; // ScrollRect 컴포넌트
[SerializeField] private GameObject _slotPrefab; // 아이템 프리팹
[SerializeField] private Transform _content; // Content 트랜스폼
[SerializeField] private int _itemCount = 100; // 아이템 개수
[SerializeField] private int _bufferItems = 10; // 여유 있게 로드할 아이템 개수
private LinkedList<GameObject> _itemList = new LinkedList<GameObject>();
private RectTransform _contentRect;
private float _itemHeight;
private int _tmpfirstVisibleIndex;
private int _poolSize;
private void Start()
{
_contentRect = _content.GetComponent<RectTransform>();
// 슬롯 높이
_itemHeight = _slotPrefab.GetComponent<RectTransform>().rect.height;
//content sizeDelta.y크기
float contentHeight = _itemHeight * _itemCount;
// content y크기에 맞춰 슬롯 갯수 할당 + 예비 슬롯 * 2
_poolSize = (int)(_scrollRect.GetComponent<RectTransform>().sizeDelta.y / _itemHeight) + _bufferItems * 2;
// content 사이즈 조절
_contentRect.sizeDelta = new Vector2(_contentRect.sizeDelta.x, contentHeight);
// 슬롯 생성 및 리스트에 추가
for (int i = 0; i < _poolSize; i++)
{
GameObject item = Instantiate(_slotPrefab, _content);
_itemList.AddLast(item);
}
//슬롯 초기 설정
SlotInit();
// ScrollRect 자식의 위치가 변경되면 실행되도록 이벤트 등록
_scrollRect.onValueChanged.AddListener(OnScroll);
}
/// <summary>슬롯의 위치를 변경하는 함수</summary>
private void OnScroll(Vector2 scrollPosition)
{
float contentY = _contentRect.anchoredPosition.y;
//현재 contentRect의 y값에 맞춰 최소 인덱스를 구한다.
int firstVisibleIndex = Mathf.Max(0, Mathf.FloorToInt(contentY / _itemHeight) - _bufferItems);
//만약 이전 위치와 현재 위치가 달라졌다면?
if (_tmpfirstVisibleIndex != firstVisibleIndex)
{
//위치 인덱스의 차이를 구한다.
int diffIndex = _tmpfirstVisibleIndex - firstVisibleIndex;
//현재 인덱스가 더 크다면?
if (diffIndex < 0)
{
//그 인덱스 차이 만큼 반복해서 슬롯의 위치를 위에서 아래로 이동시킨다.
for (int i = 0, cnt = Mathf.Abs(diffIndex); i < cnt; i++)
{
GameObject item = _itemList.First.Value;
item.SetActive(true);
_itemList.RemoveFirst();
_itemList.AddLast(item);
item.transform.localPosition = new Vector3(0, (-(_tmpfirstVisibleIndex + _poolSize + i) * _itemHeight) - _itemHeight * 0.5f, 0);
if(_itemCount < (firstVisibleIndex + _poolSize + i))
{
item.SetActive(false);
}
else
{
item.SetActive(true);
}
}
}
//이전 인덱스가 더 크다면?
else if (diffIndex > 0)
{
//그 인덱스 차이 만큼 반복해서 슬롯의 위치를 아래에서 위로 이동시킨다.
for (int i = 0, cnt = Mathf.Abs(diffIndex); i < cnt; i++)
{
GameObject item = _itemList.Last.Value;
item.SetActive(true);
_itemList.RemoveLast();
_itemList.AddFirst(item);
item.transform.localPosition = new Vector3(0, (-(firstVisibleIndex - i) * _itemHeight) - _itemHeight * 0.5f, 0);
}
}
//위치 인덱스를 갱신한다.
_tmpfirstVisibleIndex = firstVisibleIndex;
}
}
/// <summary>슬롯 초기 설정</summary>
private void SlotInit()
{
int i = 0;
foreach(GameObject item in _itemList)
{
item.transform.localPosition = new Vector3(0, (-i * _itemHeight) - _itemHeight * 0.5f, 0);
item.GetComponentInChildren<Text>().text = (i + 1).ToString();
i++;
}
}
}
이해하기 편하시도록 주석을 통해 설명을 달아놨습니다.
위 코드는 유니티에서 재사용 스크롤 뷰를 구현하는 예제입니다. _slotPrefab을 이용해 필요한 수의 슬롯을 생성하고, 스크롤 이벤트 발생 시 보이는 영역의 아이템만 업데이트하여 재사용합니다.
실행 영상
보시는 것과 같이 Slot들이 Item갯수에 맞춰 생성되는 것이 아닌, 일정 량의 슬롯만 생성 후 일정 범위 밖으로 나갈 경우 위치가 변경되어 재사용하도록 합니다.
결론
재사용 스크롤 뷰는 많은 수의 아이템을 포함하는 스크롤 리스트를 효율적으로 관리하기 위한 최적화 기술입니다. 이를 통해 메모리와 CPU 자원 사용을 최적화하고, 성능을 크게 향상시킬 수 있습니다. 유니티에서 재사용 스크롤 뷰를 구현하면 더욱 부드럽고 빠른 사용자 경험을 제공하실 수 있기에 사용을 추천드립니다.
제가 면접에서 질문 받은 재사용 스크롤 뷰를 학습하기 위해 작성했습니다.
현재 글의 내용 중 틀린 부분이나, 지적하실 내용이 있으시다면 언제든지 알려주세요! 읽어주셔서 감사합니다.
'Unity 공부' 카테고리의 다른 글
[Unity] 속성(Attribute) (0) | 2024.08.24 |
---|---|
[Unity] 코루틴(Coroutine) (0) | 2024.08.04 |
[Unity] 카메라 이동(드래그) (0) | 2024.02.04 |
[Unity] 컷 아웃 마스크(Cutout Mask) (0) | 2024.01.24 |
[Unity] Behavior Tree(BT) (0) | 2023.12.11 |