물리와 충돌은 거의 모든 게임에서 다뤄지는 오브젝트간의 상호작용 중 하나.
물리 연산은 생각보다 무거운 편이다.
하지만 많이 쓸 수 밖에는 없다.
Rigidbody가 없어도 충돌 감지만 가능하지만, 일반적으로 Rigidbody와 함께 사용되어 충돌 시 물리적 상호작용을 일으킨다.힘이 적용되더 있어 계속 떨어진다.Rigidbody 가 외부의 물리적 힘(충격)을 받지 않지만, 다른 Rigidbody에는 영향을 줄 수 있다. 이 경우 Transform을 직접 제어할 수 있다. 또한 충돌 감지도 가능하다.Update()와 달리 물리 갱신 주기에 맞춰 일정한 간격(20ms)으로 호출. 물리 관련 로직은 이 함수 안에서 작성하는 것을 권장.중력이 적용되어 있는 상태에서 오브젝트를 아주 높이 놓은 상태에서 떨어뜨리면 땅을 뚫고 가버린다.
물리 엔진은 꽤 무거운 연산이기 때문에 매 Frame 별로 연산되는 것이 아니라 특정 시점에서만 연산을 하게 된다.
그 연산 사이에 충돌이 발생되면 충돌감지를 하지 못 하게 된다.(터널, 터널링) 이에, 연산을 어떻게 할 것인지에 대한 옵션을 줄 수 있다.
이산보다는 자주 연산하기 때문에 CPU 부담이 높다.대략 cpu 부담은
Continous Speculative>Continous Dynamic>Continous>>Discrete
유니티는 기본적으로 프레임 단위의 라이프사이클을 기준으로 동작한다.
우리가 가장 자주 사용하는 Update는 매 프레임마다 호출되며, 이 프레임 수는 PC 성능에 따라 초당 호출 횟수가 달라진다.
프레임마다 실행되는 일반 게임 로직은 호출 주기가 조금 변하더라도 큰 문제가 되지 않지만,
물리 연산이 프레임 변화의 영향을 직접 받게 되면 게임 속도가 느려지거나, 움직임이 불안정해지는 등 플레이에 큰 영향을 끼치게 된다.
이러한 문제를 방지하기 위해 유니티는 물리 엔진 연산을 일정한 시간 간격으로 실행하도록 분리해 두었다.
즉, 게임 로직과는 독립적으로 물리 연산이 항상 일정한 타이밍에 처리되도록 함으로써,
프레임 변동과 관계없이 물리적 동작이 안정적이고 일관되게 동작하도록 설계된 것이다.
전반적으로는 “프레임 의존 로직”과 “시간 의존 물리 연산”을 분리하기 위한 구조라고 보면 된다.
AddForce함수는 물리 작용에 많이 사용하는 함수로, 간단히 작용하는 힘에 대한 스칼라와 벡터, 그리고 힘의 종류를 RigidBody에 추가하는 것이다.
당연하지만, RigidBody 가 Enable 상태에서만 동작한다.
해당 함수는 FixedUpdate()에서 진행 하는 것이 일반 적이다.
Force Mode 는 말 그대로 힘의 종류로, 아래와 같이 제공되고 있다.

⬆️ 이것이 충돌이다! 라기 보다는 이해를 돕기 위한 그림 입니다. ⬆️

