이번에 프로젝트를 하다가, UI 오브젝트의 scale을 조정하지도 않았는데 자꾸 scale이 달라지는 현상을 발견했다.
처음엔 버그인 줄 알고 디버그 로그를 찍어가며 누가 scale을 바꾸는지 추적했지만, 알고보니 원인은 Transform.SetParent()
의 동작 방식에 있었다.
Unity의 Transform의 Scale은 World Scale = Parent World Scale * Child Local Scale
와 같은 구조로 동작한다.
즉, 어떤 오브젝트를 SetParent()
로 부모에 붙이는 순간, 부모의 scale에 맞춰 자식의 localScale이 자동으로 조정되는 것이 일반적이다.
SetParent()
에는 worldPositionStays
라는 매개변수가 있다.
이 값을 true로 설정하면(디폴트가 true), Unity는 자식 오브젝트가 월드 좌표계 기준으로 localScale을 강제로 조정한다.
예를 들어 부모가 (0.5, 0.5, 0.5)
라면, 자식의 localScale
은 (2, 2, 2)
처럼 자동으로 바뀌게 된다.
그래야 월드 기준으로 동일한 크기를 유지할 수 있기 때문이다.
이러한 문제때문에 SetParent()를 사용할 때는 부모의 scale을 잘 고려해야하고, 되도록이면 scale을 1로 유지하는게 바람직하다.
문제를 꼬이게 만든 것은 바로 Canvas Scaler
의 Scale With Screen Size
모드 였다.
이것은 설정에 따라 해상도에 맞춰 Canvas 자체의 scale을 자동으로 조정한다.
예를들어 Reference Resolution
을 설정해두면 Rect Transform의 Width와 Height를 설정해둔 해상도 값으로 맞춰두고, 설정 해상도 대비 현재 화면 해상도의 비율값만큼 해당 canvas의 scale에 적용한다. 이렇게 Canvas Scaler
의 Scale With Screen Size
는 scale을 조정하는 방식으로 해상도를 자동으로 맞춰준다.
아래 스크린샷을 보면 현재 해상도가 QHD일 때, Reference Resolution
을 FHD로 설정해두면 scale이 1.333333이 되는 것을 확인할 수 있다.
그럼 다시 본론으로 돌아가 보자.
무엇이 문제였냐면…
이 프로젝트에서는 미리 만들어 둔 UI 오브젝트 프리팹을 필요한 만큼 Instantiate해서, SetParent()를 통해 UI Canvas 안으로 넣는 과정에서 발생했다.
이 과정에서 Canvas Scaler의 Scale With Screen Size 모드는 이미 화면 해상도에 따라 Canvas의 scale을 조정하고 있기 때문에, SetParent는 그걸 모른 채 다시 scale을 건드려버리는 상황이 생긴다.
즉,
결과적으로,
두 시스템 모두 "잘 보이게 하겠다"고 나섰지만, 서로 조율 없이 각자 판단대로 scale을 만지다 보니 오히려 화면 출력이 꼬이는 아이러니한 상황이 생긴 것이다.
이런 현상을 방지하기위해 2가지 방안을 찾았다.
첫번째는 SetParent의 worldPositionStays
매개변수를 false로 해주는 것이다.
두번째는 월드에 생성하고 Canvas의 자식으로 넣는 과정에서 문제가 생기는 것이므로, 그냥 아예 생성 자체를 Canvas의 자식으로 바로 넣어버리면 문제가 해결된다.
물론 Instantiate을 안하고 미리 씬에 배치해두고 활성/비활성화하는 방법도 있었다. 그러나 상황에 따라 매번 생성해야하는 개수가 달라지고, 성능 또한 위 두 방법도 스테이지 시작 전 초기화 단계에서만 실행되기에 크게 차이가 날 것이라고 생각하지 않아서, 유지보수 편의성을 위해 미리 씬에 배치하는 방법은 채택하지 않았다.
SetParent()를 사용할 때, 부모의 scale을 조심하자. (되도록이면 1로 유지)
특히 Canvas Scaler의 Scale With Screen Size 모드와 함께 사용할 땐 더더욱 주의하자.
Unity의 "도와주려고 해주는 일"이 꼭 내가 원하는 일은 아닐 수 있다.