리소스를 렌더링하거나 리소스에 포함된 데이터로 작업하려면 게임에서 프로젝트의 리소스를 메모리도 로드해야 합니다. 게임을 실행할 수 있는 모든 장치에는 사용할 수 있는 메모리의 양에 제한이 있습니다. 즉, 지정된 시간에 게임을 로드할 수 있는 리소스의 수 및 크기가 둘 다 제한되어 있습니다. 말할 것도 없이 이러한 제한은 모바일 장치에서 더 심합니다. 그러나 64비트 PC 및 콘솔에서조차 게임 프로젝트의 범위가 늘어남에 따라 메모리가 점점 줄어듭니다.
따라서 게임이 메모리를 모두 사용하지 않도록 해야 합니다. 게임이 운영 체제에서 얻을 수 있는 것보다 더 많은 메모리를 필요로 하는 경우 즉시 충돌이 발생해 게임 엔진이 응답할 기회를 갖기도 전에 운영 체제에서 게임을 중단할 수 있습니다.
게임이 충돌할 정도로 무거워질 때까지 메모리 사용을 무시하기 보다는 보다 예방적인 접근 방식을 통해 프로젝트 초기에서부터 예산 및 모범 사례를 마련하고 개발 주기 전체에서 실제 사용을 모니터링합니다. 이렇게 하면 출시 전 최종 단계에서 큰 문제가 발생하는 것을 방지할 수 있습니다.
이 페이지에는 게임에 필요한 메모리의 양을 줄일 수 있는 몇 가지 팁 및 방법이 제시되어 있고, 다음 3가지 기본 범주로 분류할 수 있습니다.
메모리 사용을 최적화 및 최소화하기 전에 도달하려는 목표를 분명히 파악하면 도움이 됩니다.
전체 예산은 지원하려는 하드웨어 장치의 사양에 따라 달라집니다. 여기서 운영 체제에서 사용하는 메모리는 제외해야 합니다. 그런 다음 다른 응용프로그램에서 사용하는 메모리 공간이 차지하는 큰 부분을 제외합니다. 만들려는 게임이 장치에서 유일하게 실행되는 응용프로그램이라고 착각하면 안 됩니다. 마지막으로 추가 "헤드룸"을 제공할 수 있는 추가 비율을 뺍니다. 예측할 수 없는 충돌을 방지하고자 하는 정도에 따라 전체 목표가 사용 가능한 장치 메모리의 25%이거나 더 적을 수 있습니다.
게임에 필요한 전체 예산을 전반적으로 파악하고 있는 경우 여러 하위 시스템 또는 텍스처, 오디오, 메시와 같은 다양한 종류의 리소스에 필요한 더 작은 예산으로 전체 예산을 나누면 더 유용할 수 있습니다. 여기서 수치는 개발하려는 게임의 종류에 따라 달라질 수 있습니다. 예를 들어 게임의 심미적 측면에서 디자인적 부분은 의도적으로 낮추고 음악 및 오디오 큐는 복잡한 경우 전체 예산에서 시각적인 부분이 중요한 게임에서 할당하는 것보다 더 많은 예산을 오디오 리소스에 할당해야 할 수 있습니다. 이러한 예산이 절대적일 필요는 없으며 프로젝트 진행 과정 중 협상에 따라 달라질 수 있습니다. 그러나 프로젝트의 여러 영역을 담당하는 팀원이 많은 경우 사전에 개략적인 기대치를 설정하면 팀이 보다 원활하게 협업하는 데 도움이 될 수 있습니다.
런타임 시 게임의 실제 메모리 사용에 대한 확실한 데이터가 없는 경우 문제를 어디서 해결해야 할지 파악할 수 없습니다. 이러한 데이터가 없으면 엉뚱한 곳에 시간과 노력을 쏟아 붓기 쉽습니다. 예를 들어 문제의 원인이 지나치게 자세한 모델임에도 불구하고 텍스처 품질을 크게 낮출 수 있습니다.
또한 일반적으로 리소스가 사용하는 메모리 크기가 디스크의 리소스 크기에 따라 매우 달라지기 때문에 런타임 시 게임에서 사용하는 메모리를 해석해야 합니다. 예를 들어, 프로젝트 폴더의 이미지 텍스처가 일반적으로 디스크에 저장되는 파일의 크기를 줄일 수 있는 압축된 .png 형식으로 디스크에 저장됩니다. 그러나 텍스처 데이터는 런타임 메모리에서 압축되지 않으며 파일 크기만 보고 예측할 수 있는 것보다 훨씬 더 많은 공간을 차지합니다.
다음은 게임의 메모리 사용에 대한 정보를 얻는 데 사용할 수 있는 여러 가지 콘솔 명령입니다.
memory_tree: 게임 내 여러 하위 시스템에서 할당한 모든 메모리를 보여주는 계층 트리를 Log Console로 출력합니다.
memory_resources: 게임에 로드된 리소스에서 차지하는 메모리 목록을 유형별로 정리해서 Log Console로 출력합니다.
perfhud 명령은 유용한 메모리 통계에 액세스할 수 있는 다음 두 가지 모드를 제공합니다.
perfhud memory: 총 시스템 메모리, 사용 가능한 시스템 메모리, 사용 중인 시스템 메모리를 간단히 비교해서 보여줍니다.
perfhud lua: 내장 가비지 수집 시스템에서 사용하는 비율을 포함해 Lua 환경에서 사용하는 메모리를 보여줍니다. 아래의 Lua 환경 및 스크립트 최적화 섹션을 참조하십시오.
또한 Stingray 편집기 뷰포트에서도 이러한 시각화를 확인할 수 있습니다. 뷰포트에서 View 오버레이를 클릭하고 상황별 메뉴에서 performance hud를 선택합니다.
Stingray 편집기에서 실행 중인 게임으로 명령 전송에 대한 자세한 내용은 상태 막대에서 명령 보내기를 참조하십시오. 명령 자체에 대한 자세한 내용은 콘솔 명령을 참조하십시오.
시작 시 게임이 충돌하는 경우 특히, 사용 가능한 메모리가 적은 모바일 장치에서 테스트하는 경우 부팅 패키지가 너무 많은 리소스를 로드하려고 하기 때문일 가능성이 가장 큽니다.
부팅 패키지가 와일드카드를 사용하여 포함해야 하는 리소스를 결정하는 경우 로드되는 리소스 중 일부는 실제로 필요하지 않을 수 있습니다. 이는 다음 줄을 사용하여 프로젝트에 있는 모든 유형의 모든 리소스를 로드하는 경우 특히 그렇습니다.* = ["*"]
와일드카드와 일치하지만 런타임 시 필요하지 않은 자산 또는 테스트 수준의 오래된 복사본이 프로젝트에 있는 경우 프로젝트에서 이러한 불필요한 리소스를 삭제하기만 해도 메모리를 절약할 수 있습니다.
또는 이전 복사본을 유지하려는 경우 부팅 패키지에서 자동 로드 줄을 제거하고 원하지 않는 리소스를 제외하도록 부팅 패키지를 다시 구성할 수 있습니다.
부팅 패키지 정보 및 리소스 패키지 정의도 참조하십시오.
게임의 메모리 사용을 제어하는 가장 강력하고 유연한 방법은 프로젝트의 리소스를 여러 개의 개별 패키지로 나눠 게임 실행 중 필요한 때에 메모리 내/외부에서 이러한 패키지를 동적으로 스트리밍하는 것입니다.
여러 경우에 다양한 리소스 집합을 필요로 하는 프로젝트에 일반적으로 이러한 접근 방식을 사용하면 좋습니다. 예를 들어 게임 내에 여러 수준이 있는 경우 각 수준에서 사용되는 자산에 대해 별도의 리소스 패키지를 생성할 수 있습니다. 그러면 사용 가능한 메모리를 다른 수준과 공유할 필요 없이 게임의 전체 메모리 예산을 각 수준에서 사용할 수 있습니다. 수준이 하나뿐이더라도 수준의 격리된 여러 부분이나 여러 캐릭터에 리소스 패키지를 사용할 수 있습니다.
이러한 종류의 시스템을 설정하는 방법에 대한 자세한 내용은 런타임 시 컨텐츠 로드 및 언로드의 항목을 참조하십시오.
메모리 내/외부에서 리소스 패키지를 스트리밍하기 위해 사용자 정의 시스템을 설정하려는 경우라도 로드하는 개별 리소스를 최적화하기 위해 아래 나열된 다른 방법을 적용해 유용하게 활용할 수 있습니다.
일반적으로 텍스처는 지금까지 대부분의 게임에서 메모리를 가장 많이 사용하는 항목입니다. 물론, 텍스처는 게임의 시각적인 우수성의 큰 축을 차지하고 있기도 합니다. 따라서 시각적인 품질과 메모리 크기 간에 적절한 균형을 찾는 것은 까다로운 문제일 수 있습니다. 이는 매우 다양한 화면 크기 및 사용 가능한 메모리로 여러 플랫폼을 지원해야 하는 경우 특히 더 그렇습니다. 화면이 큰 장치에서는 이미지 품질을 우선시해야 할 수 있습니다. 반면, 작은 화면에서는 렌더링 품질에 두드러진 차이를 느끼지 못한 채로 더 낮은 품질 또는 더 작은 텍스처를 사용할 수 있습니다.
다음 섹션에는 메모리와 프로젝트 품질 간 최적의 균형을 확보하는 데 도움이 되는 몇 가지 팁이 나와 있습니다.
텍스처 최적화를 위한 첫 번째 단계는 적절한 이미지 크기로 시작하는 것입니다. 게임에서 모든 텍스처를 4096x4096 픽셀로 만들었다면 모든 시스템에서 빠르게 메모리가 고갈될 것입니다.
텍스처의 크기가 어떠해야 한다는 정해진 규칙은 없습니다. 궁극적으로 다음 조건에 따라 텍스처 크기를 결정해야 합니다.
게임에서 렌더링될 때 텍스처가 차지할 최대 화면 공간. 결국, 이 조건은 텍스처가 적용되는 모델의 크기와 카메라와 모델 간의 거리에 따라 달라집니다. 예를 들어 산은 클 수 있지만 먼 거리에 있다면 차이가 눈에 띄지 않도록 하면서 더 작은 텍스처를 사용할 수 있습니다. 마찬가지로 신문이나 책과 같은 작은 오브젝트의 경우 플레이어가 자세히 들여다 볼 수 있으므로 보다 큰 텍스처를 사용해야 할 수 있습니다.
오브젝트에 필요한 시각적 품질 또는 정확도. 예를 들어, 플레이어가 게임 플레이로 인해 짧은 시간 동안만 보거나 형상으로 인해 다른 부분에 집중할 수 있는 수준의 일부에는 울퉁불퉁한 아티팩트를 사용할 것을 고려할 수 있습니다.
또한 각 재질에 할당한 여러 텍스처 맵에 다른 크기를 사용해 시험해 볼 수도 있습니다. 예를 들어, 렌더링 시 재질이 나타날 때 품질을 크게 저하시키지 않으면서도 일반적으로 거칠기 맵 및 금속 맵에는 색상 맵과 법선 맵보다 더 작은 이미지를 사용할 수 있습니다.
스트리밍 압축 텍스처의 경우 픽셀 크기가 4의 배수여야 함에 유의하십시오.
Stingray Texture Manager를 사용하면 각각의 여러 대상 플랫폼에 대해 Stingray에서 텍스처를 컴파일하는 방식을 제어할 수 있습니다.
조정해야 할 가장 중요한 설정은 텍스처에 적용되는 압축 유형을 정의하는 Output Format입니다.
iOS의 경우 PVR 또는 기타 플랫폼의 경우 DXT5와 같은 압축 알고리즘을 적용해 텍스처의 게임 내 메모리 사용을 크게 낮출 수 있지만 어쩔 수 없이 이미지에 약간의 압축 아티팩트가 발생할 수 밖에 없습니다.
텍스처 압축으로 인해 발생하는 오류 및 아티팩트는 일반적으로 간단한 색상 맵이 포함된 텍스처에서 감지하기가 가장 어렵습니다. 그러나 압축으로 인해 표면이 매우 이상해 보일 수 있기 때문에 법선 맵 압축을 피하려고 할 수 있습니다.
다른 플랫폼의 경우 가장 큰 밉맵 단계를 무시하려고 할 수도 있습니다. 1024x1024 픽셀 텍스처가 있는데 iOS에서만 이 텍스처를 256x256 픽셀로 제한하려는 경우 iOS로 컴파일하는 경우에만 가장 큰 두 개의 밉맵 단계를 무시하도록 Texture Manager를 설정할 수 있습니다. 그러면 iOS 데이터에서 이미지의 1024x1024 및 512x512 해상도가 제거되어 원래 256x256 픽셀 이미지였던 것처럼 효율적으로 작동하면서도 다른 플랫폼에서 사용하기 위해 전체 해상도로도 남아 있습니다.
필요한 메모리가 큐브의 6면을 곱한 값이기 때문에 밉맵은 특히, 볼륨 맵 또는 큐브 맵의 경우 특히 비용이 많이 들어갑니다. 따라서 수준에 반사 프로브를 베이킹하는 경우 생성된 텍스처에서 일부 또는 모든 밉맵을 제거하려고 할 수 있습니다.
Texture Manager 사용에 대한 자세한 내용은 텍스처 작업의 항목도 참조하십시오.
기본적으로 Stingray는 텍스처를 다른 리소스와 비슷하게 처리합니다. 그래서 텍스처를 리소스 패키지의 다른 모든 리소스와 함께 메모리에 로드하고, 계속해서 메모리에 유지시키고, 리소스 패키지가 언로드될 때에만 텍스처를 언로드합니다. 결국 각 텍스처의 모든 밉 수준이 실제로는 해당 텍스처가 화면 밖에 존재하거나 매우 먼 거리에 떨어져 있어 실제 사용 중이지 않을 때에도 게임 내에서 메모리를 점유하게 됩니다.
그러므로 텍스처 데이터가 실제 렌더링에 필요할 때에만 Stingray가 이 데이터를 메모리로 스트리밍하도록 설정하면 됩니다. 텍스처 스트리밍 방식에서는 메모리 사용량은 늘리지 않으면서도 해당 텍스처의 최대 해상도는 낮추지 않고 게임에서 사용하는 텍스처 수를 늘릴 수 있습니다.
이 시스템의 작동 방식에 대한 자세한 내용은 텍스처 스트리밍을 참조하십시오.
엔진은 시작 시 메모리 16MB를 텍스처 스트리밍 시스템용으로 예약합니다. 게임에서 텍스처 스트리밍을 사용하지 않는 경우에는 각 대상 플랫폼에 대한 프로젝트의 settings.ini 파일에서 streaming_buffer_size 키 값을 0으로 설정하여 이 메모리가 할당되지 않도록 하면 됩니다. Stingray 엔진 settings.ini 파일 참조를 참조하십시오.
수준에 조명을 베이킹하는 경우 게임에서 로드해야 할 추가 텍스처를 생성합니다. 베이킹하면 CPU 및 GPU 사용을 줄이면서 렌더링 품질을 높일 수 있긴 하지만 항상 런타임 메모리 사용이 늘어날 수밖에 없습니다.
라이트 맵이 너무 많은 메모리를 차지한다면 다음과 같은 아이디어를 시도해 볼 수 있습니다.
아무것도 베이킹하지 말고 음영처리 환경에서 제공하는 전역 분산 맵과 함께 사용하는 경우에만 동적 조명을 사용해 보십시오. 이렇게 하면 광원 맵 텍스처를 함께 없앨 수 있습니다. 전역 환경 조명을 참조하십시오.
라이트 베이킹을 장면의 일부 중요한 오브젝트로만 제한하고 다른 오브젝트는 동적 직접 조명과 전역 베이킹된 확산 조명을 사용하도록 유지합니다.
Light Baking 대화 상자에서 lightmap resolution 설정을 낮춥니다. 그러면 생성된 텍스처의 크기가 작아지지만 정밀도와 선명도가 떨어집니다. Stingray 베이커로 베이킹을 참조하십시오.
이런 식으로 라이트 맵 해상도를 낮추었을 때 베이킹된 조명에서 충분한 정밀도를 얻을 수 없다는 것을 알게 된 경우 직접 조명이 아닌 간접 조명만 베이킹을 시도할 수 있습니다. 해상도를 낮추는 효과는 일반적으로 간접 조명의 경우 눈에 잘 띄지 않으며 어쨌든 흐릿하고 저주파인 경향이 있습니다.
프로젝트의 다른 텍스처 리소스에서처럼 Texture Manager에서 라이트 맵에 대한 텍스처 압축 및 밉맵 생성 설정을 제어할 수 있습니다. 위에 나온 텍스처 최적화를 참조하십시오.
프로젝트에 추가할 각 모델 자산에 정점과 데이터 채널이 많을수록 해당 메시를 게임으로 로드하는 데 더 많은 메모리가 사용됩니다. 모델링 모두(즉, 3ds Max 또는 Maya)에서 메시를 최적화해 정점 수를 줄이고 게임에 필요 없는 모든 채널(예: 추가 UV 집합)을 제거하여 메모리를 약간 절약할 수 있습니다.
목표로 삼을 정점 수에 대해 정해진 규칙은 없습니다. 텍스트 크기와 마찬가지로 게임을 렌더링할 때 모델의 시각적 품질을 기준으로 필요한 메모리의 균형을 조절해야 하며 이는 모델 크기, 카메라와의 거리와 게임 플레이 또는 화면의 중요성에 따라 달라집니다. 일부 모델은 마치 영화 촬영장처럼 처리할 수 있습니다. 즉, 플레이어가 특정 건물 또는 오브젝트의 뒷면을 볼 일이 절대 없는 경우 최소한의 정점을 사용해 뒷면을 평면으로 만들 수 있습니다.
시작할 때 메시가 과도하게 크기 않은 경우 텍스처를 최적화할 때처럼 메모리 사용에 큰 차이를 발견하긴 쉽지 않을 것입니다. 하지만 동시에 게임에 다양한 메시가 많을수록 추가되는 최적화 범위가 줄어듭니다.
텍스처와 메시 다음으로 게임에서 가장 많은 메모리를 사용하는 리소스 유형은 오디오 파일입니다.
위에서 설명한 텍스처 및 메시와 마찬가지로 사운드의 품질을 일부 포기하면서 필요한 메모리를 줄일 수 있습니다. 그러나 디스크에서 직접 오디오 스트리밍, Wwise 엔진으로 내부에서 할당된 메모리 풀 조정 등과 같은 여러 가지 보완 방법을 사용할 수 있습니다.
전체 내용은 오디오 메모리 사용 최적화를 참조하십시오.
물리적 하위 시스템에서 수행할 수 있는 기본 메모리 최적화로는 액터 및 무버가 필요 없는 유닛에 대해 액터 및 무버를 생성하지 않는 것입니다. 유닛을 생성할 때마다 유닛의 액터 및 무버를 위한 약간의 추가 공간이 메모리에 예약됩니다.
충돌 감지 시 고려해야 하는 물리적 오브젝트를 나타내는 액터만 유닛에 생성합니다.
제어 가능한 캐릭터 또는 오브젝트를 나타내는 무버만 유닛에 생성합니다.
게임에 물리적 요소를 사용할 필요가 전혀 없는 경우에는 Stingray 표준 공간에서 비활성화하여 메모리를 절약할 수 있습니다. 이렇게 하려면 표준 공간을 생성하는 stingray.Application.new_world ()를 호출할 때 stingray.Application.DISABLE_PHYSICS를 전달합니다. Appkit를 사용하는 경우에는 core/appkit/lua/simple_project.lua 파일에서 이렇게 합니다.
Asset Browser에서 애니메이션 클립을 선택하면 Property Editor에서는 컴파일 시 적용할 수 있는 압축 설정 집합을 제공합니다. 이러한 압축 설정을 활성화하면 런타임 시 메모리를 약간 절약할 수 있지만 애니메이션 곡선이 약간 부정확해지는 것을 감수해야 합니다.
위치, 회전 및/또는 배율 곡선에 대한 압축을 개별적으로 활성화하고, 데이터가 압축되는 정도와 허용되는 오차의 크기 간에 균형을 제어하는 허용 오차 값을 조정하여 각 클립에 대한 압축 설정을 미세하게 조정할 수 있습니다.
자세한 내용은 애니메이션 클립 특성, 애니메이션 압축 및 애니메이션 최적화를 참조하십시오.
Lua 환경에서 사용하는 메모리의 양을 계속해서 줄이는 방법:
stingray.Raycast와 같이 많은 수의 전체 사용자 데이터 오브젝트를 생성하지 마십시오.
Vector3Box 및 QuaternionBox와 같은 "box" 오브젝트를 사용하여 게임 중 프레임 전체에서 Vector3 및 Quaternion과 같은 임시 오브젝트를 저장하려는 경우 게임 중 힙에 새 인스턴스를 반복적으로 생성하지 않도록 초기화 시 다시 사용할 수 있는 풀에 box를 생성합니다. 오브젝트 수명 및 사용자 데이터 바인딩도 참조하십시오.
Lua에서 코딩의 일반적인 모범 사례를 사용하는 경우 기본 가비지 수집 시스템에 변형을 너무 많이 배치하지 마십시오. 예를 들어 루프 내에서 문자열을 연결하지 말고 대신 문자열 조각을 테이블에 삽입하여 한 번 연결합니다.
Lua 환경의 가비지 수집 설정을 구성하는 데 익숙한 경우 stingray.Script 오브젝트가 제공하는 함수를 사용해 구성할 수 있습니다.
Lua 메모리 문제에 대한 내용은 이 블로그 게시글을 참조하십시오.
게임 내 다양한 리소스 유형 및 하위 시스템에서 사용하는 메모리를 줄이기 위해 위의 옵션을 설정한 후에도 여전히 너무 많은 메모리가 사용되는 경우 수준에서 불필요하게 또는 알 수 없이 메모리를 사용하고 있지는 않은지 철저하게 검사합니다.
나중에 생성 취소하지 말고 흐름 및 Lua에서 런타임 시 너무 많은 유닛을 생성하지 마십시오. 유닛의 추가 인스턴스는 메모리에서 추가 공간을 너무 많이 차지하지 않으므로 상당히 자유롭게 생성할 수 있습니다. 예를 들어 인스턴스(예: 총알 또는 화살과 같은 발사체)를 수천 개 생성한 경우 메모리 사용이 늘어나며 이러한 인스턴스에 물리적 액터가 있는 경우는 특히 더 그렇습니다.
절대 보이지 않는 유닛을 확인해 지면 또는 다른 메시 안으로 숨깁니다. 가능한 경우에는 수준 및 리소스 패키지에서 제거하십시오.