게임을 제작할 때 몬스터 사전에 들어갈 스크롤 뷰와 뷰를 움직이는 버튼이 필요했다.
구글에서 자료를 서치하면서 감사하게도 구현이 되어있는 코드를 발견했다.
아래 스크립트는 Unity에서 스크롤 뷰의 내용 (content)을 조작하여 위나 아래로 움직이는 기능을 구현하고 있다.
변수 정의
content: 스크롤 될 내용을 담고 있는 RectTransform이다.
count: 아마도 내용 항목의 총 개수를 나타내는 변수다.
pos: content의 현재 위치 (Y축)를 저장하는 변수다.
movepos: content가 이동해야 할 목표 위치 (Y축)를 나타낸다.
IsScroll: 현재 스크롤 중인지 여부를 나타내는 부울 변수다.
Start() 메서드
게임 오브젝트가 처음 활성화될 때 자동으로 호출된다.
content의 초기 위치를 pos에 저장하고, 이동해야 할 초기 위치를 계산하여 movepos에 저장한다.
그리고 디버그 로그를 출력하여 content의 최대 Y 위치, 최소 Y 위치, 그리고 계산된 이동 위치를 콘솔에 출력한다.
Up() 메서드
스크롤 뷰의 내용을 위로 움직이는 메서드다.
움직임의 경계를 검사하여 이미 최상단에 도달한 경우 추가로 움직이지 않는다.
그렇지 않은 경우, 스크롤 목표 위치를 계산하고, scroll() 코루틴을 시작하여 실제로 움직이게 한다.
Down() 메서드
스크롤 뷰의 내용을 아래로 움직이는 메서드다.
움직임의 경계를 검사하여 이미 최하단에 도달한 경우 추가로 움직이지 않는다.
그렇지 않은 경우, 스크롤 목표 위치를 계산하고, scroll() 코루틴을 시작하여 실제로 움직이게 한다.
scroll() 코루틴
실제로 스크롤 뷰의 내용을 움직이는 로직을 담고 있다.
Lerp 함수를 사용하여 부드럽게 스크롤될 내용을 움직인다.
내용이 거의 목표 위치에 도달하면 스크롤을 중지한다.
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class ScrollVieController : MonoBehaviour
{
[SerializeField] private RectTransform content;
public int count;
private float pos;
private float movepos;
private bool IsScroll = false;
void Start()
{
pos = content.localPosition.y;
movepos = content.rect.yMax - content.rect.yMax / count;
Debug.Log(content.rect.yMax);
Debug.Log(content.rect.yMin);
Debug.Log(content.rect.yMax - content.rect.yMax / count + "|" + pos);
}
public void Up()
{
if (content.rect.yMin + content.rect.yMax / count == movepos)
{
return;
}
IsScroll = true;
movepos = pos + content.rect.height / count;
pos = movepos;
StartCoroutine(scroll());
}
public void Down()
{
if (content.rect.yMax - content.rect.yMax / count == movepos)
{
return;
}
IsScroll = true;
movepos = pos - content.rect.height / count;
pos = movepos;
StartCoroutine(scroll());
}
IEnumerator scroll()
{
while (IsScroll)
{
content.localPosition = Vector2.Lerp(content.localPosition, new Vector2(0, movepos), Time.deltaTime * 5);
if (Vector2.Distance(content.localPosition, new Vector2(0, movepos)) < 0.1f)
{
IsScroll = false;
}
yield return null;
}
}
}
같은 역할을 하는 중복되는 코드를 리팩토링했다.
Up과 Down을 MoveContent라는 메서드로 중복을 제거했다.
MoveContent 메서드는 direction인자를 받아서 위 or 아래로 움직이는 로직을 처리한다.(1은 위, -1은 아래)
원래 코드에서는 경계를 검사하는 로직이 따로 있었지만, 부동 소수점 오차로 인한 문제를 줄이기 위해 Mathf.Approxtimately를 사용했다.
using UnityEngine;
using System.Collections;
public class ScrollVieController : MonoBehaviour
{
[SerializeField] private RectTransform content;
[SerializeField] private int count;
private float pos;
private float movepos;
private bool IsScroll = false;
private void Start()
{
pos = content.localPosition.y;
movepos = content.rect.yMax - content.rect.yMax / count;
Debug.Log(content.rect.yMax);
Debug.Log(content.rect.yMin);
Debug.Log(content.rect.yMax - content.rect.yMax / count + "|" + pos);
}
public void Up()
{
MoveContent(1);
}
public void Down()
{
MoveContent(-1);
}
private void MoveContent(int direction)
{
float boundaryCheckValue = direction > 0 ? content.rect.yMin + content.rect.yMax / count : content.rect.yMax - content.rect.yMax / count;
if (Mathf.Approximately(boundaryCheckValue, movepos))
{
return;
}
IsScroll = true;
movepos = pos + direction * content.rect.height / count;
pos = movepos;
StartCoroutine(scroll());
}
IEnumerator scroll()
{
while (IsScroll)
{
content.localPosition = Vector2.Lerp(content.localPosition, new Vector2(0, movepos), Time.deltaTime * 5);
if (Vector2.Distance(content.localPosition, new Vector2(0, movepos)) < 0.1f)
{
IsScroll = false;
}
yield return null;
}
}
}
포스몬 사전 개선하기.