티스토리 뷰

 

 

GTA의 이걸 만들고 싶었다.

 

 

일단 이런게 필요할 거 같아서 라인 렌더러를 사용하였다.

 

라인렌더러는 Positions 라는 vector3 값들을 가지고 있는데

 

인덱스 순서대로 각 벡터 위치를 이어주는 선을 그려준다.

이 값을 마우스 클릭 위치와 마우스가 드래그하여 이동한 위치로 갱신해주면 된다.

 

using UnityEngine;

public class LineDrag : MonoBehaviour
{
    [SerializeField]
    LineRenderer line;

    [SerializeField]
    float distance = 1;

    private void Start()
    { 
        line.gameObject.SetActive(false);
    }

    void Update()
    {
        Vector3 s2w = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, Camera.main.nearClipPlane + distance));

        if (Input.GetMouseButtonDown(0))
        {
            line.SetPosition(0, s2w);
            line.SetPosition(1, s2w);
            line.gameObject.SetActive(true);
        }

        else if (Input.GetMouseButton(0))
        {
            line.SetPosition(1, s2w);
        }

        else if (Input.GetMouseButtonUp(0))
        {
            line.gameObject.SetActive(false);
        }
    }
}

 

마우스의 위치는 Camera.main.ScreenToWorldPoint 를 사용해서 3D 좌표로 바꿔야한다.

 

그래서 UI 선택은 어떻게 할까?

 

 

1. 버튼 클릭이나 레이캐스트를 사용한 직접 선택?

2. UI를 6 등분하여 마우스를 드래그한 각도에 따라 범위 선택?

 

 

 

 

마우스를 떼었을 때 위치에 UI 이미지나 버튼이 있다면

Raycast 나 ButtonUp 이벤트를 통해 작업을 처리하면 되지만

게임 플레이 중에 커서 정확하게 UI 위에 올려둬야 해서 불편할지도 모른다.

하지만 딱히 UI를 선택하고 싶지 않을 때 UI 밖에서 마우스를 떼면 되기 때문에 이점도 있다.

 

using UnityEngine;
using UnityEngine.EventSystems;

public class ButtonUI : MonoBehaviour, IPointerEnterHandler,IPointerExitHandler
{
    public void OnPointerEnter(PointerEventData eventData)
    {
        transform.localScale = Vector3.one * 1.5f;
    }

    public void OnPointerExit(PointerEventData eventData)
    {
        transform.localScale = Vector3.one * 1f;
    }
}

 

일단은  IPointerEnterHandler, IPointerExitHandler 를 사용했다.

다른 방법으로는 역시 마우스 커서 위치로 부터 Raycast를 하는 것?

 

 

 

 

 

두 번째 방법으로는...

화면을 등분하여 드래그한 각도에 맞는 UI를 선택한다면?

원하는 방향으로 드래그만 하면 되니 편할 수 있다.

하지만 선택을 취소하려면 방법은 없고 원래 선택 중이던 UI를 다시 선택해야 할 것이다.

using UnityEngine;

public class MouseDragLine : MonoBehaviour
{
    [SerializeField]
    CircleUI ui;
    
    [SerializeField]
    LineRenderer line;

    [SerializeField]
    float distance = 1;

    private void Start()
    {
    	ui.gameObject.SetActive(false);
        line.gameObject.SetActive(false);
    }

    void Update()
    {
        Vector3 s2w = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, Camera.main.nearClipPlane + distance));

        if (Input.GetMouseButtonDown(0))
        {
            ui.transform.localRotation = Quaternion.identity;
            ui.transform.position = Input.mousePosition;
            ui.gameObject.SetActive(true);

            line.SetPosition(0, s2w);
            line.SetPosition(1, s2w);
            line.gameObject.SetActive(true);
        }
        else if (Input.GetMouseButton(0))
        {
            line.SetPosition(1, s2w);

            Vector3 vect = (line.GetPosition(1) - line.GetPosition(0)).normalized;
            ui.ResponsiveUI(Vector2.SignedAngle(vect,Vector2.down) + 180);
        }
        else if (Input.GetMouseButtonUp(0))
        {
            ui.gameObject.SetActive(false);
            line.gameObject.SetActive(false);
        }
    }
}

 

마우스 위치 - 시작 점 을 계산하고 그것을 Normalized 하여 정규화 한다.

그후 vector2 값으로 하여 Vector2.SignedAngle 을 계산한다.

 

ui.ResponsiveUI(Vector2.SignedAngle(vect,Vector2.up));

 

나는 시계를 보는 것처럼

12시 방향을

기준으로 하려고 위와 같이 작성했는데,

이렇게 하면 (0,1)을 기준으로 오른쪽으로는 0 ~ 180, 왼쪽으로는 0 ~ -180 을 반환하였다.

 

만약 UI 버튼이 짝수가 아니라면 180도에 걸쳐진 UI의 판정이 애매할 것 같아 보정을 하기로 했다.

ui.ResponsiveUI(Vector2.SignedAngle(vect,Vector2.down) + 180);

 

이렇게 하면 12시는 0도, 3시는 90도, 6시는 180도, 9시는 270도가 된다.

 

각도를 구했으면 모든 UI의 각도와 비교하여

반응하는 코드를 추가하면 된다.

public class CircleUI : MonoBehaviour
{
    [SerializeField]
    Image[] buttons;
    [SerializeField]
    float[] ranges;
    
    public void ResponsiveUI(float _angle)
    {
        IndexAngleCheck(_angle);
    }

    public void SellectButton(int _i)
    {
        buttons[_i].transform.localScale = Vector3.one * 1.5f;
    }

    public void UnsellectButton(int _i)
    {
        buttons[_i].transform.localScale = Vector3.one * 1;
    }

    void IndexAngleCheck(float _angle)
    {
        for (int i = 0; i < ranges.Length - 1; i++)
        {
            if (_angle >= ranges[i] && _angle < ranges[i + 1])
            {
                SellectButton(i);
            }
            else
                UnsellectButton(i);
        }

        if (_angle >= ranges[ranges.Length - 1] || _angle < ranges[0])
        {
            SellectButton(ranges.Length - 1);
        }
        else UnsellectButton(ranges.Length - 1);
    }
}

 

필요한 기능과 구현이 잔뜩있다.

1. 화면을 길게 눌러야 선택창이 나오게 하기

2. 정해진 범위 이상을 드래그 해야 UI를 선택할 수 있도록 제한하기

3. 선택할 옵션의 수에 따라 UI의 개수가 달라지게 만들기 등등...

 

게다가 아직도 UI 선택 기준에 대해 고민하고 있다...

 

 

'유니티 > Editor' 카테고리의 다른 글

UI버튼의투명영역이감지되지않게하고싶다  (0) 2020.07.09
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함