아래 공식 문서를 참고했습니다.
https://docs.unity3d.com/Packages/com.unity.physics@1.3/manual/collision-queries.html
Collision queries | Unity Physics | 1.3.9
Collision queries Collision queries (also known as spatial queries) are one of the most important features of any physics engine and often drive a significant amount of game logic. Unity Physics has a powerful collision query system which supports queries
docs.unity3d.com
Unity에서 물리 시스템은 게임 개발의 핵심 요소입니다.
오늘은 Unity의 물리 시스템 중에서 충돌 쿼리와 콜라이더 시스템에 대해 자세히 알아보겠습니다.
1. Unity 물리 시스템 개요
Unity의 물리 시스템은 크게 두 가지 방식으로 충돌을 처리합니다:
- 콜라이더 (Collider) 시스템
- 물리 쿼리 (Physics Query) 시스템
1.1 콜라이더 시스템
public class ColliderExample : MonoBehaviour
{
void OnCollisionEnter(Collision collision)
{
Debug.Log("물체와 충돌했습니다!");
}
void OnTriggerEnter(Collider other)
{
Debug.Log("트리거 영역에 진입했습니다!");
}
}
콜라이더는 게임 오브젝트에 물리적 형태를 부여하는 컴포넌트입니다.
- 자동 충돌 감지
- Unity 물리 엔진과 통합
- 지속적인 충돌 체크
- Rigidbody와 함께 사용 시 물리 시뮬레이션 가능
1.2 물리 쿼리 시스템
public class PhysicsQueryExample : MonoBehaviour
{
void Update()
{
// 레이캐스트
RaycastHit hit;
if (Physics.Raycast(transform.position, transform.forward, out hit))
{
Debug.Log($"레이가 {hit.collider.name}와 충돌!");
}
// 구체 캐스트
Collider[] nearbyObjects = Physics.OverlapSphere(transform.position, 5f);
foreach (var obj in nearbyObjects)
{
Debug.Log($"근처에 있는 오브젝트: {obj.name}");
}
}
}
물리 쿼리는 필요할 때 수동으로 수행하는 충돌 검사입니다.
- 능동적인 충돌 검사
- 다양한 형태의 쿼리 지원 (Ray, Sphere, Box 등)
- 레이어 마스크를 통한 필터링
- 일회성 검사
쿼리에는 대표적으로 3가지가 있습니다.
레이캐스트 (Ray cast)
시작점, 끝점, 필터를 입력으로 사용
방향이 있는 선분의 모든 교차점을 찾습니다.
콜라이더 캐스트 (Collider cast)
콜라이더, 시작점, 끝점, 콜라이더 스케일을 입력으로 사용
주어진 경로를 따라 콜라이더를 이동시켜 충돌 검사
거리 쿼리 (Distance query)
포인트 거리 쿼리와 콜라이더 거리 쿼리로 구분
지정된 최대 반경 내의 가장 가까운 지점을 찾음
2. 성능 비교 분석
2.1 콜라이더 시스템 성능
public class ColliderPerformance : MonoBehaviour
{
void Start()
{
// 1. 콜라이더 수에 따른 검사 횟수
// N개의 콜라이더 = N(N-1)/2 회의 검사
// 10개: 45회
// 100개: 4,950회
// 1000개: 499,500회
}
void Example()
{
// 2. 콜라이더 타입별 성능 비교
// 단순한 순서: Sphere > Box > Capsule > Mesh
SphereCollider sphere; // 가장 가벼움
BoxCollider box; // 중간
CapsuleCollider capsule; // 중간
MeshCollider mesh; // 가장 무거움
}
}
2.2 물리 쿼리 성능
public class QueryPerformance : MonoBehaviour
{
[SerializeField] private LayerMask targetLayer;
private float queryInterval = 0.1f;
private float nextQueryTime;
void Update()
{
// 최적화된 쿼리 사용
if (Time.time >= nextQueryTime)
{
Physics.Raycast(transform.position, transform.forward, out hit, 100f, targetLayer);
nextQueryTime = Time.time + queryInterval;
}
}
}
3. 실제 게임플레이 적용 가이드
3.1 이동 및 물리 상호작용
public class PlayerController : MonoBehaviour
{
private CharacterController controller;
private float groundCheckDistance = 0.2f;
void Update()
{
// 이동 처리
Vector3 moveDirection = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
controller.Move(moveDirection * speed * Time.deltaTime);
// 지면 체크
bool isGrounded = Physics.Raycast(transform.position, Vector3.down, groundCheckDistance);
// 점프 처리
if (isGrounded && Input.GetButtonDown("Jump"))
{
// 점프 로직
}
}
}
3.2 전투 시스템
public class CombatSystem : MonoBehaviour
{
[SerializeField] private float attackRadius = 2f;
[SerializeField] private LayerMask enemyLayer;
void ProcessMeleeAttack()
{
// 근접 공격 판정
Collider[] hits = Physics.OverlapSphere(
transform.position,
attackRadius,
enemyLayer
);
foreach (var hit in hits)
{
var enemy = hit.GetComponent<Enemy>();
if (enemy != null)
{
enemy.TakeDamage(attackDamage);
}
}
}
void ProcessRangedAttack()
{
// 원거리 공격 판정
RaycastHit hit;
if (Physics.Raycast(firePoint.position, firePoint.forward, out hit))
{
var enemy = hit.collider.GetComponent<Enemy>();
if (enemy != null)
{
enemy.TakeDamage(attackDamage);
}
}
}
}
4. 최적화 전략
4.1 레이어 마스크 활용
public class OptimizedQueries : MonoBehaviour
{
[SerializeField] private LayerMask interactableLayer;
[SerializeField] private LayerMask groundLayer;
void Update()
{
// 특정 레이어만 체크하여 성능 최적화
Physics.Raycast(transform.position, transform.forward, out hit, 100f, interactableLayer);
Physics.Raycast(transform.position, Vector3.down, out hit, groundCheckDistance, groundLayer);
}
}
4.2 쿼리 빈도 최적화
public class QueryOptimization : MonoBehaviour
{
private float checkInterval = 0.1f;
private float nextCheckTime;
void Update()
{
if (Time.time >= nextCheckTime)
{
PerformPhysicsChecks();
nextCheckTime = Time.time + checkInterval;
}
}
void PerformPhysicsChecks()
{
// 주기적으로 수행할 물리 검사들
CheckEnemiesInRange();
CheckInteractableObjects();
}
}
Burst 컴파일된 Job 내에서 쿼리를 실행하면 최적의 성능을 얻을 수 있습니다.
복잡한 씬에서는 단일 레이캐스트도 스레드 호출로 전환하는 것이 좋습니다.
라고 공식문서에 되어있지만 이게 뭔지 몰랐습니다.
Unity.Collections - NativeArray 등을 위한 패키지
Unity.Jobs - Job System 사용을 위한 패키지
Unity.Burst - Burst 컴파일러 사용을 위한 패키지
이런 패키지들을 다운해야 적용가능한 기능이고
Job System: 멀티스레딩을 쉽게 구현
Burst: 코드를 더 빠른 네이티브 코드로 변환
둘을 함께 사용하면 고성능 병렬 처리 가능하다는 장점이 있습니다.
특히 많은 물리 연산이나 대규모 데이터 처리가 필요할 때 유용합니다
5. 결론
Unity의 물리 시스템은 상황에 따라 적절한 방식을 선택하는 것이 중요합니다.
- 콜라이더
- 기본적인 물리 충돌이 필요한 경우
- 지속적인 충돌 감지가 필요한 경우
- Rigidbody를 사용한 물리 시뮬레이션
- 물리 쿼리
- 공격 판정
- 시야 체크
- 특정 조건에서의 충돌 검사
- 일회성 검사
두 시스템을 적절히 조합하여 사용하면 효율적이고 안정적인 게임 물리 시스템을 구축할 수 있습니다.
'유니티 > 유니티 관련 지식' 카테고리의 다른 글
머티리얼을 위한 텍스처 사이트 모음 (0) | 2024.11.25 |
---|---|
유니티 오브젝트 폴링, 더 좋은거 쓰자! Better Object Pooling (0) | 2024.11.17 |
유니티 에셋 다운로드 폴더 변경 - 윈도우 (2) | 2024.11.15 |
AsstStudio : 유니티로 만든 게임들 해부하기 (4) | 2024.11.14 |