본문 바로가기
유니티

Unity 마우스로 3D 오브젝트를 로컬 X축으로만 드래그하기

by 유니티세상 2025. 11. 3.
반응형

구현 목표

마우스로 클릭한 오브젝트를 부모 오브젝트의 X축 방향으로만 이동시키고,
이동 범위는 minX와 maxX 값 사이에서만 제한되도록 만들고 싶었다.
즉, 마우스를 움직이면 오브젝트가 마우스의 X 움직임에 따라 “좌우로만” 자연스럽게 움직이는 시스템이다.

 

 

말로 하면 간단하지만, 실제로 구현하면서 몇 가지 헷갈렸던 개념이 있었다.
대표적으로 ScreenToWorldPoint(), Mathf.Abs(), InverseTransformPoint()의 역할이었다.

 

 // 구현하고 싶은 것
 // 마우스로 3D 오브젝트를 드래그
 // 오브젝트는 "부모 기준 로컬 X축"으로만 움직임 (-0.05, 0, 0) → (1, 0, 0) 사이


 // 마우스 위치 가져오기 (스크린 좌표)
 Vector3 mousePosition = Input.mousePosition;

 // 카메라와 오브젝트 사이의 거리 계산
 // Q. 절대값으로 계산해야 하는 이유?
 // A. 카메라가 오브젝트 앞(-Z)에 있을 수도, 뒤(+Z)에 있을 수도 있기 때문.
 // ScreenToWorldPoint는 카메라 "앞쪽" 평면(양의 거리)만 계산하므로 절댓값이 필요함.
 float distance = Mathf.Abs(mainCamera.transform.position.z - activeGameObject.transform.position.z);

 // 카메라와 오브젝트 사이의 거리 설정
 // ScreenToWorldPoint는 Z축 거리를 알아야 정확히 변환 가능
 mousePosition.z = distance;

 // distance가 항상 양수이기 때문에 카메라 앞/뒤 어느 위치든 상관없이 카메라 기준 올바른 월드 좌표를 얻을 수 있음
 Vector3 worldPosition = mainCamera.ScreenToWorldPoint(mousePosition);


 // 마우스의 월드 좌표를 부모 기준 로컬 좌표로 변환시킴
 // 월드 좌표를 부모 기준 로컬 좌표로 변환 (부모 공간에서 움직이기 위함)
 // Q. parent.InverseTransformPoint()를 왜 써야 하나?
 // A. "월드 좌표 → 부모 기준 로컬 좌표" 변환이 필요하기 때문.
 //    parent를 안 쓰면 자기 자신 기준이 돼서 (거의 0,0,0 근처로 변함)
 //    parent.InverseTransformPoint() = “부모 입장에서 자식이 월드상 어디에 있지?”
 Vector3 localPosition = activeGameObject.transform.parent.InverseTransformPoint(worldPosition);


 // X축만 변경하고 싶기 때문에 나머지 축(Y,Z)은 고정
 // localPosition.x는 부모 기준에서 마우스의 X위치
 Vector3 fixedPosition = activeGameObject.transform.localPosition;
 fixedPosition.x = Mathf.Clamp(localPosition.x, minX, maxX);

 //오브젝트의 로컬 좌표를 변경
 activeGameObject.transform.localPosition = fixedPosition;

 Debug.Log($"X만 변경된 로컬좌표: {fixedPosition}");

구현 과정

먼저 마우스 입력을 받기 위해 Input.mousePosition으로 화면 좌표(스크린 좌표)를 가져온다.
하지만 이 좌표는 단순히 모니터 화면상의 픽셀 단위 좌표이기 때문에
그대로는 3D 공간에서 사용할 수 없다.
즉, 이 상태의 값으로 오브젝트를 움직이면 “어디로 이동해야 하는지”를 게임 월드가 이해하지 못한다.

이 문제를 해결하기 위해 카메라를 기준으로 한 월드 좌표(World Position) 로 변환해야 하는데,
이때 사용하는 함수가 바로 ScreenToWorldPoint()이다.

 

내가 헷갈렸던 부분 ① — z 값은 왜 필요한가?

처음엔 이렇게 생각했다.

“나는 X축으로만 움직일 건데, z값이 왜 필요하지?”

하지만 ScreenToWorldPoint()는 단순히 화면 좌표를 3D로 바꿔주는 함수가 아니라, “카메라로부터 z 거리만큼 떨어진 평면 위의 위치”를 계산하는 함수다.
즉, z값을 넣지 않으면 어느 평면에서 계산해야 할지를 모른다.

그래서 다음 코드가 들어간다.

 
mousePosition.z = Mathf.Abs(mainCamera.transform.position.z - activeGameObject.transform.position.z);
 

