
이 글은 Unity 프로젝트 최적화 팁을 설명하는 시리즈의 다섯 번째입니다. 더 적은 자원으로 더 높은 프레임 속도로 실행하기 위한 가이드로 사용하세요. 이 최선의 방법을 시도한 후에는 시리즈의 다른 페이지도 확인하세요:
물리는 복잡한 게임플레이를 생성할 수 있지만, 이는 성능 비용이 따릅니다. 이 비용을 알게 되면, 시뮬레이션을 조정하여 적절히 관리할 수 있습니다. 이 팁을 사용하여 목표 프레임 속도 내에서 유지하고 Unity의 내장 물리(NVIDIA PhysX)로 매끄러운 재생을 만드세요.
Unity 6 개발자 및 아티스트를 위한 최신 최적화 가이드를 확인하세요:
물리에서 사용되는 메시들은 조리라는 과정을 거칩니다. 이 과정은 메시가 레이캐스트, 접촉 등과 같은 물리 쿼리와 함께 작동할 수 있도록 준비합니다.
메시 충돌체는 물리를 위해 메시를 검증하는 데 도움이 되는 여러 조리 옵션을 가지고 있습니다. 메시가 이러한 검사를 필요로 하지 않는다고 확신하는 경우, 조리 시간을 단축하기 위해 이를 비활성화할 수 있습니다.
각 메시 충돌체의 조리 옵션에서 EnableMeshCleaning, WeldColocatedVertices 및 CookForFasterSimulation의 선택을 해제하면 됩니다. 이 옵션은 런타임에 절차적으로 생성된 메시에는 유용하지만, 메시가 이미 적절한 삼각형을 가지고 있다면 비활성화할 수 있습니다.
또한, PC를 대상으로 하는 경우 Use Fast Midphase를 활성화 상태로 유지해야 합니다. 이것은 시뮬레이션의 중간 단계에서 PhysX 4.1의 더 빠른 알고리즘으로 전환하여 물리 쿼리를 위한 잠재적으로 교차하는 삼각형의 작은 집합을 좁히는 데 도움이 됩니다.
자세한 내용은 조리 옵션 문서에서 확인하세요.

게임 플레이 중에 절차적으로 메시를 생성하는 경우, 런타임에 메시 충돌체를 생성할 수 있습니다. 그러나 메시에 메시 충돌체 구성 요소를 직접 추가하면 메인 스레드에서 물리를 조리/베이킹합니다. 이로 인해 상당한 CPU 시간이 소모될 수 있습니다.
메시 충돌체와 함께 사용할 수 있도록 메시를 준비하고 베이킹된 데이터를 메시 자체와 함께 저장하려면 Physics.BakeMesh을 사용하세요. 이 메시를 참조하는 새로운 MeshCollider는 이 미리 구운 데이터를 재사용합니다 (메시를 다시 구우는 대신). 이것은 씬 로드 시간이나 나중에 인스턴스화 시간을 줄이는 데 도움이 될 수 있습니다.
성능을 최적화하려면 C# 작업 시스템을 사용하여 메시 요리를 다른 스레드로 오프로드할 수 있습니다.
여러 스레드에서 메시를 굽는 방법에 대한 자세한 내용은 이 예제를 참조하십시오.

플레이어 설정에서 가능한 경우 미리 굽기 충돌 메시를 체크하십시오. 플레이어와 게임 메커니즘 객체가 올바른 레이어에 있는지 확인하기 위해 충돌 매트릭스 설정을 검토하는 것도 권장합니다.
불필요한 레이어에 대한 트리거에서 콜백을 제거하는 것은 큰 이점이 될 수 있으므로 레이어 충돌 매트릭스를 단순화해 보십시오. 물리 설정을 프로젝트 설정 > 물리을 통해 편집할 수 있습니다.
자세한 내용은 충돌 매트릭스 문서를 참조하십시오.

