무엇을 찾고 계신가요?
Hero background image
고급 프로그래밍 및 코드 아키텍처
이 기사에서는 코드 아키텍처를 탐색하여 그래픽 렌더링을 더 최적화하는 방법을 알아볼 수 있습니다. 이것은 Unity 프로젝트에 대한 최적화 팁을 포장하는 일련의 기사 중 네 번째입니다. 더 낮은 리소스로 더 높은 프레임 속도로 실행할 수 있는 가이드로 사용하십시오. 이러한 Best Practices를 시도한 후, 다음 시리즈의 다른 페이지를 확인하십시오. 강력한 성능을 위해 Unity 프로젝트를 구성 하 고급 그래픽에 대한 성능 최적화 PC 및 콘솔 게임에 대한 GPU 사용 관리 원활한 게임플레이를 위한 향상된 물리학 성능
이 페이지는 기계 번역되었습니다. 정확한 정보 출처로 원본 버전을 보려면 우측 링크를 참고하세요.
Unity PlayerLoop 이해하기

Unity PlayerLoop에는 게임 엔진의 핵심과 상호 작용하는 기능이 포함되어 있습니다. 이 구조에는 초기화 및 프레임당 업데이트를 처리하는 여러 시스템이 포함됩니다. 모든 스크립트는이 PlayerLoop에 의존하여 게임플레이 플레이를 만들 것입니다. 프로필을 작성할 때, PlayerLoop 아래에서 프로젝트의 유저. '광고 지면'의 타겟 고객 코드를 볼 수 있습니다. Editor 구성 요소는 EditorLoop 아래에서 볼 수 있습니다.

Unity의 FrameLoop실행 순서을 이해하는 것이 중요합니다. 각 Unity 스크립트는 사전 정의된 순서로 여러 이벤트 함수를 실행합니다. Awake, Start, Update 및 성능 향상을 위해 스크립트의 게임 수명주기를 만드는 다른 기능의 차이를 알아보십시오.

일부 예는 게임이 시작되기 전에 변수 또는 게임 상태를 초기화하기 위해 FixedUpdate 대신 Update 를 사용하거나, Rigidbody 를 사용하거나 Awake 대신 Start 를 사용하는 것입니다. 이들을 사용하여 각 프레임에서 실행되는 코드를 최소화하십시오. Awake 는 스크립트 인스턴스 수명 동안에 한 번만 호출되며 항상 시작 함수 이전에 호출됩니다. 즉, 시작을 사용하여 다른 객체와 대화할 수 있는 객체를 처리하거나 초기화된 것처럼 쿼리를 수행해야 합니다.

이벤트 함수의 구체적인 실행 순서에 대한 스크립트 게임 수명주기 흐름 차트를 참조하십시오.

Custom Update Manager 다이어그램
사용자 지정 업데이트 관리자 구축

프로젝트에 까다로운 성능 요구 사항이 있는 경우(예: 오픈 월드 게임) Update, LateUpdate 또는 FixedUpdate를 사용하여 사용자 지정 업데이트 관리자를 만들 수 있습니다.

Update 또는 LateUpdate의 일반적인 사용 패턴은 특정 조건이 충족된 경우에만 논리를 실행하는 것입니다. 이것은 이 조건을 확인하는 것 외에는 효과적으로 코드를 실행하지 않는 프레임당 여러 번의 콜백으로 리드 수 있습니다.

Unity Update 또는 LateUpdate와 같은 메시지 방법을 호출할 때마다 Interop 호출을 수행합니다. 즉, C/C++ 측에서 관리되는 C# 측으로 호출합니다. 작은 수의 객체의 경우, 이것은 문제가 아닙니다. 당신이 수천 개의 객체를 가지고있을 때,이 오버헤드는 중요 해지기 시작합니다.

이 Update Manager에 활성 객체를 구독하면 콜백이 필요하고, 구독이 필요하지 않을 때 구독을 취소합니다. 이 패턴은 MonoBehaviour 객체에 대한 많은 interop 호출을 줄일 수 있습니다.

구현의 예를 들어, 게임 엔진별 최적화 기술을 참조하십시오.

모든 프레임을 실행하는 코드를 최소화

코드가 모든 프레임을 실행해야 하는지 고려하십시오. Update, LateUpdate 및 FixedUpdate에서 불필요한 논리를 이동할 수 있습니다. 이러한 Unity 이벤트 함수는 모든 프레임을 업데이트해야 하는 코드를 넣을 수 있는 편리한 장소이지만 해당 주파수로 업데이트할 필요가 없는 모든 논리를 추출할 수 있습니다.

