본문 바로가기
개발/Unity 내일배움캠프 TIL

Unity2D 궁수의 전설 UI 따라만들기 (3)

by 석시 2023. 9. 21.



다음의 영상을 참고하여 궁수의 전설 UI를 제작하고 있다.

궁수의 전설 UI 수직 수평 스크롤 뷰 완벽하게 만들기
예전부터 궁수의 전설 UI가 정말 궁금했는데, 만들었습니다! UI가 굉장히 세련되고 움직이는 게 신기해서 열심히 연구해 봤는데요 수직 수평 스크롤 뷰를 이용하면 많은 분야에서 멋진 UI를 만드실 수 있습니다 탭 버튼 움직이는 것도 만족스러워요~ 보너스로 초간단 탭 메뉴도 이 영상에 포함했습니다 아래 탭 클릭시 수직으로 올라가는 것은 블로그와 스크립트 수정되었습니다. # 스크립트 보기 https://goraniunity2d.blogspot.com/2020/02/ui.html # 프로젝트, 리소스 다운받기 https://drive.google.com/file/d/1QL3u_CEF9qQA8Yhf50fa2UIroacjlM7x/view?usp=sharing
https://youtu.be/K_ujyelRZUA?si=LyXqeZmW-dHWdYGU

지난 번 글까지는 각 패널의 세로 스크롤 뷰를 구현했었다.

Unity 2D 궁수의 전설 UI 따라만들기 (2)
다음의 영상을 참고하여 궁수의 전설 UI를 제작하고 있다. 궁수의 전설 UI 수직 수평 스크롤 뷰 완벽하게 만들기예전부터 궁수의 전설 UI가 정말 궁금했는데, 만들었습니다! UI가 굉장히 세련되고 움직이는 게 신기해서 열심히 연구해 봤는데요 수직 수평 스크롤 뷰를 이용하면 많은 분야에서 멋진 UI를 만드실 수 있습니다 탭 버튼 움직이는 것도 만족스러워요~ 보너스로 초간단 탭 메뉴도 이 영상에 포함했습니다 아래 탭 클릭시 수직으로 올라가는 것은 블로그와 스크립트 수정되었습니다. # 스크립트 보기 https://goraniunity2d.blogspot.com/2020/02/ui.html # 프로젝트, 리소스 다운받기 https://drive.google.com/file/d/1QL3u_CEF9qQA8Yhf50..
https://seoksii.tistory.com/48

이번 시간에는 밑의 탭 버튼을 구현해보자.


하단 탭버튼 만들기

이제 아래쪽의 탭버튼을 만들 차례이다.

가장 상위 Canvas 바로 하위에 Image를 하나 생성해주고 이름은 Tab으로 하자. PosY = -875, Width = 1080, Height = 180으로 세팅해주자.

그 뒤 Source Image를 1편에서 만들어놨던 R_4 스프라이트로 해주자.

패널 넘어다니는 버튼 만들기

그 후 Tab 하위에 Button을 만들어주자.

Tab 우클릭 → UI → Legacy → Button 클릭

버튼 하위의 Text 오브젝트는 지워주고, Source Image는 R_5로 해주자.

이를 네 번 복제해서 총 다섯개를 만들어주자.

그 후 위치를 맞춰주기 위해 Tab에다가 Horizontal Layout Group을 추가해주자.

버튼들이 약간 이상하게 배치되어 있는 것을 볼 수 있다.

Horizontal Layout Group에서 Control Child SizeWidthHeight를 한 번씩 체크했다가 해제해주면 원래 모습으로 잘 정렬이 된다.

이제 첫 번째 버튼의 Width360으로, 나머지 버튼들의 Width180으로 조정해주자.

이제 각 버튼들의 Inspector에서 OnClick()에 요소를 새로 추가하고, Scroll View를 가져다 넣어주자.

이제 함수를 만들어줄 것이다.

NestedScrollManager.cs에 다음과 같은 함수를 추가하자.

 
public void TabClick(int n)
{
    targetPos = pos[n];
}
 

이제 Inspector에서 연결만 해주면 끝이다.