물리 엔진은 고정 시간 단계에서 실행됩니다. 프로젝트가 실행되는 고정 비율을 보려면 편집 > 프로젝트 설정 > 시간으로 이동하십시오.
고정 시간 단계 필드는 각 물리 단계에서 사용되는 시간 델타를 정의합니다. 예를 들어, 기본값인 0.02초(20ms)는 50fps 또는 50Hz에 해당합니다.
Unity의 각 프레임은 가변적인 시간 소요가 있으므로 물리 시뮬레이션과 완벽하게 동기화되지 않습니다. 엔진은 다음 물리 시간 단계까지 카운트합니다. 프레임이 약간 느리거나 빠르게 실행되면 Unity는 경과된 시간을 사용하여 적절한 시간 단계에서 물리 시뮬레이션을 실행할 시점을 알 수 있습니다.
프레임 준비에 오랜 시간이 걸리는 경우 성능 문제로 이어질 수 있습니다. 예를 들어, 게임에서 스파이크가 발생하면(예: 많은 GameObject 인스턴스화 또는 디스크에서 파일 로드), 프레임이 40ms 이상 걸릴 수 있습니다. 기본 20ms 고정 시간 단계로 인해, 이는 다음 프레임에서 두 개의 물리 시뮬레이션이 실행되어 가변 시간 단계에 "따라잡기"를 유도합니다.
추가 물리 시뮬레이션은 프레임 처리에 더 많은 시간을 추가합니다. 저사양 플랫폼에서는 성능이 하락하는 악순환으로 이어질 수 있습니다.
준비하는 데 더 오랜 시간이 걸리는 후속 프레임은 물리 시뮬레이션의 백로그를 더 길게 만듭니다. 이로 인해 프레임이 더 느려지고 프레임당 실행해야 할 시뮬레이션이 더 많아집니다. 결과적으로 성능이 점점 더 나빠집니다.
결국 물리 업데이트 간의 시간이 최대 허용 시간 단계를 초과할 수 있습니다. 이 컷오프 이후, Unity는 물리 업데이트를 중단하기 시작하고 게임이 끊깁니다.
물리와 관련된 성능 문제를 피하려면:
필요한 경우 프레임의 업데이트 단계에서 시뮬레이션 모드을 선택하여 물리 단계를 수동으로 시뮬레이션합니다. 이렇게 하면 물리 단계를 실행할 시점을 제어할 수 있습니다. 물리 시뮬레이션 시간을 동기화하기 위해 Time.deltaTime을 Physics.Simulate에 전달합니다. 이 접근 방식은 복잡한 물리 또는 매우 가변적인 프레임 시간의 장면에서 물리 시뮬레이션의 불안정을 초래할 수 있으므로 주의해서 사용하십시오.
자세한 내용은 Physics.Simulate 문서를 참조하십시오.

유니티 물리 엔진은 두 단계로 실행됩니다:
스윕 및 프룬 브로드페이즈의 기본 설정(편집 > 프로젝트 설정 > 물리 > 브로드페이스 유형)은 일반적으로 평평하고 많은 충돌체가 있는 세계에서 잘못된 긍정 결과를 생성할 수 있습니다. 장면이 크고 대부분 평평하다면 이 문제를 피하고 자동 박스 프룬 또는 멀티박스 프룬 브로드페이즈로 전환하세요. 이 옵션들은 세계를 그리드로 나누며, 각 그리드 셀은 스윕 및 프룬을 수행합니다.
멀티박스 프룬 브로드페이즈는 세계 경계와 그리드 셀의 수를 수동으로 지정할 수 있게 해주며, 자동 박스 프룬은 이를 자동으로 계산합니다.
물리 속성의 전체 목록은 여기에서 확인하세요.

특정 물리 바디를 더 정확하게 시뮬레이션하려면 Rigidbody.solverIterations 값을 증가시키세요.
이것은 Physics.defaultSolverIterations를 오버라이드하며, 이는 편집 > 프로젝트 설정 > 물리 > 기본 솔버 반복 횟수에서도 찾을 수 있습니다.
물리 시뮬레이션을 최적화하려면 프로젝트의 defaultSolveIterations에 상대적으로 낮은 값을 설정하세요. 그런 다음 더 많은 세부 정보가 필요한 개별 인스턴스에 더 높은 사용자 지정 Rigidbody.solverIterations 값을 적용하세요.
Rigidbody.solverIterations에 대한 추가 정보를 얻으세요.

