Notice
«   2026/07   »
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 31
Archives
Today
Total
관리 메뉴

월세 500 호기심 천국

2주차: 의외로 세상 잡다한 Transform에 대한 모든 것 본문

내 맘대로 쓰는 Unity 심층분석 析深層分

2주차: 의외로 세상 잡다한 Transform에 대한 모든 것

원펀치 쓰리 갓냥이 2024. 1. 7. 21:34
0주차: 인트로 - 새롭고 신기한 것을 좋아하거나, 모르는 것을 알고 싶어 하는 마음
1주차: Unity 엔진은 왜 개발 언어로 C#을 선택했을까?
2주차: 의외로 세상 잡다한 Transform에 대한 모든 것
3주차: EulerAngle아, Quaternion아, 둘이 싸우지 말고 사이좋게 지내야 한다~ (1/2)
4주차: EulerAngle아, Quaternion아, 둘이 싸우지 말고 사이좋게 지내야 한다~ (2/2)
5주차: 헐크 아저씨도 깜짝 놀란 Unity Render Pipeline이 작동하는 과정
6주차: 매 프레임 나보다 갓생 사는 Unity의 Life Cycle
7주차: 남들보다 더 갓생 사는 Coroutine의 충격적인 정체
8주차: 야물(YAML)딱진 그 녀석의 은밀한 속마음 - Unity Scene 파일 (.unity)
9주차: 내 과자 포장지 누가 버렸어? - meta data 파일 (.meta) 파헤치기
10주차: 철가방 채로 배달온 Unity 빌드 파일. 난 단무지 시킨 적 없는데, 왜 있지?
11주차: 마무리 - 후기

 


 

이번 주는 Unity 개발을 하다보면 무조건 알 수밖에 없는 Transform에 대한 것이다.

Transform은 Scene 화면 내의 사실상 모든 오브젝트에 포함되어있는 필수 컴포넌트로(Canvas, UI 제외),

흔히들 오브젝트의 위치, 회전각, 크기 등의 정보들이 담겨져 있는 것으로 알고 있으며,

Hierarchy 창에서의 오브젝트 부모 자식 관계도 Transfrom에서 관리된다.

 

 

필수 컴포넌트인 만큼 우리가 자주 접하고 사용하지만,

의외로 우리는 이 친구에 대해서 아는 것이 많이 없다.

 

나를 포함, 대다수의 입문 개발자들은 보통

위치, 회전, 크기, 오브젝트 부모 자식의 상속이라는 Transform의 큼직한 역할만 알고 있지,

구체적으로 어떤 세부 기능들이 있는지는 잘 모르는 경우가 많기 때문이다.

 

반에서 항상 반장을 도맡아 하고, 주변에서 친하게 안 지내본 친구가 없다는 엄청난 인싸 녀석이

정작 의외로 그 녀석이 어디에서 사는지, 평소에 속으로 무슨 생각을 하고 다니는지는 아무도 모르는,

비밀에 숨겨진 그런 인싸 친구랄까?

 

 

오늘은 그런 불쌍한 친구와 속을 터놓고 이야기해보고자,

Transform에 담겨있는 자세한 내용들을 Position, Rotation, Scale, Parent,

이렇게 네 가지 분류로 나누어 알아볼 예정이다.

 

 

자세한 이야기를 시작하기에 앞서,

Transform 클래스는 사실 Object 클래스를 상속한 Component 클래스를 상속하고 있다.

 

Transform 클래스가 Component 클래스를 상속

 

그리고 Component 클래스가 Object 클래스를 상속 / 여기서 Object는 System.Object가 아닌 UnityEngine.Object

 

때문에 Transform 클래스는 Component 클래스, Object 클래스가 가지고 있는 속성들을 모두 공통적으로 가지고 있으며,

이것들에 대한 설명은 본 게시물에서 따로 다루지 않을 생각이다.

Transform 고유의 속성들만으로도 충분히 양이 많다.

 

친구랑 제대로 대화 해보기도 전에 아빠랑 할아버지가 누군지까지 알아버렸다

 

 

 