단지 상황이 변할 때 논리를 실행합니다. 이벤트의 형태로 관찰자 패턴과 같은 기술을 활용하여 특정 기능 서명을 트리거하십시오.

업데이트를 사용해야 하는 경우에는 각 레임마다 코드를 실행할 수 있습니다. 이것은 Time Slicing 을 적용하는 방법 중 하나입니다. 이는 여러 프레임에 걸쳐 무거운 작업량을 분배하는 일반적인 기술입니다.

이 예에서는 ExampleExpensiveFunction을 3개 프레임마다 실행합니다.

트릭은 다른 프레임에서 실행되는 다른 작업과 이것을 섞는 것입니다. 이 예에서는 Time.frameCount % interval == 1 또는 Time.frameCount % interval == 2 때 다른 비싼 함수를 "계획"할 수 있습니다.

또는 사용자 지정 Update Manager 클래스를 사용하여 프레임마다 구독된 객체를 업데이트합니다.

비싼 기능의 결과를 캐시

Unity 버전 2020.2 이전에서는 GameObject.Find, GameObject.GetComponentCamera.main이 비싸게 사용할 수 있으므로 업데이트 방법에서 호출하지 않는 것이 가장 좋습니다.

또한, OnEnableOnDisable에 비싼 방법을 자주 호출하는 경우에 배치하지 마십시오. 이러한 방법을 자주 호출하면 CPU 스픽에 기여할 수 있습니다.

가능하면 MonoBehaviour.AwakeMonoBehaviour와 같은 비싼 기능을 실행합니다. 초기화 단계에서 시작하십시오. 필요한 참조를 캐시하고 나중에 다시 사용하십시오. 더 자세한 스크립트 순서 실행을 위해 Unity PlayerLoop에 대한 이전 섹션을 참조하십시오.

다음은 반복되는 GetComponent 호출의 비효율적인 사용을 보여주는 예입니다.

void Update()
{
Renderer myRenderer = GetComponent<Renderer>();
ExampleFunction(myRenderer);
}

대신, 함수의 결과가 캐시되어 GetComponent를 한 번만 호출합니다. 캐시된 결과는 GetComponent에 대한 추가 호출 없이 Update에서 재사용할 수 있습니다.

이벤트 함수의 실행 순서에 대한 자세한 내용을 읽으십시오.

빈 Unity 이벤트 및 디버그 로그 설정을 피합니다.

로그 문 (특히 Update, LateUpdate 또는 FixedUpdate에서)은 성능을 낮출 수 있으므로 빌딩을 만들기 전에 로그 문을 비활성화하십시오. 이를 신속하게 수행하려면 조건적 속성을 사전 처리 지침과 함께 작성하는 것을 고려하십시오.

예를 들어, 아래와 같이 사용자 지정 클래스를 만들 수도 있습니다.

사용자 지정 클래스를 사용하여 로그 메시지를 생성합니다. Player Settings > Scripting Define Symbols 에서 ENABLE_LOG preprocessor를 비활성화하면 모든 로그 계정이 한 번에 사라집니다.

문자열과 텍스트를 처리하는 것은 Unity 프로젝트에서 성능 문제의 일반적인 원인입니다. 이것이 로깅 문 및 그 비싼 문자열 포맷을 제거하는 것이 잠재적으로 큰 성능 승리 일 수있는 이유입니다.

마찬가지로 빈 MonoBehaviours는 리소스가 필요하므로 빈 Update 또는 LateUpdate 방법을 제거해야 합니다. 테스트를 위해 다음 방법을 사용하는 경우 preprocessor 지침을 사용합니다.

#if UNITY_EDITOR
void Update()
{
}
#endif

여기에서 Update in-Editor 를 사용하여 불필요한 비용이 빌딩에 빠지지 않고 테스트할 수 있습니다.

10,000 개의 업데이트 호출에 관한 이 블로그 게시물은 Unity가 MonoBehaviour.Update를 어떻게 실행하는지 설명합니다.

Stack Trace 로깅 비활성화

Player Settings Stack Trace 옵션을 사용하여 로그 메시지가 나타나는 유형을 제어합니다. 애플리케이션이 릴리스 빌드에서 오류 또는 경고 메시지를 로깅하는 경우(예: 야생에서 충돌 보고서를 생성하는 경우), Stack Traces를 비활성화하여 성능을 향상시킵니다.

Stack Trace 로깅에 대해 자세히 알아보십시오.