기본적으로 유니티는 물리 엔진과의 변환 변경 사항을 자동으로 동기화하지 않습니다. 대신, 다음 물리 업데이트까지 기다리거나 수동으로 Physics.SyncTransforms를 호출할 때까지 기다립니다. 이것이 활성화되면, 해당 Transform 또는 그 자식에 있는 모든 Rigidbody 또는 Collider가 물리 엔진과 자동으로 동기화됩니다.
수동으로 동기화할 때
autoSyncTransforms가 비활성화되면, 유니티는 FixedUpdate의 물리 시뮬레이션 단계 전에 또는 Physics.Simulate를 통해 명시적으로 요청할 때만 변환을 동기화합니다. Transform 변경과 물리 업데이트 사이에 물리 엔진에서 직접 읽는 API를 사용하는 경우 추가 동기화가 필요할 수 있습니다. 예를 들어 Rigidbody.position에 접근하거나 Physics.Raycast를 수행하는 것이 포함됩니다.
성능 모범 사례
autoSyncTransforms가 최신 물리 쿼리를 보장하지만 성능 비용이 발생합니다. 각 물리 관련 API 호출은 동기화를 강제하며, 이는 성능을 저하시킬 수 있습니다. 특히 여러 연속 쿼리와 함께 사용할 때 더욱 그렇습니다. 다음 모범 사례를 따르십시오:
Physics.SyncTransforms에 대해 자세히 알아보십시오.

연락처 배열은 일반적으로 훨씬 더 빠르므로 일반적인 권장 사항은 충돌 콜백을 재사용하기보다는 이를 사용하는 것입니다. 그러나 특정 사용 사례가 있는 경우 다음 사항을 고려하십시오.
콜백 MonoBehaviour.OnCollisionEnter, MonoBehaviour.OnCollisionStay 및 MonoBehaviour.OnCollisionExit는 모두 충돌 인스턴스를 매개변수로 사용합니다. 이 충돌 인스턴스는 관리 힙에 할당되며 가비지 수집이 필요합니다.
생성된 가비지 양을 줄이기 위해 Physics.reuseCollisionCallbacks를 활성화하십시오(또한 프로젝트 설정 > 물리 > 충돌 콜백 재사용에서 찾을 수 있습니다). 이 기능이 활성화되면 Unity는 각 콜백에 단일 충돌 쌍 인스턴스만 할당합니다. 이것은 가비지 수집기를 위한 낭비를 줄이고 성능을 향상시킵니다.
일반적인 권장 사항은 성능 이점을 위해 항상 충돌 콜백 재사용을 활성화하는 것입니다. 이 기능은 코드가 개별 Collision 클래스 인스턴스에 의존하는 레거시 프로젝트에 대해서만 비활성화해야 합니다. 개별 필드를 저장하는 것이 비현실적입니다.
Physics.reuseCollisionCallbacks에 대해 자세히 알아보세요.

