다음 영상을 참고하여 제작하면서 나오는 방법들을 정리해보았다.
프로젝트 세팅
2D 프로젝트를 하나 새로 생성하고
화면비를 1080x1920
으로 만들자.
스프라이트 준비
위 영상에서 사용한 애셋은 다음 링크에서 받을 수 있다.
UI에 사용할 애셋들의 기본 설정을 해줘야 한다.
이제 Sprite Mode를 Multiple로 바꾸고 Sprite Editor에서 Sprite를 다음과 같이 잘라주자.
Horizontal Scroll View 세팅
Canvas 크기 설정
이 때 씬에 보이는 Canvas가 너무 크니 크기를 적당히 줄여주자.
그 후 Render Camera
를 씬의 Main Camera로 설정해주자.
이제부턴 작업이 제법 된다.
이렇게 만들면 비율이 정확하게 맞는다.
Scroll View 크기 설정
이 스크롤은 좌우로만 움직이게 하고 싶은데 세로로도 스크롤이 가능하다.
세로 스크롤이 가능하게 만들어주는 자식을 제거하자.
그렇게 만들면 가로로 밖에 스크롤이 되지 않는다.
하지만 스크롤 뷰의 바가 보이기 때문에 이를 보이지 않게 가려주도록 하자.
이제 Scroll View 안에 내용을 추가해주자.
다음과 같이 Scroll View를 생성했으면 Viewport 아래에 기본적으로 Content라는 자식이 추가되어 있는 것을 볼 수 있다.
이 때 같이 추가된 Component인 Content Size Filter
에서
Horizontal Fit
을 Preferred Size로 설정해주면
크기도 자동으로 맞춰주게 된다.
모든 크기를 Canvas size인 1080x1750.8
에 맞춰주자.
구분을 위해 각 패널의 색깔도 적절히 바꿔주자.
현재 상태로라면 세로로도 움직여지는데, 세로 움직임은 Scroll Rect에서 막을 수 있다.
이제 완벽하게 가로로만 움직인다.
자석처럼 붙는 화면 패널 만들기
위 화면에서 아무런 조작을 가하지 않을 때, 한 색깔만 보이게 자동으로 조정되도록 하고 싶다.
이를 구현해보자.
스크립트의 상단에 using UnityEngine.EventSystems;를 추가해주고, 다음과 같은 인터페이스들을 상속하게 해주자.
using UnityEngine;
using UnityEngine.EventSystems;
public class NestedScrollManager : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
}
이걸 사용하지 않고
만약 Update()
에서
Input.GetMouseButton(0)
을 사용해서 구현하면
해당 스크립트는 스크롤뷰를 참조하는 것이 아니라서
바깥을 마우스로 클릭해도 해당 스크립트가 실행이 된다.
해당 스크롤뷰 안에서만 마우스 입력을 받고 싶기 때문에 해당 작업들을 해주는 것이다.
IBeginDragHandler
, IDragHandler
, IEndDragHandler
는
UnityEngine.EventSystems
의 IEventSystemHandler
를 상속받는 애들이다.
해당 스크립트가 스크롤 뷰에 붙어 있기 때문에 이 범위 안에서만 이벤트를 수신받게 된다.
바깥은 클릭해도 이벤트 수신이 되지 않는 것이다.
IBeginDragHandler
는 드래그를 시작했을 때,
IDragHandler
는 드래그를 하는 중일 때,
IEndDragHandler
는 드래그가 끝났을 때
이벤트를 수신한다.
참고로 인터페이스의 내용을 스크립트 안에서 구현을 해줘야 하는데, 이 형태를 간단하게 하는 방법이 있다.
빨간 줄이 쳐진 인터페이스를 우클릭 → 빠른 작업 및 리팩터링을 클릭 후 인터페이스 구현 클릭
그러면 다음과 같이 인터페이스 구현부를 알아서 작성해준다.
이 때 eventData.delta를 참조할 수 있는데, 이는 마우스 포인터 위치의 순간변화율을 의미한다.
미분값이기 때문에 속도가 빠를수록 높게 나온다.
이제 스크립트를 다음과 같이 써주고, Inspector에서 다음과 같이 연결해주자.
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
public class NestedScrollManager : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
[SerializeField] Scrollbar scrollbar;
const int SIZE = 4; // Contents의 자식(패널) 개수
float[] pos = new float[SIZE]; // 각 패널의 Scrollbar value값 저장
float distance; // pos들 간의 간격
float targetPos; // 목표 위치 : Update에서 해당 위치로 서서히 움직임
bool isDrag; // 현재 드래그 중인지 아닌지 확인
void Start()
{
distance = 1f / (SIZE - 1);
for (int i = 0; i < SIZE; ++i) pos[i] = distance * i;
}
public void OnBeginDrag(PointerEventData eventData)
{
isDrag = true;
}
public void OnDrag(PointerEventData eventData)
{
}
public void OnEndDrag(PointerEventData eventData)
{
isDrag = false;
for (int i = 0; i < SIZE; ++i)
if (scrollbar.value < pos[i] + distance * 0.5f && scrollbar.value > pos[i] - distance * 0.5)
{ // 스크롤뷰 드래그를 멈추면 한 패널만 보이도록 위치를 조정
targetPos = pos[i];
}
}
void Update()
{
if (!isDrag) scrollbar.value = Mathf.Lerp(scrollbar.value, targetPos, 0.1f);
}
}
이동 위치가 반을 넘어가지 않아도 드래그 속도가 충분히 빠를 경우에 패널이 넘어가게 할 수 있을까?
코드를 다음과 같이 고쳐주자.
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
public class NestedScrollManager : MonoBehaviour, IBeginDragHandler, IEndDragHandler
{
[SerializeField] Scrollbar scrollbar;
[SerializeField] float thresholdSpeed = 18f;
const int SIZE = 4; // Contents의 자식(패널) 개수
float[] pos = new float[SIZE]; // 각 패널의 Scrollbar value값 저장
float distance; // pos들 간의 간격
bool isDrag; // 현재 드래그 중인지 아닌지 확인
float curPos; // 기존 위치
float targetPos; // 목표 위치 : Update에서 해당 위치로 서서히 움직임
void Start()
{
distance = 1f / (SIZE - 1);
for (int i = 0; i < SIZE; ++i) pos[i] = distance * i;
}
float SetPos()
{
for (int i = 0; i < SIZE; ++i)
if (scrollbar.value < pos[i] + distance * 0.5f && scrollbar.value > pos[i] - distance * 0.5)
{ // 절반거리를 기준으로 가까운 위치를 반환
return pos[i];
}
return 0f;
}
public void OnBeginDrag(PointerEventData eventData)
{
isDrag = true;
curPos = SetPos();
}
public void OnEndDrag(PointerEventData eventData)
{
isDrag = false;
targetPos = SetPos();
// 스크롤뷰 드래그를 멈추면 한 패널만 보이도록 targetPos를 설정
if (curPos == targetPos)
{
if (eventData.delta.x > thresholdSpeed && curPos - distance >= 0)
targetPos = curPos - distance;
else if (eventData.delta.x < -thresholdSpeed && curPos + distance < 1f)
targetPos = curPos + distance;
}
}
void Update()
{
if (!isDrag) scrollbar.value = Mathf.Lerp(scrollbar.value, targetPos, 0.1f);
}
}
이렇게 하면 속도가 충분히 빨라도 넘어가게 된다.
이제 각 패널마다 세로 스크롤을 추가해줄 차례다.
이는 다음 글에서 다루도록 하겠다.
Uploaded by N2T
'개발 > Unity 내일배움캠프 TIL' 카테고리의 다른 글
Unity2D 궁수의 전설 UI 따라만들기 (3) (0) | 2023.09.21 |
---|---|
Unity2D 궁수의 전설 UI 따라만들기 (2) (0) | 2023.09.20 |
사원수, 쿼터니언 (Quaternion) (1) | 2023.09.18 |
Unity에서 짐벌 락 (Gimbal Lock) 관찰하기 (0) | 2023.09.15 |
Unity 코루틴(Coroutine)으로 딜레이 구현하기 (1) | 2023.09.14 |