Transform의 속성 중 너무 유명한 녀석들

회색 글씨로 그냥 짧게만 설명하고 바로 넘어갈 것이다.

 

 


1. Position

1) public Vector3 forward, right, up

파란색이 forward, 빨간색이 right, 초록색이 up

 

이미 잘 알려진 녀석이다.

오브젝트를 기준으로 세 가지 방향축을 나타내는 3차원 벡터이다.

2) public Vector3 position, localPosition

마찬가지로 이미 유명하다.

position은 오브젝트의 world 상에서의 좌표를 나타내는 3차원 벡터,

localPosition은 부모(0, 0, 0)를 기준으로 했을 때의 상대적인 좌표를 나타내는 3차원 벡터이다.

3) public Matrix4x4 localToWorldMatrix, worldToLocalMatrix

localPostion(로컬 좌표계)을 position(월드 좌표계)으로,

position(월드 좌표계)을 localPosition(로컬 좌표계)으로 변환하는 데에 사용되는 4차원 변환행렬이다.

localToWorldMatrix 행렬localPosition 앞에,

혹은 worldToLocalMatrix 행렬position 앞에 곱하여 좌표계가 변환된 결과를 얻어낼 수 있다.

 

왜 월드 좌표계와 로컬 좌표계를 오가는데 4차원 행렬을 곱하느냐? 한다면...

사실 단순 좌표계 변환 뿐만 아니라 위치, 회전, 크기 조정 모두 4차원 행렬으로 표현할 수 있고,

실제로도 Transform의 작동 원리 또한 그러하기 때문이다.

 

(tx, ty, tz)만큼 위치 이동 변환

 

θ각만큼 z축 회전 변환

 

(sx, sy, sz)만큼 확대/축소 변환

 

이렇게 해서 좌표계 변환 또한 행렬의 곱으로 나타낼 수 있다. 좌표계 변환 역시 위치 이동, 회전, 확대/축소의 연속일 것이므로.

 

 

Vector3 lPosition = transform.localPosition;
Vector3 wPosition1 = transform.position;
Vector3 wPosition2 = transform.localToWorldMatrix
       			* new Vector4(lPosition.x, lPosition.y, lPosition.z, 1);

//is wPosition1 equivalent to wPosition2? << yes

 

그리하여 좌표계 변환을 할 때 위 식에서 가운데에 곱해주는 변환행렬 역할을 하는 것이

localToWorldMatrix, worldToLocalMatrix 인 것이다.

곱해주기만 하면 원하는 좌표계에서의 좌표가 출력될 수 있게끔

이동, 회전 등의 연산들이 미리 적용되어 있는

변환행렬계의 밀키트라 볼 수 있겠다.

 

Transform 내에서 미리 계산하여 넘겨주는 것이기 때문에 당연히 읽기 전용이고,

수정불가능하다.

4) TransformDirection, InverseTransformDirection
TransformPoint, InverseTransformPoint
TransformVector, InverseTransformVector

바로 위에서 설명했던 4차원 변환행렬 친구들과 비슷한 녀석들이다.

다만 아까 그 친구는 4차원 변환행렬을 제공하는 것에서 그쳤다면,

이 녀석들은 변환까지 직접 해주는 함수들이다.

 

각각

local에서의 direction(forward, up, right)을 world에서의 direction으로,

local에서의 pointworld에서의 point로,

local에서의 vectorworld에서의 vector

변환해준다.

 

예)

Vector3 wForward = transform.TransformDirection(Vector3.forward);

Inverse가 붙었을 경우 전과는 반대로 world에서 local로 변환해준다.

5) Translate

유명한 녀석이다.

transform의 position 좌표를 이동시켜주는 함수이며,

transform.position += new Vector3(speed * Time.deltaTime, 0, 0);

과 함께 가장 많이 쓰이는 오브젝트 이동 수단이다.

 

예)

transform.translate(Vector3.forward * speed * TIme.deltaTime);

 

 


2. Rotation

1) public Vector3 eulerAngles, localEulerAngles