⬆️ 이것이 트리거다! 라기 보다는 이해를 돕기 위한 그림 입니다. ⬆️
※ 동작해야 하는 Object 의 한쪽에라도 Rigidbody 가 필요하다.
⚠️ 본 링크를 타고 들어가 문서의 아래쪽 충돌 액션 메트릭스를 꼭 읽어봅시다 ⚠️
⬇️아래는 강사님이 정리해 주신 메트릭스.
| Static Collider | Rigidbody Collider | Kinematic Rigidbody Collider | Static Trigger Collider | Rigidbody Trigger Collider | Kinematic Rigidbody Trigger Collider | |
|---|---|---|---|---|---|---|
| Static Collider | collision | trigger | trigger | |||
| Rigidbody Collider | collision | collision | collision | trigger | trigger | trigger |
| Kinematic Rigidbody Collider | collision | trigger | trigger | trigger | ||
| Static Trigger Collider | trigger | trigger | trigger | trigger | ||
| Rigidbody Trigger Collider | trigger | trigger | trigger | trigger | trigger | trigger |
| Kinematic Rigidbody Trigger Collider | trigger | trigger | trigger | trigger | trigger | trigger |
Collision, Trigger 둘다 Stay 에서 무언가 하는 경우가 많지 않다.
왜냐면 두 함수 모두 충돌/트리거가 계속 진행되고 있을 경우 특정 물리적 현상에 대한 무언가를 하는 경우는 많지 않기 때문이다.
대부분이 게임 로직에 대한 것은 update에서 진행된다.
물론, 물리적인 내용에 대한 구현이 필요하면 OnCollisionStay, OnTriggerStay 에서 하는 것이 맞다.
간단히 물리 엔진 라이브러리이다. C# 코드에서는 물리엔진 관련 함수등을 호출 할 수 있도록 도와주는 라이브러리라 봐도 무방하다.
Collider 를 반환해주는 메소드. Collider 배열을 반환한다.integer로 되어 있다. (참조된 GameObject 의 참조값)| Physics.OverlapSphere() | Physics.OverlapSphereNonAlloc() | |
|---|---|---|
| 메모리 사용량 | 새로운 배열을 할당하여 결과를 반환 | 기존 배열을 사용하여 결과를 반환 |
| 성능 | 메모리 할당 및 해제 과정이 추가되어 성능이 저하될 수 있음 | 메모리 할당 및 해제 과정이 없기 때문에 성능이 향상될 수 있음 |
| 사용법 | 새로운 배열을 생성하여 결과를 저장해야 함 | 기존 배열을 사용하여 결과를 저장할 수 있음 |
// Physics.OverlapSphere() 메서드 사용 예
int numColliders = Physics.OverlapSphere(position, radius);
Collider[] colliders = new Collider[numColliders];
Physics.OverlapSphere(position, radius, colliders);
// Physics.OverlapSphereNonAlloc() 메서드 사용 예
Collider[] colliders = new Collider[10];
int numColliders = Physics.OverlapSphereNonAlloc(position, radius, colliders);
개발 중, Object 에 물려있는 다른 Script 를 불러오거나, 자신의 자식 혹은 부모 Object 에 있는 Script 를 불러오는 경우도 있다.
이럴때 유용하게 사용할 수 있는 내용에 대해 간단히 정리한다.
// 해당 Script Class 위에 사용한다.
[RequireComponent(typeof(CLASSNAME001))] // 필요한 Script 가 인스팩트에 자동으로 추가되며, 인스팩트에서 삭제할 수 없다. (강제한다.)
public class CLASSNAME000 : MonoBehaviour
{
[SerializeField] private CLASSNAME001 _className001;
}
GetComponent<T>(); // 제네럴T 는 찾으려고 하는 스크립트를 현재 gameObject 내에서만 찾는다. 즉, 유니티 에디터 의 GameObject 에 인스팩터에 등록은 해놔야한다.
GetComponent<interface>(); // Interface 형도 사용 가능하다.
GetComponentInChildren<T>(); // 자식 gameObject 에 대해 모두 찾는다.
GetComponentInParent<T>(); // 최상위 부모 gameObject 까지 모두 찾는다.
// 아래 함수들은 모두 배열로 받는다.
GetComponents<T>();
GetComponentsInChildren<T>();
GetComponentsInParent<T>();
// Element 로부터 gameObject 를 가져올 수도 있다 (물론 다른 방법으로 Object 를 불러 올 수도 있다.)
_className001.gameObject;
GetComponent 는 Update에서 사용하는 것은 좋지 않다.
기본적으로 GetComponent는 선형탐색을 통하여 게임오브젝트내 모든 컴포넌트를 확인하고 지정된 타입과 일치할 경우 반환하는 형태로 되어 있다.
즉, CPU 에 영향이 큰 동작을 한다. 또한, 반환시 매번 새로운 참조를 생성하기 때문에 캐싱하지 않거나 로딩한 참조를 반환 및 정리하지 않으면 GC 성능 문제로 이어질 수도 있다.
따라서, GetComponent는 Awake나 Start등에서 한번만 검색하고 캐싱해 둔 상태로 사용하는 것을 권장하는 편이다.
물리 작용에 대한 대상에 대한 그룹 관리로 볼 수 있다.
묶인 Layer 를 통해 Layer 간 충돌에 대한 Logic 을 구현하면 비교적 쉽게 구현이 가능하(다고한)다.
가령, Monster 와 Player, Ground, 그외 맵 내 Object 들에 대해 Layer 를 각각 주고
각 Layer 간 충돌에 대한 규칙을 상세히 설계하면 tag 나 name 을 이용한 추가적인 작업 없이
일괄적으로 적용하는 것이 가능하다는 것이다.
과거 GPT 를 이용한 게임 만들기에서도
적끼리는 겹치지 못하게, Player 의 모함과도 겹치지 못하게,
단, 모함이 생성한 탐재기는 충돌은 없도록 할때 Layer 를 활용(하라고 GPT가 말)하였다.
다음 교안에도 배우지만 먼저 이야기 하자면..
Bit 연산을 이용하여 Layer Matching 을 찾는 것을 할 수 있다.
"IP Network에서 netmask 를 이용하여 network id 와 host id 를 뽑는 것처럼"
4byte 로 이루어진 layer 값을 32개의 bit 를 이용하여 and연산을 통해 matching 되는 bit 를 찾는 것이다.
그래서 실제 Unity Editor 내에서도 Layer 는 최대 32개(이미 given 으로 주어진 것들이 4~5개 정도 있다)까지 생성할 수 있다.
// 간단한 예시.
// bit shift 를 꼭 해줘야 한다.
if (((1 << other.gameObject.layer) & _layerMask.value) != 0)
{
_helicopterController = other.gameObject.GetComponent<HelicopterController>();
}

❓이친구는 아니다.⬆️

오브젝트를 만들었을 때 보이던 일종의 뼈대 같이 보이던 그것.

오브젝트를 선택 하면 보이긴 하다만, 매번 찾아서 클릭해 보는 것은 귀찮은 짓이다.
기즈모는 해당 오브젝트의 위치나 기울어진 정도등을 잘 확인할 수 있도록 도와주는 녀석이다. 하지만 빈 오브젝트를 생성하거나 오브젝트가 매우 작을 경우 이를 확인하는 것은 매우 어렵다. 때문에 아래와 같이 임의적으로 오브젝트를 확인하기 위해 기즈모의 형태 및 색상등을 변경하면 조금 더 보기 편해진다

디버깅 용도로 사용하면 굉장히 편리하다!