전부 올려둘 것인가 필요할 때 올릴 것인가
아틀라스를 잘 묶어 놓아도 질문 하나가 남습니다. 이걸 언제 메모리에 올릴 것인가.
기본 동작은 단순합니다. Include in Build 는 기본으로 켜져 있고, 켜 두면 프로젝트가 시작할 때 유니티가 알아서 올려서 스프라이트에 붙입니다. 따로 부르는 코드가 없어도 되는 게 이것 때문입니다.
그래서 아틀라스가 스무 장이면 스무 장이 다 올라간 채로 게임이 시작합니다. 쓰지도 않는 화면의 아틀라스까지 그렇습니다. 몇 장일 때는 이걸로 충분한데, 장수가 늘면 시작 시간과 상시 메모리가 같이 문제가 됩니다.
한 가지 더. 아틀라스는 통째로 올라갑니다. 스프라이트 한 장이 필요해도 그 아틀라스 전체가 메모리에 뜹니다. 아래 Runtime Atlas 가 노리는 게 정확히 이 지점입니다.
Sprite Atlas V2
Unity 6 의 기본값입니다. 쓰는 법은 V1 과 거의 같습니다.
주의할 것은 마이그레이션이 되돌려지지 않는다는 점 하나입니다. 구버전 프로젝트에서 올라오면 전환을 물어보는데, 한번 전환하면 V1 으로 못 돌아갑니다. 버전 관리 없이 작업 중이라면 전환 전에 백업하세요.
Late Binding
전부 미리 올리지 않고, 필요해질 때 로드하는 방식입니다.
아틀라스의 Include in Build 를 끄면 그 아틀라스는 빌드에서 빠집니다. 자동 로드만 꺼지는 것이 아니라 텍스처 자체가 안 실립니다.
스프라이트는 여전히 그 아틀라스를 가리키는데 빌드 안에는 없습니다. 그래서 이것만 끄고 두면 플레이 모드와 빌드에서 스프라이트가 사라집니다. 에디터의 씬 뷰와 게임 뷰는 프로젝트 창의 파일을 그대로 보고 있어서 멀쩡해 보입니다. 이게 함정입니다.
아틀라스를 우리가 따로 실어 보내야 합니다. Resources 나 에셋 번들, Addressables 입니다.
그러고 나면 스프라이트가 아직 안 올라온 아틀라스를 요청하는 순간
유니티가 SpriteAtlasManager.atlasRequested 콜백을 부르고, 거기서 우리가 넘겨 줍니다.
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
static void Hook()
{
SpriteAtlasManager.atlasRequested += Provide;
}
static void Provide(string tag, Action<SpriteAtlas> callback)
{
// tag 에 맞는 아틀라스를 로드해서 넘겨줍니다
var atlas = LoadAtlas(tag);
callback(atlas);
}
콜백은 첫 요청보다 먼저 걸려 있어야 합니다. 스프라이트가 이미 요청한 뒤에 등록하면 그 요청은 못 받습니다.
씬 안 오브젝트의 OnEnable 은 늦을 수 있어서, 보통 [RuntimeInitializeOnLoadMethod] 로 앞당깁니다.
그리고 콜백이 오는 시점에 스프라이트는 아직 안 그려집니다. 비동기로 로드하면 그 프레임에는 빈 화면이 나옵니다. 로딩 화면 뒤에서 미리 부르거나, 첫 프레임에 안 보여도 되는 것에만 씁니다.
Sprite Atlas Variants
같은 아틀라스의 저해상도 복제본을 만들어 두고 기기에 따라 고르는 기능입니다. 원본이 2048이면 변형은 1024로 두는 식입니다.
고사양과 저사양의 격차가 큰 안드로이드에서 쓸 만합니다. 다만 빌드에 둘 다 들어갑니다. 빌드 용량은 늘고 런타임 메모리만 줄어듭니다. 용량이 문제라면 이건 답이 아닙니다.
Runtime Atlas
실행 중에 실제로 쓰는 스프라이트만 모아 아틀라스를 새로 만드는 기능입니다.
인벤토리를 생각하면 이해가 빠릅니다. 포션은 아이템 아틀라스에, 검은 무기 아틀라스에, 방패는 장비 아틀라스에 있다고 하면, 지금 인벤토리에 있는 세 개를 보여주려고 아틀라스 세 장이 통째로 올라옵니다. Runtime Atlas 는 그 세 개만 뽑아서 한 장으로 묶습니다. 메모리도 줄고 배치도 하나로 이어집니다.
대신 패킹 연산이 무겁습니다.
게임플레이 중에 부르면 스파이크가 확실히 납니다. 어쩌다 걸리는 게 아니라 부를 때마다 걸립니다.
그래서 쓸 자리가 정해져 있습니다.
- 로딩 화면에서 처리한다
- 씬 전환 시점에 처리한다
- 여러 프레임에 나눠서 처리하고, 그동안은 원래 아틀라스로 그린다
전투 중에 아이템을 주울 때마다 다시 패킹하는 식이면 안 쓰느니만 못합니다.
정리
| 무엇을 줄이나 | 대가 | |
|---|---|---|
| 기본 (시작할 때 전부 로드) | 없음 | 시작 시간, 상시 메모리 |
| Late Binding | 상시 메모리 | 아틀라스를 따로 실어 보내고 로드 시점을 관리해야 합니다 |
| Variants | 저사양 기기의 메모리 | 빌드 용량이 늡니다 |
| Runtime Atlas | 메모리와 배치를 동시에 | 패킹 스파이크 |
넷 중에 무엇을 고를지는 아틀라스가 몇 장인지가 정합니다. 대여섯 장이면 기본으로 두는 게 맞습니다. 관리 비용이 이득보다 큽니다. 스무 장이 넘어가고 시작 시간이 눈에 띄면 그때 Late Binding 부터 봅니다.