들어가기 앞서 간단한 비유로 먼저 이해해보자
velocity는 마치 누군가가 자동차를 강제로 시속 50km로 설정해버리는 것과 같다. 브레이크, 중력, 마찰 같은 물리 법칙은 전혀 무시되고 곧바로 그 속도로 움직인다.
AddForce는 운전자가 엑셀을 밟아서 점점 시속 50km에 도달하는 것과 같다. 이 경우 중력, 마찰, 차량의 무게(질량)같은 물리 요소들이 모두 반영되어 자연스럽게 가속된다.
Rigidbody.velocity
Rigidbody의 현재 속도를 직접 덮어써서 지정하는 Vector3이다. 물리 엔진 결과가 아니라 사용자가 이 속도로 움직여!라고 명령하는 값이다.
velocity는 매 FixedUdpate마다 Rigidbody의 위치를 계산할 때 사용된다. 이 값을 덮어쓰면 기존에 적용하고 있던 모든 물리력(중력, AddForce, 충돌 등) 무시하고 즉시 적용된다.
rb.velocity = new Vector3(5f, rb.velocity.y, 0f);
주로 캐릭터의 일정 속도 이동, 방향 전환, 점프 초기화 등에 사용된다.
Rigidbody.AddForce
AddForce는 리지드바디에 힘(force) 을 가해서 자연스럽게 움직이게 한다. 이는 물리 엔진의 힘, 질량, 마찰, 중력 등을 모두 고려하여 적용된다.
RidgidBody에 힘을 누적시켜 가속도를 생성하고 이것이 속도로 전환된다. AddForce는 한 번 호출하면 그 다음 FixedUpdate에 반영된다. 그래서 즉시 반응은 약간 느릴 수 있지만 자연스럽게 작동한다. ForceMode에 따라 물리 연산 방식이 달라진다.
Unity에서 질량
Ridibody 컴포넌트에 포함되어있는데 mass값이 클수록 힘에 둔감해진다. 그러므로 AddForce로 밀어도 반응이 느리다.
ForceMode 종류
01 ForceMode.Force
공식 : F = m(질량) * a(가속도)
Rigidbody에 계속해서 힘을 가해서 점점 빨라진다. 질량이 클수록 힘을 더 줘야 효과가 크다.
void FixedUpdate() // 걷기
{
if (Input.GetKey(KeyCode.RightArrow))
{
rb.AddForce(Vector3.right * 5f, ForceMode.Force);
}
}
캐릭터 걷기, 바람 효과, 꾸준히 가속되는 물체 등에 사용한다.
02 ForceMode.Impulse
공식 : F = Δmv
순간적인 큰 힘(충격, 점프 등)을 짧은 시간에 준다.
void Update() // 점프
{
if (Input.GetKeyDown(KeyCode.Space))
{
rb.AddForce(Vector3.up * 7f, ForceMode.Impulse);
}
}
점프, 발사체 발사, 넉백 등에 사용한다.
03 ForceMode.VelocityChange
공식 : velocity += force
사실상 rb.velocity = rb.velocity + force처럼 바로 더한다. 즉시 이동하거나 튕기는 반응이 일어난다.
void Update() // 대시
{
if (Input.GetKeyDown(KeyCode.LeftShift))
{
rb.AddForce(transform.forward * 10f, ForceMode.VelocityChange);
}
}
질량이 무시되고 무조건 같은 거리를 움직인다.
04 Acceleration: 질량 고려 안 함
공식 : a = force
velocity를 직접 바꾸지 않고 시간이 지나면서 속도가 점점 증가한다.
void FixedUpdate() // 강풍 효과
{
if (Input.GetKey(KeyCode.W))
{
rb.AddForce(Vector3.forward * 2f, ForceMode.Acceleration);
}
}
질량이 무시되고 매 프레임 힘이 누적되어 점점 빨라진다.
Velocity 덮어쓰기 문제
플레이어가 발사기(Launcher) 위에 올라서면, 발사기는 AddForce(ForceMode.Impulse)를 사용해 플레이어를 위로 튕겨 보내려 한다. 그러나 플레이어의 이동 코드에서 매 프레임마다 rb.velocity를 직접 덮어쓰기 때문에, 발사기의 AddForce가 적용되기 전에 덮어씌워져 튕김 효과가 무시된다.
원인 분석
velocity는 즉시 적용되고 모든 물리력(중력, AddForce등)을 덮어쓴다.
// 이동 코드
void FixedUpdate()
{
float x = Input.GetAxisRaw("Horizontal");
rb.velocity = new Vector3(x * speed, rb.velocity.y, 0); // 여기가 문제
}
AddForce는 다음 FixedUpdate에서 적용되므로 타이밍이 충돌한다.
rb.AddForce(Vector3.up * force, ForceMode.Impulse);
발사기의 AddForce가 누적되고 있는 사이 이동 코드가 매 FixedUpdate에서 velocity를 다시 설정 결국 이전의 물리적 힘(점프, 넉백, 발사기 힘 등)이 모두 무효화된다.
해결 방법
01 발사 중 이동 코드 일시 정지
isLaunched가 true인 동안은 이동 로직을 무시하므로 velocity를 덮어쓰지 않으므로 발사기의 AddForce가 정상 반영된다. 쿨타임 후 이동 재개된다.
private bool isLaunched = false;
private float launchTimer = 0f;
public float launchCooldown = 0.3f;
void FixedUpdate()
{
if (isLaunched)
{
launchTimer += Time.fixedDeltaTime;
if (launchTimer >= launchCooldown)
{
isLaunched = false;
}
return;
}
float x = Input.GetAxisRaw("Horizontal");
rb.velocity = new Vector3(x * moveSpeed, rb.velocity.y, 0f);
}
public void Launch(Vector3 force)
{
isLaunched = true;
launchTimer = 0f;
rb.velocity = Vector3.zero;
rb.AddForce(force, ForceMode.Impulse);
}
02 이동을 AddForce로 전환
AddForce(ForceMode.Acceleration)은 질량을 무시하고 일정한 가속도를 줌 이동 또한 물리 기반으로 구현되므로 AddForce(Impulse)와 충돌하지 않음 velocity를 직접 덮어쓰지 않아서, 다른 물리 이벤트와 자연스럽게 통합됨
float x = Input.GetAxisRaw("Horizontal");
rb.AddForce(Vector3.right * x * accel, ForceMode.Acceleration);
결론
AddForce 무시되는 문제 생기면 velocity 체크 해보자!
'유니티 공부 > Unity' 카테고리의 다른 글
Unity - 캐싱(Caching) +) FSM 패턴,GetComponent ,Distance (2) | 2025.06.04 |
---|---|
Unity - Unity에서 수학 개념을 이해해보자 1편 - 벡터,내적,외적 (0) | 2025.05.23 |
Unity - Lighting을 활용한 낮과 밤 시스템 구현 방법 (0) | 2025.05.19 |
Unity - InputAction을 사용해서 플레이어 동작 기능 구현하기 (0) | 2025.05.16 |
Unity - 제네릭이 꼭 좋은 건 아니였다. (0) | 2025.05.12 |
댓글