본문 바로가기

취미로 하는 게임코딩_gameCodingAsHobby/유니티 구현 정리_Implement Summary

유니티 각개격파_019_스크린 좌표를 월드 좌표로 변환하기

이전 글에서 스크린 좌표계에서 사용하는 위치와 월드 좌표에서 사용하는 위치는 다르다고 했다.

그래서 월드 좌표에 있는 게임오브젝트와 스크린 좌표계를 사용하는 마우스 포인트의 위치는 서로 상호작용할 수 없다.

 

여기서는 마우스 포인트 위치를 월드 좌표로 변환하는 코드를 알아본다.

먼저 2D 환경 부터 테스트 해보자.

 

유니티 허브를 실행한 후 3D 모드의 프로젝트를 하나 만든다.

 

처음에는 2D 환경으로 테스트 하기 때문에 <각개격파018>글을 참고해서 2D환경으로 수정한다.

귀찮으면 그냥 2D 타입으로 프로젝트를 만들고, 3D 타입으로 테스트 할 때는 다시 하나 더 만들어도 상관없다.

여기서는 3D환경을 2D 환경으로 수정하여 테스트 한다.

 

Layout을 2by3으로 바꾼다.

 

Hierarchy창에 Sphere를 하나 만든다.

 

Position(위치)를 위와 같이 설정한다.

오른쪽 3점 옵션을 클릭하여 Reset을 클릭하면 간단하게 초기화할 수 있다.

 

2D 테스트이므로 위의 3가지 컴퍼넌트를 삭제한다.

마찬가지로 3점 옵션을 클릭하여 Remove Component(컴퍼넌트 삭제)를 클릭하면 간단하게 없앨 수 있다.

 

Add Component를 클릭한 후 Sprite Renderer를 추가한다.

 

Sprite와 Color, Scale을 위와 같이 설정한다.

 

 

이렇게 만든 2D 스프라이트(이미지)를 마우스 포인트와 함께 움직이도록 코드를 작성해 본다.

Sphere에 코드를 작성하기 위해 스크립트 컴퍼넌트를 만들어서 연결하자.

 

Add Component를 클릭한 후 제일 아래에 있는 New script를 선택하고 스크립트 이름은 ScreenToWorldPoint로 한다.

 

Assets폴더에 스크립트 파일이 만들어지고, Sphere 게임오브젝트에 스크립트 파일이 연결되었다.

스크립트 파일은 따로 폴더를 만들어서 관리하는 것이 좋다.

Assets폴더를 선택한 후 Project창 아래 +버튼을 누르고 Folder를 클릭해서 만든다.

 

폴더명은 Scripts로 수정하고 여기로 스크립트 파일을 옮긴다.

 

이제 스크립트 파일을 편집하기 위해 더블클릭한다.

 

7,8번 줄에 스크린 좌표를 사용하는 마우스 위치를 저장할 벡터3 타입의 변수를 만든다.

변수 이름은 mousePosition(마우스 위치)라고 하자.

그리고 월드 좌표로 변환한 위치를 저장할 벡터3 타입의 변수도 만든다.

변수 이름은 worldPosition(월드 위치)라고 하자.

 

17번 줄에 스크린 좌표를 월드 좌표로 변환하는 메서드를 만든다.

메서드명은 ScreenToWorld (스크린을 월드로) 라고 하자.

 

19번 줄에 마우스 위치를 변수 mousePosition에 저장한다.

마우스 현재 위치는 Input.mousePosition 으로 벡터3 값을 가져올 수 있다.

이때 z 값은 항상 0 이다.

 

21번 줄에 ScreenToWorldPoinst 메서드를 이용하여 월드 위치로 변환한 후 변수 worldPosition에 저장한다.

변환할 인수로 mousePosition을 전달한다.

추가로 z값이 문제가 될 수 있다.

 

위와 같이 만약 카메라의 z 값이 -10 일 경우 보정하지 않으면 월드 위치로 변환한 z값이 -10 이 되어 게임오브젝트가 카메라 시야에서 벗어나서 보이지 않게 된다.

 

