zyeon's 작심삼일 코딩 공부
WorldToScreenPoint, ScreenToWorldPoint 본문
WorldToScreenPoint 는 월드 좌표계에 있는 오브젝트의 위치를 화면 좌표계로 변환할 때 사용한다.
반대로 ScreenToWorldPoint는 UI 요소의 위치를 월드 좌표계로 변환할 수 있다.
러닝 게임을 하다보면 코인을 획득하면 ui로 빨려 들어가는 모습을 흔히 볼 수 있다.
나는 맵에서 획득하는 아이템(별)을 캔버스에 있는 이미지로 이동하는걸 구현해보았다.
2D 처럼 보이지만 2.5D 느낌을 원해서 만든 게임이라 사실은 3D 게임이다.
(플레이어로 사용하는 캐릭터 이미지는 '가디언 테일즈' 캐릭터로 한 때 재밌게 플레이해서 가져왔다.)
처음에는 ScreenToWorldPoint 를 사용해 오브젝트를 화면 상의 위치로 변환한 후 이동시키도록 구현해 보았다.
private IEnumerator MoveToUIAni(GameObject obj, Transform target)
{
// 목표 UI의 위치를 월드 좌표계로 변환
Vector3 targetPos = Camera.main.ScreenToWorldPoint(target.position);
// 아이템이 월드 좌표로 변환한 UI 위치로 이동하는 애니메이션
// 지정한 시간(aniDuration) 동안 목표위치로 이동
obj.transform.DOMove(targetPos, aniDuration).SetEase(Ease.InOutQuad);
yield return new WaitForSeconds(aniDuration);
// 애니메이션이 끝나면 obj를 push 해주고 star text count++
Push(); //obj return
GameObject.Find("starText").GetComponent<StarCount>().GetSetStar += 1;
}
목표 UI의 월드 좌표계상 위치를 구한 다음, 아이템을 해당 위치로 옮겨주었다.
참고로 여기서 DoMove()는 DoTween 라이브러리에서 제공하는 함수로, 이를 사용해 간단하게 이동 애니메이션을 구현했다.
또, Push() 는 오브젝트 풀링 시스템에서 오브젝트를 반환해주는 역할이다. obj.SetActive(false) 와 같은 동작이라 보면 된다.
그런데 이 코드의 문제점이 있었다.
캔버스에 있는 UI들이 제일 위로 보여지기 때문에 이동하는 아이템이 다른 UI들의 밑으로 지나간다는 점이었다.
코드를 유지하고 해결하는 방법도 있었지만 찾아보니 카메라와 캔버스의 랜더 모드를 수정해야 했다.
내가 사용하는 캐릭터가 레이어도 많고 2.5D 느낌으로 만든 프로젝트라 그런지 카메라 설정을 건들이기엔 약간 복잡하다 생각했다.
그래서 다음은 플레이어가 아이템과 충돌하는 순간
아이템과 같은 위치에 미리 만들어둔 UI가 위치하게 한 뒤, 그 UI를 이동하도록 했다.
WorldToScreenPoint를 사용해 아이템의 화면 상 위치를 가져왔다.
private IEnumerator MoveToUIAni()
{
// 아이템의 월드 위치를 화면 위치로 변환
Vector2 itemPos = Camera.main.WorldToScreenPoint(transform.position);
//목적지 위치 저장, targetUI는 RectTransform형
Vector2 targetPos = targetUI.anchoredPosition;
RectTransformUtility.ScreenPointToLocalPointInRectangle(
canvas.GetComponent<RectTransform>(), //캔버스의 rectTransform
itemPos, //변환할 좌표
null, //카메라 렌더 모드에 따라 다름
out Vector2 localPoint); //변환된 로컬 좌표
// 원래 있던 아이템은 화면에 안보이게 함
Vector3 pos = transform.position;
pos.y = -5.0f;
transform.position = pos;
// 마찬가지로 starCopy는 RectTransform형
// 이동할 star copy의 시작 위치를 item 위치로 설정해줌
starCopy.localPosition = localPoint;
//star copy가 목표 ui까지 이동하는 애니메이션
starCopy.DOAnchorPos(targetPos, aniDuration).SetEase(Ease.InOutQuad);
yield return new WaitForSeconds(aniDuration);
//ui 변화
GameObject.Find("starText").GetComponent<StarCount>().GetSetStar += 1;
Push(); //obj return 비활 되면 코루틴 작동x 때문에 마지막에 해줌
}
RectTransformUtility.ScreenPointToLocalPointInRectangle()는 아이템의 화면 좌표 (itemPos) 를 캔버스의 RectTransform 을 기준으로 한 좌표로 변환 해준다. 변환한 좌표는 localPoint에 저장된다.
그 뒤에 아이템을 화면 밖으로 이동시켜 화면에 보이지 않게 해주었다.
해당 오브젝트가 비활성화 되면 코루틴이 더이상 작동하지 않기 때문에 일단 안보이게 해준뒤 마지막에 Push() 해주었다.
(근데 왜 이전 코드에서는 Push(); 이후의 코드가 잘 작동했는지는 모르겠다.)
마찬가지로 DoTween 라이브러리 함수를 사용했지만 RectTransform의 앵커 위치를 이동시키기 위해 DOAnchorPos()를 사용해주었다.
이펙트도 없고 애니메이션도 직선으로 이동하게 해서 약간 어색한거 같지만 결과는 의도한 대로 나왔다.
이 메서드들을 사용할 때 주의점은 UI가 다른 부모의 자식으로 속해있으면 내가 의도한 것과 다른 좌표가 나올 수 있다는 것이다.
기준으로 한 캔버스만 부모로 가지고 있을 수 있게 하자!
'unity' 카테고리의 다른 글
2.5D 게임에서 캐릭터 이동 구현 (0) | 2024.08.28 |
---|---|
오브젝트 풀링 (오브젝트 풀 패턴) (0) | 2024.08.24 |
싱글톤 패턴 (0) | 2024.08.22 |
Camera.main 오류 (CS1061) (0) | 2024.08.09 |
C#에서 람다식 (0) | 2024.08.08 |