티스토리 뷰
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 선택 기준에 대해 고민하고 있다...
'유니티' 카테고리의 다른 글
Layer Mask와 비트 연산 (0) | 2024.10.16 |
---|---|
Scroll View에 대한 사용법 정리 (0) | 2024.10.15 |
UI버튼의투명영역이감지되지않게하고싶다 (0) | 2020.07.09 |