- 게임을 만드는 데 있어서 중요한 습관 한 가지
1. 최적화를 시키는 습관
게임을 구현하다 보면 보통 동작이 만들어졌는지 확인만 한 상태로 그대로 넘어가는 경우가 있다.
그런데 만약 이런 상황에서 구현된 코드를 한 번 더 훑어본 후에 최적화를 할만한 요소가 있는지 확인을 하고 나서
해당 부분을 최적화를 시키면 더욱 좋은 퍼포먼스를 보여줄 수가 있다.
만약 작업을 하는데 시간이 여유롭다면 최적화 부분을 신경을 써주면 좋다고 할 수 있지만,
대부분 시간이 여유롭지 않다고 생각하기 때문에 이후에 코드 리펙토링을 통해 최적화를 하게 된다.
따라서 작업에 주어진 시간이 넉넉하다고 판단이 되면,
그 당시 구현된 내용들을 확인하고 최적화를 시도해보는 것도 나쁘지 않다고 생각한다.
2. 최적화에 쓰이는 자료구조는?
Array(배열)와 unordered_map 외에는 쓰이는 것이 별로 없다.
그렇기 때문에 작업을 진행하면서 주로 이 두 가지를 활용하여 구현한 부분을 최적화하게 된다.
이와 비슷한 자료구조 중에서는 "Linked list"라는 것이 있다.
그런데 Linked list의 가장 큰 단점은 파편화가 되어 있기 때문에 메모리의 속도가 낮아질 수 밖에 없는 것이다.
그래서 보통은 Linked list보다는 Array를 주로 쓰게 되는데, Array 자체가 관리하는 부분이 까다롭기 때문에
해당 부분은 STL_Vector를 사용하여 관리를 하게 된다.
STL_Vector도 굉장히 좋은 자료구조 중에 하나라고 말할 수 있지만,
너무 범용적으로 사용이 되고 있기 때문에 Array에 비해서 상당히 무거운 편이다.
그래서 주로 메모리 관리하는 부분에서 따로 만들어서 쓰이게 된다.
따라서 프로그램을 최적화하는 부분에 있어서는 목적과 상황에 맞게 Array와 unordered_map
이 두가지를 잘 활용하여 최적화를 하면 좋은 퍼포먼스를 얻을 수 있게 된다.
- WorldTransform에서 지정한 부모에 상대적인 LocalTransform으로 변환하는 과정
언리얼과 유니티 엔진을 사용해보면 부모 트랜스폼과 자식 트랜스폼의 관계를 많이 보게 된다.
이제 그 두 관계의 Scale, Rotation, Position이 어떻게 변환이 되는지 변환되는 과정을 Scale부터 순서대로 알아보자.
- Scale
먼저 서로 연관관계가 없는 트랜스 폼이 두 개가 있다.

여기서 이제 한 개는 스케일이 0.25 나머지 한 개는 1이다.

이 두 개를 서로 연관 지어서 계층 구조를 만들게 되면 1번이 부모가 되고 2번이 자식이 되는 관계가 형성된다.
그럼 여기서 부모 오브젝트의 스케일이 0.25면 자식의 스케일은 1이 될까?
1이 되지 않는다.
처음에 오브젝트가 만들어지면 해당 스케일은 월드 스케일을 기준으로 해서 오브젝트가 만들어진다.
그런데 부모와 자식으로 나누어져 있는 경우에는 자식이 부모로부터의 영향을 받게 되므로.
자식의 스케일은 월드가 아닌 로컬 스케일로 변환이 된다.
로컬 스케일로 변환이 된 상태에서 월드 스케일로 되어있는 부모 스케일의 정보를 알아내야 하기 때문에
자식의 로컬 스케일에서 부모의 월드 스케일 값을 곱했을 경우에 1이 나와야 한다.
왜 1이 나와야 하냐면 게임 오브젝트의 기본 스케일의 값은 1이기 때문이다.
따라서 부모의 월드 스케일이 0.25라면 자식의 로컬 스케일의 값은 1이 아닌 4가 된다.

다른 예를 들어보자면

서로 연관이 없는 오브젝트 두 개 중에서 1번의 스케일 값은 4, 2번은 1이다.
이런 경우 다음과 같이 부모와 자식 관계를 형성하면 자식의 스케일 값은 몇이 나오게 될까?

위에서 설명한 것처럼 부모의 월드 스케일 값을 곱했을 경우에 1이 나와야 하기 때문에 자식 스케일의 값은 0.25가 된다.

- Rotation
회전도 스케일과 같이 유사하게 생각해볼 수가 있다.

현재 부모의 회전 값이 20이고 자식의 회전 값은 10이라고 가정하고 자식이 부모 안으로 들어가게 된다면
자식의 회전 값은 몇이 나올까?
자식의 회전 값이 부모의 회전 값보다 10만큼 덜 돌아가 있기 때문에 -10이라고 할 수 있다.
부모의 회전 값에 대한 역수는 자식의 회전 값에서 반대 방향으로 회전을 하여 음수를 붙이게 되는데 이렇게 하면
반대 방향이라는 것은 곱셈에 대한 역원이 아닌 덧셈에 대한 역원이라고 할 수 있다.
- Position

게임에 A와 B의 오브젝트가 있다. 여기서 둘 중 하나의 오브젝트의 위치를 알려면 A - B 또는 B - A를 통해 알 수 있다.

그런데 왜 위치는 덧셈 연산일까?
해당 위치에 이동 행렬에서 반대 방향에 대한 것은 역행렬이고 이것은 덧셈에 대한 역원에 들어가기 때문에
덧셈 연산을 쓸 수 있다고 생각한다.
그런데 만약 회전이 들어간다면 어떻게 될까?
회전이 들어가게 되면 위치 역시 변화가 생긴다.
그렇다면 B가 A의 자식으로 들어가고 A의 회전 값이 20, B의 회전 값이 10이라고 했을 때
부모의 트랜스폼의 정보는 어떻게 계산을 해야 될까?
A와 B의 오브젝트의 거리가 (20, 10)이라고 가정하였을 때

일단 A(부모)가 회전한 값(20)을 초기 값(0)으로 먼저 되돌려 주고 기본 상태로 만든다.

그렇게 되면 (20, 10)이라는 벡터 역시 되돌려준 회전 값만큼 반대로 회전을 하게 되면서
부모로부터 얼마만큼 떨어졌는지 측정을 할 수 있고,
그 후에 스케일로 다시 역변환을 해줘야 올바른 로컬 좌표의 위치를 얻게 된다.
- 부모 GameObject에 WorldTransform을 저장하는 이유는?
부모 오브젝트에 월드 트랜스폼이 없으면 자식 오브젝트에서 월드 트랜스폼의 정보를 알아낼 때
부모 오브젝트 안에 있는 자식의 오브젝트 들의 정보를 처음부터 끝까지 모두 계산을 해야 하기 때문이다.
만약 부모 오브젝트에 월드 트랜스폼이 있다고 한다면 더 많은 메모리를 소비한다는 단점이 있지만
월드 트랜스폼이 없는 것보다는 정보를 더 빠르게 찾을 수가 있다는 장점이 있다.
'Programming > Soft Renderer_2020' 카테고리의 다른 글
복소수 (cosθ, sinθ) 가 회전 행렬과 동일함을 증명 (0) | 2020.06.12 |
---|---|
복소수의 기본 성질 (0) | 2020.06.12 |
GameEngine2D : Circle, CameraCircleBound (0) | 2020.06.02 |
GameEngine 2D : QuadTree (0) | 2020.06.02 |
GameEngine 2D : GameObject, Camera (0) | 2020.05.26 |