Collision
Rigidbody
Rigidbody 컴포넌트는 게임 오브젝트에 물리엔진을 적용하는 컴포넌트로, 물체의 질량, 공기저항, 중력에 대한 연산과 기본 지원 함수를 사용하여 현실적인 물리처리가 가능하도록 만든다. 참고로 Rigidbody가 없는 물체끼리는 충돌 감지가 되지 않는다.
ForceMode | 설명 | 특징 |
---|---|---|
Force | 물체의 질량과 시간에 따라 힘을 누적 | 가속하는 느낌 |
Acceleration | 질량에 영향을 받지 않고 힘을 가함 | 질량 무시 |
Impulse | 질량에 따라 순간적으로 힘을 가함 | 순간적인 힘, 점프 등 |
VelocityChange | 질량 무시, 속도만 즉시 변화 | 텔레포트 느낌, 질량 무시 |
public class RigidbodyTest : MonoBehaviour
{
// new : 부모의 rigidbody가 아닌, 새롭게 만드는 rigidbody라는 뜻
[SerializeField] (new) Rigidbody rigid;
[SerializeField] float power;
private float h;
// 입력은 Update에서
private void Update()
{
h = Input.GetAxis("Horizontal");
// 단 Impulse의 경우 Update에서 처리하는 것이 좋다.
if(Input.GetButtonDown("Jump"))
{
rigid.AddForce(Vector3.up * power, ForceMode.Impulse)
}
}
// AddForce와 같은 물리 연산은 FixedUpdate에서
private void FixedUpdate()
{
// 1. 지속적으로 힘 가하기 - 가속시켜주는 경우 사용
rigid.AddForce(Vector3.right * h * power);
// 2. 속도 설정하기 - 게임적 허용(비현실적인 움직임)
rigid.velocity = Vector3.right * power * h;
// 3. 회전력 가하기
rigid.AddTorque(Vector3.up * h * power);
// 4. 회전 속도 설정하기
rigid.angularVelocity = Vector3.up * power * h;
}
}
// 이 스크립트는 해당 컴포넌트가 있어야 동작한다는 의미
// 이렇게 하면 Bullet 스크립트를 추가하면 자동으로 Rigidbody가 추가된다.
// 심지어 Rigidbody 삭제도 불가능
[RequireComponent(typeof(Rigidbody)]
public class Bullet : MonoBehaviour
{
[SerializeField] Rigidbody rigid;
[SerializeField]
private void Awake()
{
// Rigidbody가 Bullet에 있는 것이 확실한 경우
// null이면 GetComponent를 하고 아니면 그냥 쓰고
rigid ??= GetComponent<Rigidbody>();
}
private void Update()
{
// 속도가 일정크기 이상인 경우만 (magnitude : 벡터의 크기)
// 이하인 경우 회전x
if(rigid.velocity.magnitude > 2)
{
// 날아가는 방향을 바라보도록 설정
transform.forward = rigid.velocity;
}
}
}
Mass
물체의 질량을 의미하며 [kg] 단위를 사용한다. 물리 엔진에 기본적으로 사용되는 물리량이다.
Drag
선형 운동에 대한 저항을 의미하며, 공기저항을 생각하면 된다.
Angular Drag
회전 운동에 대한 저항을 의미하며, 공이 무한대로 굴러가는 것을 방지하기 위한 바닥과의 마찰을 생각하면 된다.
UseGravity
해당 물체의 중력 적용 여부를 결정할 수 있다. Edit - ProjectSettings - Physics 탭에서 씬에 적용될 중력의 크기와 중력의 유무를 결정할 수 있다.
Is Kinematic
물리 엔진에 영향을 주지만, 자신은 영향을 받지 않는 것을 의미한다. 문이 잠겨있을 때 통과할 수는 없지만, 열리지 않게 설정할 때 유용하게 쓰인다.
Interpolate
빠르게 움직이는 물체가 부드럽게 보이도록 보간하는 기능으로, 시각적으로 튀는 현상을 방지하기 위해 사용한다.
Collision Detection
물체의 속도가 연산 속도를 뛰어넘는 경우 물체가 충돌하지 않고 통과하는 현상이 발생한다. Collision Detection를 체크할 경우 이러한 현상을 방지할 수 있어 총알과 같이 빠른 물체에 필수적이다. 연산량이 증가하지만 중요한 오브젝트의 경우 체크하여 조금 더 현실적으로 만들 필요가 있다.
rigid.collisionDetectionMode = CollisionDetectionMode.Continuous;
Constraints
축을 기준으로 하는 제약을 의미한다. 점프가 불가능한 게임에서는 Freeze Position Y를 체크하여 Y축의 값이 변하지 않도록 막는 것이 가능하다.
rigid.constraints = RigidbodyConstraints.FreezePositionY | RigidbodyConstraints.FreezeRotation;
Collider
게임 오브젝트의 물리적 충돌을 목적으로 Collider라는 모양을 정의하여 게임 오브젝트 간의 Collider로 부딪힘과 반발력을 처리한다. Collider는 충돌이 있을 경우 유니티 충돌 메시지를 받아 상황을 확인한다.
public class CollisionTest : MonoBehaviour
{
// 충돌(접촉)을 한 시점에 1회 호출
private void OnCollisionEnter(Collision collision)
{
Debug.Log($"{collision.gameObject.name}랑 충돌 시작");
}
// 충돌 중일 때 지속적으로 호출
private void OnCollisionStay(Collision collision)
{
Debug.Log($"{collision.gameObject.name}랑 충돌 중");
}
// 충돌(접촉)에서 벗어나는 순간 1회 호출
private void OnCollisionExit(Collision collision)
{
Debug.Log($"{collision.gameObject.name}랑 충돌 끝");
}
}
public class Bullet : MonoBehaviour
{
[Header("Components")]
[SerializeField] Rigidbody rigid;
[Header("Properties")]
[SerializeField] GameObject Effect;
private void OnCollisionEnter(Collision collision)
{
Destroy(this.gameObject)
// Destroy는 삭제 예정이기 때문에 this를 즉시 없애는 것이 아니다.
// 해당 프레임의 끝에 오브젝트를 파괴한다.
// 따라서 프레임이 끝나기 전까지는 transform을 사용하는 것이 가능하다.
// 즉시 삭제의 경우 DestroyImmediate가 있지만 웬만해서 사용x
Instantiate(Effect, transform.position, transform.rotation);
}
}
위 코드의 단점은 총알을 너무 빠르게 발사하면 포신 앞에서 총알끼리 부딪혀 폭발하는 현상이 발생한다는 것이다. 따라서 충돌을 무시할 상황과 해야하는 상황을 구분지어야할 필요가 있으며, 이는 Layer를 활용하여 해결하는 것이 가능하다.
Trigger
Trigger는 하나의 Collider가 충돌을 일으키지 않고, 다른 Collider 공간에 들어가는 것을 감지하는 것을 의미한다. Trigger는 겹침 상황에 있을 경우 유니티 트리거 메시지를 받아 상황을 확인한다.
Trigger로 사용하기 위해서는 오브젝트의 인스펙터 상에 있는 RenderingMode를 TransParent로 설정하여 투명하게 만들고 Is Trigger를 체크한다.
public class TriggerTest : MonoBehaviour
{
private void OnTriggerEnter(Collider other)
{
Debug.Log("Trigger Enter!");
}
private void OnTriggerStay(Collider other)
{
Debug.Log("Trigger Stay!");
}
private void OnTriggerExit(Collider other)
{
Debug.Log("Trigger Exit!");
}
}
항상 같은 자리에 위치하며 절대로 움직이지 않는 물체에 사용된다. 정적 collider는 rigidbody를 가지지 않기에 정적 collider끼리는 충돌하지 않으며, 다른 rigidbody와 충돌하더라도 정적 collider는 움직이지 않는다.
움직이는 물체에 사용되는 collider로 rigidbody를 가지고 있어 모든 종류의 collider와 충돌한다. 물리엔진에 의해 완전히 시뮬레이션 되며 스크립트로부터 적용되는 충돌과 힘에 반응하여 가장 흔하게 사용되는 collider 설정이다.
rigidbody 속성에 Is Kinematic을 활성화한 경우로 상황에 따라 움직이거나 활성화/비활성화되지만 그 외의 상황에서는 정적 collider처럼 동작한다. 장애물이나 이동하는 벽 등과 같은 곳에서 활용이 되며, 이동을 하며 다른 rigidbody collider에 작용할 수 있도록 만든다.
트리거 또한 collider와 같은 종류가 존재하지만, static Triger은 작동 방식에 유의할 필요가 있는데, static Trigger와 static collider는 서로 상호작용하지 않기 때문이다. 따라서 움직이는 물체의 충돌이 제대로 이루어지지 않는 경우 rigidbody와 collider 설정 여부를 확인해야한다.