Unity

[ Unity ] 원형 슬라이더, Radial Slider(Circular Slider)

거두절me 2024. 9. 4. 19:23



 

원하는 거: 그냥 단순히.. 슬라이더를 원형으로 하고 싶었고 핸들을 잡고 직접 돌리고 싶다.

아래 사진 참고.

 

검은 점을 잡고 돌리는 슬라이더

 

내가 원하는 건 위 사진의 검은 점을 잡고 원형으로 돌렸을때, 그 위치를 따라오면서 원의 값이 바뀌었으면 했다.

원리를 쉽게 말하자면 아래 사진을 보면 된다.

내 마우스의 위치가 A에 있을때 B에 해당하는 위치에 슬라이더 값이 있어야 한다는 뜻이다.

 

슬라이더 핸들이 드래그 중인 마우스의 위치와 직선상에 있어야 한다는게 포인트.

 

 

 

소스를 바로 봐 보자.

Canvas의 Render Mode가 Camera인 경우

using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class CircularSlider : MonoBehaviour, IDragHandler, IPointerDownHandler
{
    public RectTransform handle; // 핸들 오브젝트
    public RectTransform centerPoint; // 중심점 오브젝트(따로 깡통 오브젝트 만들기)
    public Canvas canvas; // 현재 사용 중인 캔버스
    public Text valueText; // 값을 표시할 텍스트
    public Image fillImage; // 모드가 Radial 360 fill인 이미지

    public int maxChips = 280; // 최대 값 (100%에 해당)
    private float radius; // 중심점과 핸들 사이의 고정된 거리 (반지름)
    private float maxAngle = 260f; // 최대 각도 (260도)
    private float currentAngle = 0f; // 현재 핸들의 각도

    void Start()
    {
        // 중심점과 핸들 사이의 초기 거리(반지름)를 계산
        radius = Vector2.Distance(handle.anchoredPosition, centerPoint.anchoredPosition);
        UpdateHandlePosition(maxAngle); // 초기 위치를 0%로 설정
    }

    public void OnPointerDown(PointerEventData eventData)
    {
        UpdateHandlePosition(eventData);
    }

    public void OnDrag(PointerEventData eventData)
    {
        UpdateHandlePosition(eventData);
    }

    private void UpdateHandlePosition(PointerEventData eventData)
    {
        // 마우스의 화면 좌표를 캔버스의 로컬 좌표로 변환
        Vector2 localMousePosition;
        RectTransformUtility.ScreenPointToLocalPointInRectangle(centerPoint, eventData.position, canvas.worldCamera, out localMousePosition);

        // 중심점에서 마우스 위치 사이의 벡터를 계산
        Vector2 direction = (localMousePosition - (Vector2)centerPoint.anchoredPosition).normalized;

        // 마우스 위치로부터 각도 계산 (0~360도)
        float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
        if (angle < 0) angle += 360; // 각도를 0~360도로 조정

        // 시계방향으로 돌 때 각도 제한
        if (angle > maxAngle)
        {
            currentAngle = maxAngle; // 시계방향으로 최대값을 넘어가면 멈춤
        }
        else
        {
            currentAngle = Mathf.Clamp(angle, 0, maxAngle);
            // 핸들의 새로운 위치 계산: 중심점에서 방향 벡터에 반지름을 곱한 거리만큼 이동
            Vector2 newPosition = (Vector2)centerPoint.anchoredPosition + new Vector2(Mathf.Cos(currentAngle * Mathf.Deg2Rad), Mathf.Sin(currentAngle * Mathf.Deg2Rad)) * radius;

            // 핸들의 위치를 업데이트
            handle.anchoredPosition = newPosition;

            // 현재 값 및 이미지 업데이트
            UpdateValueText();
            UpdateFillImage();
        }

    }

    private void UpdateHandlePosition(float angle)
    {
        // 초기 위치 설정 함수
        currentAngle = angle;
        Vector2 newPosition = (Vector2)centerPoint.anchoredPosition + new Vector2(Mathf.Cos(currentAngle * Mathf.Deg2Rad), Mathf.Sin(currentAngle * Mathf.Deg2Rad)) * radius;
        handle.anchoredPosition = newPosition;
        UpdateValueText();
        UpdateFillImage();
    }

    private void UpdateValueText()
    {
        // 각도를 최대 칩 수로 변환하여 텍스트 업데이트
        // currentAngle이 최대일 때 0이 표시되도록 반대로 계산
int currentValue = Mathf.RoundToInt((1 - (currentAngle / maxAngle)) * maxChips);

        valueText.text = currentValue.ToString();
    }

    private void UpdateFillImage()
    {
        // 각도를 0에서 1 사이의 값으로 변환하여 Radial Fill에 적용
        // FillAmount는 핸들이 움직인 비율로 설정해야 합니다.
        fillImage.fillAmount = currentAngle / 360;
    }
}

 

 

 

주석을 친절히 써놨으니, 대강 알것이다...

 

 

 

결과물:

근데 지금 보니 내가 색상을 반대로 해놨네;; 숫자가 올라갈때 색상이 채워져야 하는데ㅇㅅㅇ ㅋㄷㅋㄷ

뭐 아무튼..

 

UI는 정말 딱 참고만 하시고...

내가 한 방법은.

두 개의 원 이미지를 만들고 레이어상 뒤에 원을 크게 만들어서 지금처럼 초록색을 넣고 모드를 radial 360 fill로 넣었다. 

핸들은 0이어야 하는 지점에 미리 가져다 놓았다.