정적 충돌체는 Collider 구성 요소가 있지만 Rigidbody가 없는 GameObject입니다.
정적이라는 용어와는 달리 정적 충돌체를 이동할 수 있습니다. 이를 위해 물리 본체의 위치를 수정하기만 하면 됩니다. 위치 변경 사항을 누적하고 물리 업데이트 전에 동기화하세요. 정적 충돌체를 이동하기 위해 Rigidbody 구성 요소를 추가할 필요는 없습니다.
그러나 정적 충돌체가 다른 물리 본체와 더 복잡하게 상호작용하기를 원한다면 kinematic Rigidbody를 부여하세요. Transform 구성 요소에 접근하는 대신 Rigidbody.position 및 Rigidbody.rotation을 사용하여 이동하세요. 이것은 물리 엔진에서 더 예측 가능한 동작을 보장합니다.
참고: 개별 Static Collider 2D를 런타임에 이동하거나 재구성해야 하는 경우, Rigidbody 2D 구성 요소를 추가하고 Static Body Type으로 설정하세요. Collider 2D가 자체 Rigidbody 2D를 가질 때 시뮬레이션이 더 빠릅니다. Collider 2D 그룹을 런타임에 이동하거나 재구성해야 하는 경우, 각 GameObject를 개별적으로 이동하는 것보다 단일 숨겨진 부모 Rigidbody 2D의 자식으로 두는 것이 더 빠릅니다.
Rigidbodies에 대한 더 많은 정보를 얻으세요.
특정 거리와 방향 내에서 3D 프로젝트의 충돌체를 감지하고 수집하려면 raycasts 및 BoxCast와 같은 다른 물리 쿼리를 사용하세요. 참고하세요
OverlapSphere 또는 OverlapBox와 같이 배열로 여러 충돌체를 반환하는 물리 쿼리는 관리 힙에서 해당 객체를 할당해야 합니다. 이는 가비지 수집기가 결국 할당된 객체를 수집해야 함을 의미하며, 잘못된 시간에 발생하면 성능이 저하될 수 있습니다.
이 오버헤드를 줄이기 위해 해당 쿼리의 NonAlloc 버전을 사용하세요. 예를 들어, 특정 지점 주변의 모든 잠재적 충돌체를 수집하기 위해 OverlapSphere를 사용하는 경우, 대신 OverlapSphereNonAlloc를 사용하세요.
이것은 충돌체 배열(결과 매개변수)을 버퍼로 작용하도록 전달할 수 있게 해줍니다. NonAlloc 메서드는 가비지를 생성하지 않고 작동합니다. 그렇지 않으면 해당 할당 메서드처럼 작동합니다.
NonAlloc 메서드를 사용할 때 충분한 크기의 결과 버퍼를 정의해야 한다는 점에 유의하세요. 버퍼는 공간이 부족해도 성장하지 않습니다.
2D 물리
위의 조언은 2D 물리 쿼리에는 적용되지 않으므로, Unity의 2D 물리 시스템에서는 메서드에 "NonAlloc" 접미사가 없습니다. 대신, 여러 결과를 반환하는 메서드를 포함한 모든 2D 물리 메서드는 배열이나 리스트를 수용하는 오버로드를 제공합니다. 예를 들어, 3D 물리 시스템에는 RaycastNonAlloc과 같은 메서드가 있지만, 2D에서는 배열이나 List를 매개변수로 사용할 수 있는 Raycast의 오버로드 버전을 사용합니다.
var results = new List();
int hitCount = Physics2D.Raycast(origin, direction, contactFilter, results);
오버로드를 사용하면 전문화된 NonAlloc 메서드 없이 2D 물리 시스템에서 비할당 쿼리를 수행할 수 있습니다.
자세한 내용은 NonAlloc 메서드 문서를 참조하세요.
Physics.Raycast를 사용하여 레이캐스트 쿼리를 실행할 수 있습니다. 그러나 레이캐스트 작업이 많으면(예: 10,000명의 에이전트에 대한 시야 계산) 상당한 CPU 시간을 소모할 수 있습니다.
C# 작업 시스템을 사용하여 쿼리를 배치하려면 RaycastCommand를 사용하세요. 이것은 메인 스레드에서 작업을 오프로드하여 레이캐스트가 비동기적으로 병렬로 발생할 수 있도록 합니다.
예제는 RaycastCommands 문서에서 확인하세요.
문제 있는 충돌체나 불일치를 해결하는 데 도움이 되도록 Physics Debug 창(Window > Analysis > Physics Debugger)을 사용하세요. 이것은 서로 충돌할 수 있는 GameObject의 색상 코드가 표시된 지표를 보여줍니다.
자세한 내용은 Physics Debugger 문서를 참조하세요.


Unity 베스트 프랙티스 허브에서 더 많은 베스트 프랙티스와 팁을 찾아보세요. 업계 전문가와 Unity 엔지니어 및 기술 아티스트가 만든 30개 이상의 가이드 중에서 선택하여 Unity의 도구 세트와 시스템을 효율적으로 개발하는 데 도움을 받을 수 있습니다.