본문 바로가기
유니티 공부/Unity

Unity - velocity vs addForce +) Velocity 덮어쓰기 , AddForce 무시 문제

by 코딩하는 돼징 2025. 5. 22.
반응형

들어가기 앞서 간단한 비유로 먼저 이해해보자

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 체크 해보자!

반응형

댓글