![](https://blog.kakaocdn.net/dn/btpc6O/btsxgpuLeqi/IVcia6hHOtgYqz9kkrDnOK/img.png)
다음의 영상을 참고하여 궁수의 전설 UI를 제작하고 있다.
![](https://www.youtube.com/s/desktop/8093e6f6/img/favicon_144x144.png)
![](https://i.ytimg.com/vi/K_ujyelRZUA/maxresdefault.jpg)
지난 번 글까지는 각 패널의 세로 스크롤 뷰를 구현했었다.
![](https://img1.daumcdn.net/thumb/R800x0/?scode=mtistory2&fname=https%3A%2F%2Ft1.daumcdn.net%2Ftistory_admin%2Fstatic%2Fimages%2FopenGraph%2Fopengraph.png)
이번 시간에는 밑의 탭 버튼을 구현해보자.
하단 탭버튼 만들기
이제 아래쪽의 탭버튼을 만들 차례이다.
가장 상위 Canvas 바로 하위에 Image를 하나 생성해주고 이름은 Tab으로 하자.
PosY
= -875, Width
= 1080, Height
= 180으로 세팅해주자.
그 뒤 Source Image를 1편에서 만들어놨던
R_4
스프라이트로 해주자.
패널 넘어다니는 버튼 만들기
그 후 Tab 하위에 Button을 만들어주자.
버튼 하위의 Text 오브젝트는 지워주고,
Source Image
는 R_5로 해주자.
이를 네 번 복제해서 총 다섯개를 만들어주자.
그 후 위치를 맞춰주기 위해
Tab에다가 Horizontal Layout Group
을 추가해주자.
![](https://blog.kakaocdn.net/dn/HBSee/btsxgVNPGky/CH49kkBeAZ22qu8BzubEsk/img.png)
버튼들이 약간 이상하게 배치되어 있는 것을 볼 수 있다.
Horizontal Layout Group
에서
Control Child Size
의 Width
와 Height
를
한 번씩 체크했다가 해제해주면 원래 모습으로 잘 정렬이 된다.
![](https://blog.kakaocdn.net/dn/cZQY6K/btsxtlRh6qC/ywMB5rv233XpPYEckIaU90/img.png)
이제 첫 번째 버튼의 Width
를 360으로,
나머지 버튼들의 Width
는 180으로 조정해주자.
![](https://blog.kakaocdn.net/dn/bgOajZ/btsxgqAoYWn/v4ZdDqZOudXCsNgUUnX6rK/img.png)
이제 각 버튼들의 Inspector에서
OnClick()
에 요소를 새로 추가하고,
Scroll View
를 가져다 넣어주자.
이제 함수를 만들어줄 것이다.
NestedScrollManager.cs에 다음과 같은 함수를 추가하자.
public void TabClick(int n)
{
targetPos = pos[n];
}
이제 Inspector에서 연결만 해주면 끝이다.
![](https://blog.kakaocdn.net/dn/okDD7/btsxrY91qV7/JV5sdTHzj2y5G1TTLcwgo0/img.png)
![](https://blog.kakaocdn.net/dn/cruIyk/btsxp6U9HtV/ZXSxe0oTwCbbjbVLbJnHB1/img.png)
![](https://blog.kakaocdn.net/dn/B8Ey3/btsxsnoiH8Q/7UOXGIb4LdpuP5ZHakPDNK/img.png)
![](https://blog.kakaocdn.net/dn/bmDuch/btsxtnO7SwY/7RMqnVVvjWugYirK32dxJk/img.png)
마지막 버튼까지 쓰고 싶다면
NestedScrollManager에서 SIZE = 5
로 바꿔주고
패널을 다섯개로 만들어주자.
![](https://blog.kakaocdn.net/dn/PvVjk/btsxkExRYzS/67j34dBDZDGrktxyCxjaKk/img.webp)
버튼 사이즈 조정하기
이제 내가 지금 있는 패널의 버튼을 다른 버튼보다 두 배 커지도록 만들어 줄 것이다.
이름은 TabSlider로 해주고, Hierarchy에서 자식들 중 가장 상단에 위치하도록 위치를 옮겨주자.
Slider는 다른 버튼들보다 뒤에 있어야하기 때문이다.
![](https://blog.kakaocdn.net/dn/xhJjZ/btsxg2e2bWS/7IAbZzQDfakKkyag9f28NK/img.png)
또한 Slider 역시 크기를 가지고 있기 때문에 버튼 배치가 뭉개지는데 이를 위해 추가적인 설정을 해주자.
Layout Element
에 Ignore Layout을 체크하게 되면
Horizontal Layout Group에 포함이 되지 않게 된다.
이제 TabSlider의 크기와 위치를
Pos X
= 270, PosY
= -90, Width
= 720, Height
= 180으로 맞춰준 뒤
자식 중 Background와 Fill Area는 지워주도록 하자.
우리는 Slider의 Handle Slide Area
만 사용할 것이기 때문이다.
그 후 여백이 없게
Handle Slide Area
의 Rect Transform의 상하좌우를 모두 0으로 만들자.
Handle Slide Area 하위에 있는 Handle의 경우
PosX
를 180, Width
를 360으로 설정하고,
Source Image를 R_6
으로 바꿔주자.
![](https://blog.kakaocdn.net/dn/q2eAM/btsxh5iouIu/K3POaNpyhQO6Eta7pQXaH1/img.webp)
우리는 이 슬라이더를 직접 조작할 것이 아니기 때문에
Slider 컴포넌트의 Interactable
을 꺼주도록 하자.
그러면 색이 약간 희꾸무레하게 바뀌는데
Transition의 Color Tint
를 None
으로 바꿔주면 원래대로 돌아온다.
이제 다시 코드를 작성하자.
NestedScrollManager.cs에 다음 파트를 추가해주자.
[SerializeField] Slider tabSlider;
void Update()
{
tabSlider.value = scrollbar.value;
if (!isDrag) scrollbar.value = Mathf.Lerp(scrollbar.value, targetPos, 0.1f);
}
tabSlider를 연결해주고 확인해보면, 패널이 바뀔 때마다 Slider가 따라오는 것을 확인할 수 있다!
![](https://blog.kakaocdn.net/dn/8laxr/btsxtoAuVCM/Hn6C1y9WwO7GsNPT4BFda1/img.webp)
이제 어느 패널에 있는지에 따라 버튼들의 크기를 조정해주자.
NestedScrollManager.cs에 다음 파트를 추가하고 Inspector에서 Btn들을 지정해주자.
[SerializeField] RectTransform[] BtnRect;
![](https://blog.kakaocdn.net/dn/b3QFKI/btsxsnBQ050/0sljJHF3Kzae59E8y7KUDK/img.png)
이제 이 버튼들을 각각 접근해서 목표 버튼이라면 크기를 키워주고 그렇지 않다면은 원래 크기로 만들어 줄 것이다.
코드를 작성해주자.
몇 번 패널에 있는지를 알기 위해 targetIndex를 추가해줄 것이다.
NestedScrollManager.cs
int targetIndex; // 목표 인덱스 : targetPos가 바뀔 때 같이 바뀜
float SetPos()
{
for (int i = 0; i < SIZE; ++i)
if (scrollbar.value < pos[i] + distance * 0.5f && scrollbar.value > pos[i] - distance * 0.5)
{ // 절반거리를 기준으로 가까운 위치를 반환
targetIndex = i;
return pos[i];
}
return 0f;
}
public void OnEndDrag(PointerEventData eventData)
{
isDrag = false;
targetPos = SetPos();
// 스크롤뷰 드래그를 멈추면 한 패널만 보이도록 targetPos를 설정
if (curPos == targetPos)
{
if (eventData.delta.x > thresholdSpeed && curPos - distance >= 0)
{
--targetIndex;
targetPos = curPos - distance;
}
else if (eventData.delta.x < -thresholdSpeed && curPos + distance < 1f)
{
++targetIndex;
targetPos = curPos + distance;
}
}
// 목표가 수직스크롤이고, 옆에서 옮겨왔다면 수직스크롤을 맨 위로 올림
for (int i = 0; i < SIZE; ++i)
if (curPos != pos[i] && targetPos == pos[i])
{
Transform child = contentTr.GetChild(i);
if (child.GetComponent<ScrollScript>() != null)
child.GetChild(1).GetComponent<Scrollbar>().value = 1;
break;
}
}
void Update()
{
tabSlider.value = scrollbar.value;
if (!isDrag)
{
scrollbar.value = Mathf.Lerp(scrollbar.value, targetPos, 0.1f);
// 목표 버튼은 크기가 커짐
for (int i = 0; i < SIZE; ++i) BtnRect[i].sizeDelta = new Vector2(i == targetIndex ? 360 : 180, BtnRect[i].sizeDelta.y);
}
}
public void TabClick(int n)
{
targetIndex = n;
targetPos = pos[n];
}
Rect Transform
의 sizeDelta
가 무엇일지 궁금해 할 사람들이 있을텐데,
Rect Transform
의 Width
와 Height
를 Vector2
형태로 접근할 수 있는 속성이다.
![](https://blog.kakaocdn.net/dn/DKota/btsxh9SsKXT/wRmBs5t2y7AUMPasfhGJEk/img.webp)
탭버튼에 이미지 추가하기
이미지를 만들어서 꾸며줘보자.
Tab 오브젝트 하위에 Image를 생성하고
역시 Horizontal Layout Group
에 영향을 받지 않아야 하기 때문에
Layout Element
를 추가시켜 Ignore Layout
을 체크해주자.
Rect Transform은
Pos X
= 180, Pos Y
= -84.65, Width
= 180, Height
= 180으로 설정하자.
Source Image는 R_8
또는 자신이 원하는 것을 넣어주자.
이제 Image의 자식으로 Text를 추가시켜주고, 아래에 배치한 뒤 글자를 써주자.
![](https://blog.kakaocdn.net/dn/bngCrd/btsxgVfWsa9/a2u47f6PeRmaOStYVBHuY0/img.png)
다 꾸미면 이렇게 된다.
![](https://blog.kakaocdn.net/dn/bTE3TP/btsxrVFshL7/90Pal9A1MpftPKVkm19sX1/img.png)
이제 텍스트들은 전부 비활성화 해주자.
NestedScrollManager에서
RectTransform
의 배열로 BtnImageRect
를 선언해주고,
Inspector에서 각 이미지들을 연결해주자.
[SerializeField] RectTransform[] BtnImageRect;
![](https://blog.kakaocdn.net/dn/cgrf4o/btsxqvgfhXp/A8zUTECxr9tiKRe44NOCTk/img.png)
또 NestedScrollManager.cs의 Update를 끝부분에 다음과 같은 코드를 추가하여주자.
void Update()
{
tabSlider.value = scrollbar.value;
if (!isDrag)
{
scrollbar.value = Mathf.Lerp(scrollbar.value, targetPos, 0.1f);
// 목표 버튼은 크기가 커짐
for (int i = 0; i < SIZE; ++i) BtnRect[i].sizeDelta = new Vector2(i == targetIndex ? 360 : 180, BtnRect[i].sizeDelta.y);
}
if (Time.time < 0.1f) return;
for (int i = 0; i < SIZE; ++i)
{
Vector3 BtnTargetPos = BtnRect[i].anchoredPosition3D;
BtnImageRect[i].anchoredPosition3D = Vector3.Lerp(BtnImageRect[i].anchoredPosition3D, BtnTargetPos, 0.25f);
}
}
여기서 Rect Transform
의 anchoredPosition3D
는
해당 Rect Transform
의 Position
을 Vector3 형태로 받아오는 프로퍼티이다.
시간이 0.1f 이하일 때 함수를 종료해주는 이유는 처음에는 순간적으로 다른 이미지들의 위치가 0이기 때문이다.
실행시켜보면 슬라이드 시킬 때는 문제없이 아이콘들이 이동하는데 클릭하면 화면이동이 일어나지 않는 것을 알 수 있다.
이것은 이미지가 버튼들을 가리고 있어서 그런건데,
Image
컴포넌트의 Raycast Target
이라는 성분을 끄면 해결된다.
![](https://blog.kakaocdn.net/dn/VwWc4/btsxspTYeL6/hMoQz2joojUP9rq7BZ8ppK/img.webp)
이제 우리는 활성화된 탭의 이미지가 위로 살짝 올라오면서, 크기가 커지고, 텍스트가 활성화되도록 만들어주고 싶다.
NestedScrollManager.cs의 Update를 다음과 같이 수정해주자.
void Update()
{
tabSlider.value = scrollbar.value;
if (!isDrag)
{
scrollbar.value = Mathf.Lerp(scrollbar.value, targetPos, 0.1f);
// 목표 버튼은 크기가 커짐
for (int i = 0; i < SIZE; ++i) BtnRect[i].sizeDelta = new Vector2(i == targetIndex ? 360 : 180, BtnRect[i].sizeDelta.y);
}
if (Time.time < 0.1f) return;
for (int i = 0; i < SIZE; ++i)
{
Vector3 BtnTargetPos = BtnRect[i].anchoredPosition3D;
Vector3 BtnTargetScale = Vector3.one;
bool textActive = false;
if (i == targetIndex)
{
BtnTargetPos.y = -23f;
BtnTargetScale = new Vector3(1.2f, 1.2f, 1);
textActive = true;
}
BtnImageRect[i].anchoredPosition3D = Vector3.Lerp(BtnImageRect[i].anchoredPosition3D, BtnTargetPos, 0.25f);
BtnImageRect[i].localScale = Vector3.Lerp(BtnImageRect[i].localScale, BtnTargetScale, 0.25f);
BtnImageRect[i].transform.GetChild(0).gameObject.SetActive(textActive);
}
}
이제 완성이다!
![](https://blog.kakaocdn.net/dn/cDGc8C/btsxgTCzlHb/45BrsXfMW1fqhbHkqB8YFk/img.webp)
이제 각 패널에다가 표시할 컨텐츠를 만들어주기만 하면 되는 것이다.
이렇게 디테일을 하나하나 살려주는 것이 직접 만져봤을 때 느낌이 너무 좋은 것 같다.
여러분도 시간이 난다면 꼭 따라해서 만들어보도록 하자.
Uploaded by N2T
'개발 > Unity 내일배움캠프 TIL' 카테고리의 다른 글
Unity 3D 타일맵 만들기 (0) | 2023.09.25 |
---|---|
Unity 어트리뷰트 (Attribute) (0) | 2023.09.22 |
Unity2D 궁수의 전설 UI 따라만들기 (2) (0) | 2023.09.20 |
Unity2D 궁수의 전설 UI 따라만들기 (1) (0) | 2023.09.19 |
사원수, 쿼터니언 (Quaternion) (1) | 2023.09.18 |