이 부분이 핵심이다.
카메라와 오브젝트 사이의 실제 거리(z)를 구해서 마우스가 그 거리 평면 위의 좌표로 변환될 수 있게 하는 것이다.

여기서 Mathf.Abs()를 쓰는 이유는 카메라가 오브젝트의 앞에 있을 수도, 뒤에 있을 수도 있기 때문이다.
ScreenToWorldPoint()는 오직 카메라 앞쪽(양의 거리)만 계산하기 때문에 음수가 들어가면 엉뚱한 평면으로 계산된다.
따라서 항상 양수(절댓값)를 넣어줘야 올바른 월드 좌표를 얻을 수 있다.

 

 

내가 놓쳤던 부분 ② — 왜 InverseTransformPoint()를 써야 하나?

마우스로 변환된 worldPosition은 게임 전체 기준(월드 좌표계)의 위치이다.
그런데 내가 움직이고 싶은 방향은 “부모 기준의 로컬 X축”이다.
즉, 부모가 회전했거나 기울어져 있을 때 그 부모의 방향을 따라가야 한다.

이때 필요한 게 바로 parent.InverseTransformPoint()다.

 
Vector3 localPosition = activeGameObject.transform.parent.InverseTransformPoint(worldPosition);
 

이 함수는
“월드 좌표 → 부모 기준 로컬 좌표” 로 변환한다.
즉, 부모 입장에서 봤을 때 “자식이 지금 월드에서 어느 위치에 있는가”를 알려주는 함수다.

이걸 쓰지 않으면 오브젝트는 항상 자기 자신 기준으로 계산되어 좌표가 (0,0,0) 근처로 왜곡되거나, 부모가 회전해 있을 때 마우스 방향과 전혀 다르게 움직인다.

 

핵심 정리

이제 변환된 로컬 좌표에서 X축만 수정해주면 된다.

 
Vector3 fixedPosition = activeGameObject.transform.localPosition;
fixedPosition.x = Mathf.Clamp(localPosition.x, minX, maxX);
activeGameObject.transform.localPosition = fixedPosition;
  • localPosition.x : 부모 기준 X축 좌표
  • Mathf.Clamp() : 이동 가능한 구간을 제한 (minX, maxX)
  • transform.localPosition : 최종적으로 부모 기준 좌표로 적용

최종 구현의 의미

정리하자면,

  1. 마우스 좌표(스크린)를 가져온다.
  2. 카메라와 오브젝트 거리(z)를 계산한다.
  3. ScreenToWorldPoint()로 월드 좌표를 구한다.
  4. InverseTransformPoint()로 부모 기준 로컬 좌표로 변환한다.
  5. X축만 수정하고 Mathf.Clamp()로 제한한다.
  6. transform.localPosition에 반영한다.

이 과정을 거치면
부모가 어떤 방향을 향하든,
오브젝트는 항상 부모의 로컬 X축을 따라 이동한다.


 마무리하면서 느낀 점

이번 구현을 통해, “마우스 좌표는 화면 좌표일 뿐, 3D 공간상의 위치로 쓰려면 반드시 z(거리)를 알려줘야 한다”
또한 InverseTransformPoint()를 통해 부모 좌표계와 월드 좌표계의 차이를 명확히 알게 되었다.

겉보기엔 단순한 X축 이동 로직이지만, 그 뒤에는 “좌표계 변환”이라는 3D의 핵심 개념이 숨어 있었다.
이제는 마우스 입력으로 오브젝트를 조작할 때 “지금 이 좌표가 어떤 공간 기준으로 계산되는가?”를 먼저 생각하게 되었다.

 

 

https://skystory.tistory.com/18?utm_source=chatgpt.com

 

ScreenPointToRay, ScreenToWorldPoint 차이, 미니맵 클릭 사용 함수

ScreenPointToRay는 기본적으로 카메라 위치에서 마우스 방향을 가리키는 광선을 한다. ScreenToWorldPoint 포인트는 실제로 마우스 아래의 3D 위치를 반환한다. Vector3 p = camera.ScreenToWorldPoint(new Vector(Input.mo

skystory.tistory.com

https://hankyo-dev.tistory.com/entry/Unity-3D-Object-Drag-Drop?utm_source=chatgpt.com

 

Unity 3D Object Drag & Drop

Unity에서 3D 게임을 만들다 보면 3D GameObject를 컴퓨터의 경우 마우스, 모바일의 경우 손가락으로 자유롭게 Drag & Drop 하여 원하는 위치에 놓는 기능이 필요할때가 있다 위에서 서술한 기능을 나는

hankyo-dev.tistory.com

 

반응형