카메라의 Near Clipping Plane 값이 0.3이라면 z값에 그 이상의 양수로 더해주면 카메라 시야에 나타나게 된다.

만약 z값을 10.0f 로 설정하면 월드 위치의 z값은 0 이 된다.

z값을 보정하지 않으려면 Near 값을 음수로 해서 카메라 뒤로 이동시키는 방법도 있다.

 

또는 위와 같이 Near값이 양수이면 카메라 앞쪽에 위치하므로 마우스 위치의 z값을 Near Clipping Plane 위치로 미리 수정하는 방법도 있다.

 

near clipping plane에 대해서는 <각개격파016, 카메라>를 참고한다.

 

23번 줄에는 변환된 월드 위치를 콘솔창에 보여주는 디버그 로그를 작성한다.

 

12번 줄에 ScreenToWorld메서드를 업데이트 메서드에 넣는다.

14번 줄에 게임오브젝트의 위치(transform.position)에 변환한 worldPosition을 넣어주면 녹색원을 월드 위치로 변환된 마우스 위치로 옮길 수 있다.

 

실행하면 마우스가 움직이는 위치로 게임오브젝트가 함께 이동한다.

 

 

3D 환경에서 스크린 좌표를 월드좌표로 변환하기

 

3D 환경에서는 마우스의 z 값을 조정하지 않으면 월드좌표로 변환했을 때 오브젝트가 카메라에 고정되어 보이지 않는다.

 

near clipping plane값을 조정하고 z값을 옮겨도 카메라 화면에는 제대로 표현되지 않는다.

결론적으로 이 문제는 카메라에서 마우스 위치 까지의 깊이에 원인이 있다.

3D환경에서는 게임오브젝트의 깊이가 모두 다르므로 마우스 포인터가 어디에 위치하느냐에 따라 깊이가 달라지므로 변환할 다른 방법을 찾아야 한다.

 

여기서는 가상의 광선(Ray)을 쏘아서 거리를 파악하는 방법을 알아본다.

 

먼저 3D 오브젝트 중에서 Plane (평면) 을 추가한다.

 

위치를 초기화하면 아래와 같이 된다.

 

 

메인 카메라를 위와 같이 수정한다.

 

Main Camera(메인카메라)에 ScreenToWorldPoint 라는 스크립트를 만들어 붙인다.

 

 

7,8번줄에 plane과 cube 게임오브젝트를 선언한다.

10번 줄에 MeshCollider 컴퍼넌트를 선언한다.

plane는 충돌을 감지할 수 있는 MeshCollider컴퍼넌트를 가지고 있는데 여기에 광선(Ray)을 쏘아서 위치를 알아낼 것이다.

 

11, 12번 줄에 월드 위치와 Ray 를 선언한다.

 

16,17번 줄 Start()메서드 에는 plane, cube 두 객체를 변수에 저장한다.

plane에 있는 메쉬콜라이더도 호출하여 저장한다.

 

28번 줄에 ScreenToWorld( )메서드를 작성한다.

30번 줄에서 ray 변수에 스크린 위치를 Ray로 변환한 값을 넣는다.

32번 줄에서 RaycastHit 타입의 변수를 정의한다.

원하는 곳에 광선이 맞으면 정보를 받을 수 있다.

 

34번줄 if 조건문으로 meshCollider에 맞았는지 확인하고 out 키워드로 추가 정보를 저장하고, 최대 거리는 1000으로 지정한다. 

36번 줄에서 맞았다면 월드위치에 히트한 지점의 정보를 저장한다.

 

스크립트를 저장한 후 마우스 포인터를 plane위로 가져가면 위치와 함께 큐브가 해당 위치로 움직일 것이다.

 

Ray는 3D 환경에서 다양하게 활용할 수 있는데 여기서는 마우스 포인트를 월드 위치로 변환하는 방법만 간단하게 살펴보았다.

 

자세한 활용법은 다른 글에서 다루도록 한다.

 

끝.

Wraven...