오브젝트의 회전각을 오일러각(Vector3)의 형태로 표현한다.

local이 붙은 경우 world에서의 회전각이 아닌 부모 좌표계에서의 회전각을 나타낸다.

2) public Quaternion rotation, localRotation

오브젝트의 회전각을 쿼터니온(Quaternion)의 형태로 표현한다.

local이 붙은 경우 world에서의 회전각이 아닌 부모 좌표계에서의 회전각을 나타낸다.

 

왜 회전각을 굳이 오일러각과 쿼터니온으로 구분해서 표현할까...?

에 대해서는 3주차(Quaternion)에서 알아볼 예정이다.

3) LookAt

 

transform.LookAt(장난감.transform);

 

오브젝트forward 벡터특정 방향을 가리키도록 자동으로 회전시켜주는 함수이다.

여기에서 특정 방향이라 함은 LookAt 함수에 인자로 전달받은 Transform을 바라보는 방향이다.

 

허나 3차원 좌표계일 경우 단순히 바라보기만 한다면 문제가 조금 생길 수 있다.

오브젝트가 말 그대로 목표 Transform을 '바라보기만 하면' 되는 것이기 떄문에

내가 원하지 않는 각도로 오브젝트가 갸우뚱하면서 바라보는 상황이 생길 수 있다.

 

3명의 박진영은 모두 당신을 잘 '바라보고' 있기 때문에 LookAt 함수는 잘 적용되고 있다. 다만 내가 저런 각도를 원하지 않을 뿐.

 

때문에 LookAt 함수의 인자로 바라볼 대상 Transform에다가

윗벡터 Vector3를 하나 더 넣어주면 이런 문제를 해결할 수 있다.

transform.LookAt(target, Vector3.up);

 

오브젝트가 취할 수 있는 여러 각도 중 Transform 방향을 바라보는 각도는 무수히 많으나,

그 동시에 제3의 윗벡터가 오브젝트의 머리 위로 오게끔 하는 각도는 단 하나로 결정되기 때문에

이것으로 원하는 모습의 회전을 만들어낼 수 있는 것이다.

 

이제 각도는 괜찮은데 걍 사진이 문제

 

4) Rotate

흔히 아는 오브젝트를 회전시키는 함수이다.

 

현재 회전각에서 상대적인 수치만큼 회전시키고 싶다면

인자로 오일러각 Vector3를,

 

아예 절대적인 각도를 대입하고 싶다면

인자로 오일러각 Vector3와 함께 Space.World를 넣는다.

5) RotateAround

 

 

Rotate는 항상 오브젝트의 중심을 기준으로 회전하는 반면,

RotateAround는 회전의 중심이 되는 점 좌표 (Vector3), 회전축 벡터 (Vector3), 회전각 (float)

세 가지를 인자로 넘겨주면

원하는 중심축을 기준으로, 원하는 각도만큼 오브젝트를 회전시킬 수 있다.

 

 


3. Scale

1) public Vector3 localScale

흔히 쓰이는 transform의 크기 속성이다. 다만 parent 오브젝트에 대한 상대적인 크기라는 특징이 있다.

2) public Vector3 lossyScale

world 좌표계를 기준으로 하는, 오브젝트의 절대적인 크기이다.

lossyScale은 읽기 전용이기 때문에 우리가 직접적으로 수정할 수 없다는 특징이 있지만,

parent를 null로 설정해놓고 localScale을 원하는 크기만큼 조정한 다음,

다시 원래의 parent를 설정하는 식으로 lossyScale을 간접적으로 수정하는 방법이 있다.

 

 


4. Parent

1) public Transform root, parent

이름에서 알 수 있듯이, 해당 transform이 속한 오브젝트의 상위 오브젝트 transform을 저장해놓는 곳이다.

transform.parent를 통해 자신의 부모 오브젝트의 transform을,

transform.root을 통해 최상위 계층의 부모 오브젝트를 얻을 수 있다.

 

root의 경우 자신 위의 오브젝트가 존재하지 않으면 자기 자신을 반환하기 때문에 null이 나올 수 없다.

