들어가며
경량 패턴.
무엇이 가볍길래?
설명에 따르면 사용하는 메모리가 가볍다는 의미이다.
객체의 수가 너무 많아서 메모리의 양에 차지하는 공간이 많을 때, 객체를 가볍게 만들어서 최적화하는 패턴을 경량 패턴이라고 한다.
그래서 가볍게는 어떻게 만드나요?
모든 개체가 똑같은 값을 가지고 있는 데이터를 모아서 개체마다 저장하는 것이 아닌 한 곳에다 저장한 후 모든 개체가 그 데이터를 참조하는 방법으로 만든다!
이러한 공통 데이터를 GoF에서는 고유 상태 (intrinsic state)
라고 부르고
그렇지 않는 개별 데이터는 외부 상태 (extrinsic state) 라고 부른다.
게임 프로그래밍 패턴에서는 자유문맥 (context-free) 상태
라고 부르는데,
음 글쎄다… 한글로 어감이 잘 번역이 되지 않은 것 같다.
문맥이 없는 상태?라고 적당히 알아들으면 될 것 같다.
경량 패턴을 설명하는 대부분의 글에서 숲에 있는 나무를 렌더링하는 상황을 예로 든다.
보통 나무들끼리 같은 모델링을 공유하기 때문에 나무가 어디있는지 위치 정보만을 가지고 메시나 머테리얼 같은 모델링은 전부 하나의 데이터를 참조하도록 하는 것이다.
해당 예시의 핵심은 경량 패턴 그 자체보다도 어떤 식으로 자원을 공유하고 있는 지를 그래픽카드도 이해할 수 있는 방식으로 표현해야 한다는 것이다.
이는 Direct3D와 OpenGL에서 인스턴스 렌더링이라는 기능을 통해 기본적으로 지원하고 있다고 한다.
경량 패턴의 구현?
사실 경량 패턴의 자세한 구현 방식은 잘 몰라도 좋다.
유니티에서 기본적으로 지원하는 기능들로 구현이 가능하고, 다른 패턴과 섞어서 사용한는 것이 대부분이기 때문이다.
앞서 언급했듯이 고유 상태의 공유 자원을 어떻게 Implement 할 건지가 경량 패턴의 핵심이겠다.
유니티에서 가능한 방법들을 몇 가지 소개해보고자 한다.
방법 1) 스크립터블 오브젝트 (Scriptable Object)
위 영상의 예시는 좀비이다.
좀비가 제대로 기능하기 위해 개체의 최대체력, 데미지, 시야, 움직이는 속도를 참조할 때 해당 데이터를 스크립터블 오브젝트로 만들어서 일종의 프리셋 형태로 저장해놓자는 방식이다.
그렇게 되면 좀비가 200마리, 300마리가 스폰되어도 공통된 스탯을 저장하기 위해 메모리에 2~300개의 공간을 할당할 필요가 없이 단 한 번의 메모리 할당만으로 모든 좀비들의 스탯을 읽어올 수 있게 되는 것이다.
스크립터블 오브젝트의 주의점으로는 에디터에서는 스크립터블 오브젝트를 언제든 생성하고 관리할 수 있지만, 빌드 버전에서는 그것이 불가능하다.
스크립터블 오브젝트는 애셋 파일 형식으로 관리되기 때문에 애셋 번들로 빌드하고 배포하는 방식으로 게임 데이터를 관리하고 있다.
자세한 내용은 다음의 유튜브를 참고하자.
추가로 스크립터블 오브젝트에 데이터를 저장하면 이것이 빌드된 게임에 포함되어 배포가 되기 때문에 사용자가 마음만 먹는다면 데이터를 조작할 수 있기 때문에 멀티플레이 게임에서는 다른 방식을 사용하는 것이 좋다.
방법 2) 정적 배칭 (Static Batching)
유니티 오브젝트를 인스펙터 상에서 보면 오른쪽 위 구석탱이에 Static이라는 것을 활성화 시킬 수 있는 것을 볼 수 있다.
이는 오브젝트를 정적 오브젝트 (Static Object)
로 만들어주는 체크박스이다.
런타임에 절대로 움직이지 않는 객체를 정적 오브젝트로 지정할 수 있는데, 정적 오브젝트로 지정되면 유니티 엔진이 시스템을 사전 계산할 때 해당 오브젝트를 포함할 수가 있게 된다.
미리 계산하여 로드를 줄이는 효과도 있지만, 가장 중요한 것은 유니티가 같은 머테리얼을 가진 정적 오브젝트들을 한 그룹으로 묶어 한 번에 렌더링하기 때문에 드로우콜이 줄어든다는 것이다!
즉, 머테리얼을 여러 번 불러올 필요가 없으니 GPU 입장에서 상당히 많은 수준의 최적화를 이룰 수 있는 것이다.
주의점이라면 유니티에서 지원하는 인스턴스 렌더링 기능인 GPU 인스턴싱을 정적 오브젝트에는 사용할 수 없다는 점이다.
렌더링해야하는 머테리얼의 종류가 다양하다면 GPU 인스턴싱
,
같은 머테리얼을 공유하는 대규모 객체 그룹이 존재한다면 정적 배칭
으로 최적화를 하면 되겠다.
경량 패턴을 언제 사용할지 모르겠어요
경량 패턴을 언제 사용해야 하는지 감이 잡히지 않을 경우가 있다.
게임 프로그래밍 패턴에서는 필요할 때 만드는 것이 낫다고 한다.
공유 기능을 유지하고 싶다면, 인스턴스를 요청받았을 때 이전에 만들어 놓은 것이 있는지 확인하고, 있다면 그걸 반환해주는 방식으로 구현하면 된다.
using System.Collections.Generic;
using UnityEngine;
public class ObjectContainer : MonoBehaviour {
static Dictionary<string, GameObject> dict = new Dictionary<string, GameObject>();
static GameObject objectPrefab;
public static GameObject getPerson(string key)
{
if (!dict.ContainsKey(key))
{
GameObject tmp = Instantiate(objectPrefab);
dict.Add(key, tmp);
}
return dict[name];
}
}
Uploaded by N2T
'개발 > 게임 디자인 패턴' 카테고리의 다른 글
게임 디자인 패턴 5. 싱글턴 패턴 (Singleton Pattern) (1) | 2023.10.27 |
---|---|
게임 디자인 패턴 4. 프로토타입 (Prototype) (0) | 2023.10.26 |
게임 디자인 패턴 3. 관찰자 패턴 (Observer Pattern) (0) | 2023.10.25 |
게임 디자인 패턴 1. 명령 패턴 (Command Pattern) (0) | 2023.10.23 |
게임 디자인 패턴 0. 디자인 패턴에 대해 (1) | 2023.10.23 |