해시 값 대신 문자열 매개 변수 사용

Unity는 문자열 이름을 내부적으로 Animator, Material 또는 Sheader 속성을 주소로 사용하지 않습니다. 속도를 위해, 모든 속성 이름은 Property ID 에 해시되며, 이 ID는 해당 속성에 대한 주소를 사용합니다.

Animator, Material 또는 셰이더에서 Set 또는 Get 메서드를 사용하는 경우 string-value 메서드 대신 integer-value 메서드를 활용합니다. string-valed 방법은 string 해시를 수행한 다음 hashed ID를 integer-valed 방법으로 전송합니다.

Animator 속성 이름을 위해 Animator.StringToHash를 사용하고 Material 및 셰이더 속성 이름을 위해 Shader.PropertyToID를 사용합니다.

관련된 것은 데이터 구조의 선택입니다. 이는 프레임당 수천 번 반복할 때 성능에 영향을 미칩니다. 올바른 구조를 선택하는 일반적인 가이드로서 C#의 MSDN 데이터 구조 가이드를 따르십시오.

오브젝트 풀 스크립트 인터페이스
당신의 물체를 병합

InstantiateDestroy가비지 컬렉션(GC) 스피크를 생성할 수 있습니다. 이 과정은 일반적으로 느리기 때문에 GameObjects를 정기적으로 인스턴스화하고 파괴하는 것(예: 총에서 총을 쏘는 것) 대신 사전 할당된 객체의을 사용하여 재사용 및 재활용할 수 있습니다.

CPU 스파이크가 덜 눈에 띄는 메뉴 화면 또는 로딩 화면과 같은 게임의 어느 시점에서 재사용 가능한 인스턴스를 만들 수 있습니다. 컬렉션으로 객체의이 " 풀"을 추적하십시오. 게임플레이 플레이 중에 필요한 경우 다음 사용 가능한 인스턴스 활성화하고 물체를 파괴하는 대신 오브젝트를 비활성화하십시오. 이것은 프로젝트에서 관리 할당 수를 줄이고 GC 문제를 예방할 수 있습니다.

마찬가지로 실행 중에 구성 요소를 추가하지 마십시오. AddComponent 호출은 약간의 비용이 발생합니다. Unity 실행 중에 구성 요소를 추가할 때마다 복제본 또는 기타 필요한 구성 요소를 확인해야 합니다. 이미 설정된 원하는 구성 요소를 사용하여 Prefab을 인스턴셔닝하는 것은 더 뛰어난 성능입니다. 따라서 오브젝트 풀과 함께 사용하십시오.

관련, Transforms를 이동할 때 Transform.SetPositionAndRotation을 사용하여 위치와 회전을 동시에 업데이트합니다. 이것은 변환을 두 번 수정하는 비용을 피합니다.

실행 중에 GameObject를 인스턴스화하고, 파트너를 설정하고, 최적화를 위해 다시 배치해야 하는 경우 아래를 참조하십시오.

Object.Instantiate에 대한 자세한 내용은 스크립팅 API를 참조하십시오.

Unity에서 간단한 Object Pooling 시스템을 만드는 방법을 여기 알아보십시오.

Scriptable 오브젝트 풀
ScriptableObjects의 힘을 활용하십시오.

MonoBehaviour 대신 ScriptableObject에 변경되지 않는 값이나 설정을 저장합니다. ScriptableObject는 프로젝트 내부에 살고 있는 자산입니다. 한 번만 설정해야 하며 GameObject에 직접 연결할 수 없습니다.

ScriptableObject에 필드를 생성하여 값이나 설정을 저장하고 MonoBehaviours에서 ScriptableObject를 참조하십시오. ScriptableObject의 필드를 사용하면 MonoBehaviour 객체를 인스턴스팅할 때마다 데이터가 불필요하게 복제되지 않도록 할 수 있습니다.

ScriptableObjects 소개 튜토리얼을 보시고 관련 문서를 여기 찾아보세요.

Unity key art 21 11
무료 e-북을 받으세요

우리의 가장 포괄적인 가이드 중 하나는 PC 및 콘솔용 게임 최적화(하다)에 대한 80가지 이상의 실행 가능한 팁을 수집합니다. 성공 및 Accelerate Solutions 엔지니어들에 의해 제작된 이 뎁스 / 게재되고 있는 광고의 개수 인 팁은 Unity 최대한 활용하고 게임 성능을 높일 수 있도록 도와줍니다.

이 콘텐츠가 마음에 드셨나요?