마지막 버튼까지 쓰고 싶다면 NestedScrollManager에서 SIZE = 5로 바꿔주고 패널을 다섯개로 만들어주자.

버튼으로도 잘 넘어다닌다.

버튼 사이즈 조정하기

이제 내가 지금 있는 패널의 버튼을 다른 버튼보다 두 배 커지도록 만들어 줄 것이다.

Tab 우클릭 → UI → Slider 클릭

이름은 TabSlider로 해주고, Hierarchy에서 자식들 중 가장 상단에 위치하도록 위치를 옮겨주자.

Slider는 다른 버튼들보다 뒤에 있어야하기 때문이다.

또한 Slider 역시 크기를 가지고 있기 때문에 버튼 배치가 뭉개지는데 이를 위해 추가적인 설정을 해주자.

TabSlider에 Layout Element 추가 후 Ignore Layout 체크

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으로 바꿔주자.

Slider의 Value를 변경해보면 잘 움직인다.

우리는 이 슬라이더를 직접 조작할 것이 아니기 때문에 Slider 컴포넌트의 Interactable을 꺼주도록 하자.

그러면 색이 약간 희꾸무레하게 바뀌는데 Transition의 Color TintNone으로 바꿔주면 원래대로 돌아온다.

이제 다시 코드를 작성하자.

NestedScrollManager.cs에 다음 파트를 추가해주자.

 
[SerializeField] Slider tabSlider;

void Update()
{
    tabSlider.value = scrollbar.value;

    if (!isDrag) scrollbar.value = Mathf.Lerp(scrollbar.value, targetPos, 0.1f);
}

tabSlider를 연결해주고 확인해보면, 패널이 바뀔 때마다 Slider가 따라오는 것을 확인할 수 있다!

이제 어느 패널에 있는지에 따라 버튼들의 크기를 조정해주자.

NestedScrollManager.cs에 다음 파트를 추가하고 Inspector에서 Btn들을 지정해주자.

[SerializeField] RectTransform[] BtnRect;

이제 이 버튼들을 각각 접근해서 목표 버튼이라면 크기를 키워주고 그렇지 않다면은 원래 크기로 만들어 줄 것이다.

코드를 작성해주자.

몇 번 패널에 있는지를 알기 위해 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 TransformsizeDelta가 무엇일지 궁금해 할 사람들이 있을텐데, Rect TransformWidthHeightVector2 형태로 접근할 수 있는 속성이다.

탭버튼에 이미지 추가하기

이미지를 만들어서 꾸며줘보자.

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를 추가시켜주고, 아래에 배치한 뒤 글자를 써주자.

다 꾸미면 이렇게 된다.

이제 텍스트들은 전부 비활성화 해주자.

NestedScrollManager에서 RectTransform의 배열로 BtnImageRect를 선언해주고, Inspector에서 각 이미지들을 연결해주자.

 
[SerializeField] RectTransform[] BtnImageRect;
 

또 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 TransformanchoredPosition3D는 해당 Rect TransformPosition을 Vector3 형태로 받아오는 프로퍼티이다.

시간이 0.1f 이하일 때 함수를 종료해주는 이유는 처음에는 순간적으로 다른 이미지들의 위치가 0이기 때문이다.

실행시켜보면 슬라이드 시킬 때는 문제없이 아이콘들이 이동하는데 클릭하면 화면이동이 일어나지 않는 것을 알 수 있다.

이것은 이미지가 버튼들을 가리고 있어서 그런건데, Image 컴포넌트의 Raycast Target이라는 성분을 끄면 해결된다.

이제 우리는 활성화된 탭의 이미지가 위로 살짝 올라오면서, 크기가 커지고, 텍스트가 활성화되도록 만들어주고 싶다.

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);
    }
}
 

이제 완성이다!

이제 각 패널에다가 표시할 컨텐츠를 만들어주기만 하면 되는 것이다.

이렇게 디테일을 하나하나 살려주는 것이 직접 만져봤을 때 느낌이 너무 좋은 것 같다.

여러분도 시간이 난다면 꼭 따라해서 만들어보도록 하자.


Uploaded by N2T