public void SetParent(Transform parent)를 통해 자신의 부모를 바꿀 수도 있다.

2) public int childCount

자신이 가지고 있는 child의 수를 반환한다.

비활성화돼있는 child 역시 카운팅에 포함되며,

모든 자식 오브젝트를 순회해야 할 일이 있을 때 유용하게 쓰인다.

3) Find, GetChild

자신이 가지고 있는 child 중 원하는 child를 얻어야 할 때 쓰인다.

Find의 경우에는 오브젝트의 이름(string)을 인자로 받으며,

GetChild는 찾고자 하는 child가 몇 번째 child인지 그 index(int)를 인자로 받는다.

4) DetachChildren

 

자신이 가지고 있는 모든 child를 부모 자식 관계를 끊고 unparent 시킨다.

진짜 무서운 함수다.

5) GetSiblingIndex, SetSiblingIndex

부모의 child 중 자신이 몇 번째인지 정보를 얻거나,

몇 번째 child가 되겠다고 변경한다.

즉 child간의 순서를 얻고 수정하는 속성이다.

 

 

오늘 서열 정리 좀 해야 쓰것다

6) SetAsFirstSibling, SetAsLastSibling

형제(Sibling) 중 첫 번째, 혹은 마지막 child로 순서를 변경한다.

 

 

 

 


etc.

public bool hasChanged

Transform에 있는 속성 중 내게 가장 생소한 속성이었다.

position, rotation, scale 등 Transform의 속성 중 하나라도 변한 것이 있다면 true반환한다.

 

다만 우리의 기대와는 달리 우리가 따로 Transform의 내용들을 수정한 적이 없어도

항상 매 프레임 true를 반환한다고 한다.

그 이유는 Transform의 속성 중 hasTransform이라는 녀석이 있는데,

이 녀석이 매 프레임마다 true로 변경되고, 프레임이 끝날 때쯤 fasle로 변경된 뒤 프레임이 종료되기 때문에

hasTransform의 변경 사실을 감지하고 hasChanged가 항상 true를 반환한다는 것이다.

 

때문에 false인 모습의 hasChanged를 보고 싶다면 개발자가 따로 타이밍을 정해

hasTransform 녀석과 원만한 합의를 봐야할 것이다.

아니면 직접 hasTransform = false로 값을 대입하던가.

 

 


 

 

이렇게 해서 Transform 안에 있는 속성들에 대해

자주 쓸 일 있는, 혹은 거의 써볼 일 없는 것들까지 전부 다 알아보았다.

 

Transform 정도면 간단한 기능 몇 개 있는 만만한 녀석일 거라 생각했는데...
양이 너무 많았다.

 

이 바닥 겸손해야 한다.

 

 

 

다음 주에 다시 돌아오겠다...

 

 


 

0주차: 인트로 - 새롭고 신기한 것을 좋아하거나, 모르는 것을 알고 싶어 하는 마음
1주차: Unity 엔진은 왜 개발 언어로 C#을 선택했을까?
2주차: 의외로 세상 잡다한 Transform에 대한 모든 것
3주차: EulerAngle아, Quaternion아, 둘이 싸우지 말고 사이좋게 지내야 한다~ (1/2)
4주차: EulerAngle아, Quaternion아, 둘이 싸우지 말고 사이좋게 지내야 한다~ (2/2)
5주차: 헐크 아저씨도 깜짝 놀란 Unity Render Pipeline이 작동하는 과정
6주차: 매 프레임 나보다 갓생 사는 Unity의 Life Cycle
7주차: 남들보다 더 갓생 사는 Coroutine의 충격적인 정체
8주차: 야물(YAML)딱진 그 녀석의 은밀한 속마음 - Unity Scene 파일 (.unity)
9주차: 내 과자 포장지 누가 버렸어? - meta data 파일 (.meta) 파헤치기
10주차: 철가방 채로 배달온 Unity 빌드 파일. 난 단무지 시킨 적 없는데, 왜 있지?
11주차: 마무리 - 후기