안동민 개발노트

본문 시작

물리 시뮬레이션과 Force 적용

Chaos 물리 시뮬레이션을 활성화하고 Force·Impulse·물리 머티리얼로 물체의 운동과 반응을 제어합니다.

이전 절에서 우리는 트리거 박스와 오버랩 이벤트를 통해 비물리적인 상호작용을 감지하는 방법을 배웠습니다.

이제 게임 월드 내에서 오브젝트들이 중력의 영향을 받거나, 충돌 시 튕겨 나가고, 외부 힘에 의해 움직이는 등 현실적인 물리적 상호작용을 구현하는 방법을 다룰 시간입니다.

언리얼 엔진은 강력한 물리 시뮬레이션(Physics Simulation) 시스템을 제공하며, 이를 통해 오브젝트에 힘(Force)을 적용하여 동적인 게임플레이를 만들 수 있습니다.


물리 시뮬레이션이란?

물리 시뮬레이션은 게임 엔진이 현실 세계의 물리 법칙(중력, 질량, 마찰, 반발력 등)을 모방하여 3D 오브젝트의 움직임을 계산하는 과정입니다.

UE5의 Chaos 물리 엔진은 액터에 부착된 UPrimitiveComponent (예: UStaticMeshComponent, USkeletalMeshComponent, UBoxComponent, UCapsuleComponent 등)의 충돌 메시(Collision Mesh)와 물리 속성을 기반으로 작동합니다.

물리 시뮬레이션 활성화의 전제 조건

어떤 액터나 컴포넌트가 물리 시뮬레이션의 영향을 받으려면 다음 설정과 유효한 물리 바디가 필요합니다. 메시의 충돌 형상(스켈레탈 메시는 Physics Asset)과 Movable 설정도 확인합니다.

충돌 컴포넌트를 가질 것: 액터에 UPrimitiveComponent (가장 흔하게는 UStaticMeshComponent나 USkeletalMeshComponent)가 부착되어 있어야 합니다.

이 컴포넌트가 물리 시뮬레이션의 대상이 됩니다.

SetSimulatePhysics(true) 호출: 해당 컴포넌트의 물리 시뮬레이션을 활성화해야 합니다.

또한 해당 컴포넌트의 Collision Enabled는 QueryAndPhysics 또는 PhysicsOnly로 설정되어 있어야 합니다.

MyPhysicsActor.h (물리 시뮬레이션될 액터 예시)
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyPhysicsActor.generated.h"

class UStaticMeshComponent; // 스태틱 메시 컴포넌트 선언

UCLASS()
class MYPROJECT_API AMyPhysicsActor : public AActor
{
    GENERATED_BODY()

public:
    AMyPhysicsActor();

protected:
    virtual void BeginPlay() override;

    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
    UStaticMeshComponent* MyMesh; // 물리 시뮬레이션될 메시 컴포넌트
};
MyPhysicsActor.cpp (생성자)
#include "MyPhysicsActor.h"
#include "Components/StaticMeshComponent.h" // UStaticMeshComponent 헤더 포함

AMyPhysicsActor::AMyPhysicsActor()
{
    PrimaryActorTick.bCanEverTick = true; // 틱이 필요할 경우 활성화

    // 1. 스태틱 메시 컴포넌트 생성
    MyMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MyMesh"));
    RootComponent = MyMesh; // 루트 컴포넌트로 설정

    // (선택 사항) 스태틱 메시 에셋 할당 (블루프린트에서 할당하는 것을 권장)
    // static ConstructorHelpers::FObjectFinder<UStaticMesh> CubeMeshAsset(TEXT("/Game/StarterContent/Shapes/Shape_Cube.Shape_Cube"));
    // if (CubeMeshAsset.Succeeded())
    // {
    //     MyMesh->SetStaticMesh(CubeMeshAsset.Object);
    // }

    // 2. 물리 시뮬레이션 활성화
    MyMesh->SetSimulatePhysics(true);

    // 3. 충돌 설정: 물리 시뮬레이션과 쿼리 모두 가능하도록 설정
    // Project Settings에서 적절한 Collision Preset을 사용하는 것을 권장합니다.
    MyMesh->SetCollisionProfileName(TEXT("PhysicsActor")); // 또는 "BlockAllDynamic"
    // 또는 수동 설정:
    // MyMesh->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
    // MyMesh->SetCollisionObjectType(ECollisionChannel::ECC_PhysicsBody); // 물리 오브젝트 채널
}

void AMyPhysicsActor::BeginPlay()
{
    Super::BeginPlay();
    // 물리 시뮬레이션은 자동으로 시작됩니다.
}
에디터에서 확인

이 C++ 클래스를 기반으로 블루프린트(BP_MyPhysicsActor)를 만든 후 레벨에 배치합니다.

BP_MyPhysicsActor의 MyMesh 컴포넌트를 선택하고, 디테일 패널의 Physics 섹션에서 Simulate Physics가 체크되어 있는지 확인하세요.

먼저 MyMesh에 유효한 충돌 형상을 가진 메시 에셋을 할당합니다. 중력, Mobility, 시뮬레이션과 상대 충돌 응답이 맞으면 플레이 중 낙하·접촉 반응을 확인할 수 있습니다. 코드만으로 에셋이 지정되거나 움직임이 검증된 것은 아닙니다.


Force (힘) 적용하기

물리 시뮬레이션이 활성화된 오브젝트에 외부에서 힘을 가하여 움직임을 제어할 수 있습니다.

언리얼 엔진은 다양한 종류의 힘을 적용하는 함수를 제공합니다.

이 함수들은 주로 UPrimitiveComponent에 정의되어 있습니다.

주요 힘 적용 함수

  • AddForce(FVector Force, FName BoneName = NAME_None, bool bAccelChange = false)
  • 지정한 힘을 바디에 가합니다. 기본 모드에서는 같은 힘이라도 질량이 크면 가속도가 작으며, 추진력처럼 유지하려면 그 기간 동안 매 프레임 호출합니다.

  • bAccelChange = true이면 벡터를 질량에 독립적인 가속도 변화로 해석합니다. Force는 Impulse처럼 즉시 속도를 바꾸는 입력이 아닙니다.

  • BoneName을 지정하면 스켈레탈 메시의 특정 본에만 힘을 가할 수 있습니다.

  • AddImpulse(FVector Impulse, FName BoneName = NAME_None, bool bVelChange = false)
  • 오브젝트에 한 번의 충격량을 적용합니다. 폭발이나 총알에 맞는 것처럼 한 번의 타격으로 속도를 변화시킬 때 사용합니다.

  • bVelChange = true로 설정하면 질량과 상관없이 즉각적인 속도 변화를 줍니다.

  • AddTorqueInRadians(FVector Torque, FName BoneName = NAME_None, bool bAccelChange = false)
  • 오브젝트에 회전력(Torque)을 가합니다. 오브젝트를 회전시키거나 굴릴 때 사용합니다.

  • AddRadialForce(FVector Origin, float Radius, float Strength, ERadialImpulseFalloff Falloff, bool bAccelChange = false)
  • 호출 대상 컴포넌트의 바디 중 Origin과 Radius로 정한 범위에 방사형 힘을 가합니다.

  • Falloff: 힘의 감소 방식(Constant 또는 Linear).

  • 월드의 다른 컴포넌트를 자동으로 찾지 않습니다. 여러 물체를 밀려면 대상을 별도로 찾고 각각 적용해야 합니다.

호출 시간과 적용 대상

지속 힘과 단발 충격량, 컴포넌트 범위를 비교합니다.

호출 시간과 적용 대상
호출시간·질량 해석이 예제에서 읽을 범위
AddForce유지할 동안 반복 호출. 기본값은 질량의 영향 받음API 소개이며 아래 코드에는 실제 AddForce 호출이 없음
ApplyUpwardImpulse한 번의 충격량. 코드의 bVelChange는 falseMyMesh 한 바디에 적용하며 속도 변화는 질량에 따라 달라짐
ApplyExplosionForce호출할 때 방사형 힘 적용MyMesh의 반경 내 바디만 대상. 한 번 호출했다고 지속 힘이나 월드 전체 폭발이 되지 않음
AddForce
시간·질량 해석: 유지할 동안 반복 호출. 기본값은 질량의 영향 받음
이 예제에서 읽을 범위: API 소개이며 아래 코드에는 실제 AddForce 호출이 없음
ApplyUpwardImpulse
시간·질량 해석: 한 번의 충격량. 코드의 bVelChange는 false
이 예제에서 읽을 범위: MyMesh 한 바디에 적용하며 속도 변화는 질량에 따라 달라짐
ApplyExplosionForce
시간·질량 해석: 호출할 때 방사형 힘 적용
이 예제에서 읽을 범위: MyMesh의 반경 내 바디만 대상. 한 번 호출했다고 지속 힘이나 월드 전체 폭발이 되지 않음

출력 로그는 함수 호출을 알립니다. 실제 변위·충돌·힘의 크기를 측정한 결과는 아니며, 순간 방사형 충격에는 AddRadialImpulse를 구분해 사용합니다.

C++에서 Force 적용 예시

이제 AMyPhysicsActor에 마우스 클릭 시 상방향으로 임펄스를 가하는 기능을 추가해 봅시다.

MyPhysicsActor.h (AddForce/Impulse 테스트를 위한 함수 추가)
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyPhysicsActor.generated.h"

class UStaticMeshComponent;

UCLASS()
class MYPROJECT_API AMyPhysicsActor : public AActor
{
    GENERATED_BODY()

public:
    AMyPhysicsActor();

protected:
    virtual void BeginPlay() override;

    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
    UStaticMeshComponent* MyMesh;

public:
    // 블루프린트에서도 호출할 수 있도록 BlueprintCallable 추가
    UFUNCTION(BlueprintCallable, Category = "Physics")
    void ApplyUpwardImpulse();

    UFUNCTION(BlueprintCallable, Category = "Physics")
    void ApplyExplosionForce(FVector ExplosionOrigin, float ExplosionRadius, float ExplosionStrength);
};
MyPhysicsActor.cpp
#include "MyPhysicsActor.h"
#include "Components/StaticMeshComponent.h"
// AddRadialForce는 위 StaticMeshComponent가 상속받은 함수입니다.

AMyPhysicsActor::AMyPhysicsActor()
{
    // ... 기존 생성자 코드 ...
}

void AMyPhysicsActor::BeginPlay()
{
    Super::BeginPlay();
}

void AMyPhysicsActor::ApplyUpwardImpulse()
{
    if (MyMesh && MyMesh->IsSimulatingPhysics())
    {
        // Z축 양의 방향으로 50000 단위의 임펄스 적용
        // bVelChange = true;로 설정하면 질량과 관계없이 일정한 속도 변화를 줍니다.
        MyMesh->AddImpulse(FVector(0.0f, 0.0f, 50000.0f), NAME_None, false);
        UE_LOG(LogTemp, Warning, TEXT("Applied upward impulse to %s"), *GetName());
    }
}

void AMyPhysicsActor::ApplyExplosionForce(FVector ExplosionOrigin, float ExplosionRadius, float ExplosionStrength)
{
    if (MyMesh && MyMesh->IsSimulatingPhysics())
    {
        // MyMesh에 방사형 힘 적용
        // AddRadialForce는 UPrimitiveComponent의 함수입니다.
        // 폭발 지점에서 MyMesh까지의 거리에 따라 힘이 약해집니다 (Linear Falloff).
        MyMesh->AddRadialForce(ExplosionOrigin, ExplosionRadius, ExplosionStrength, ERadialImpulseFalloff::RIF_Linear);
        UE_LOG(LogTemp, Warning, TEXT("Applied radial force to %s from %s"), *GetName(), *ExplosionOrigin.ToString());
    }
}
테스트 방법 (블루프린트 연동)

BP_MyPhysicsActor 블루프린트를 레벨에 여러 개 배치합니다.

입력이 연결된 로컬 PlayerController 또는 플레이어 캐릭터의 이벤트 그래프를 엽니다. 별도의 BP_PhysicsLauncher 액터를 사용한다면 그 액터의 입력 수신을 먼저 설정합니다.

마우스 왼쪽 클릭 이벤트를 받습니다.

마우스 화면 좌표를 월드 위치·방향으로 변환한 뒤 시작점과 끝점을 만들고, Line Trace By Channel 또는 Sphere Trace By Channel로 대상을 검사합니다.

트레이스 결과로 감지된 Hit Actor가 BP_MyPhysicsActor 타입인지 Cast To MyPhysicsActor 노드를 사용하여 확인합니다.

성공적으로 캐스팅되면 해당 BP_MyPhysicsActor 레퍼런스에서 Apply Upward Impulse 또는 Apply Explosion Force 함수를 호출합니다.

Apply Explosion Force의 경우, 폭발 지점은 트레이스의 Hit Location으로, 반경과 힘은 임의의 값으로 설정할 수 있습니다.


물리 재질 (Physical Material)

물리 재질(Physical Material)은 오브젝트의 표면이 가진 물리적 특성(마찰, 반발력 등)을 정의하는 에셋입니다.

이를 메시 컴포넌트에 적용하면 해당 오브젝트가 다른 물리 오브젝트와 상호작용할 때 더욱 사실적인 물리 반응을 얻을 수 있습니다.

물리 재질 설정

콘텐츠 브라우저에서 마우스 오른쪽 버튼 클릭 > Physics > Physical Material을 선택하여 새 물리 재질 에셋을 생성합니다. (예: PM_Rubber, PM_Ice)

물리 재질 에셋을 열고 Friction (마찰), Restitution (반발력, 탄성), Density (밀도) 등을 조절합니다.

이 물리 재질을 UStaticMeshComponent 또는 UMaterialInterface (메시에 적용된 재질)의 속성으로 할당합니다.

  • 메시 컴포넌트에 직접 할당: UStaticMeshComponent->SetPhysMaterialOverride(UMyPhysicalMaterial); 또는 에디터 디테일 패널에서 Physics > Phys Material Override에 할당합니다.
  • 재질에 할당: UMaterialInterface 에셋을 열고 Physical Material 속성에 할당합니다. 단, 실제 적용 재질은 단순·복잡 충돌 및 컴포넌트 오버라이드에 따라 달라집니다.

물리 재질을 통해 오브젝트의 표면 특성을 섬세하게 조절하여 게임 월드의 물리적 상호작용에 깊이를 더할 수 있습니다.

예를 들어, 얼음 바닥 위에서는 마찰이 적어 더 미끄러지게 하고, 고무공은 더 높이 튀어 오르게 할 수 있습니다.

물리 재질은 마찰·반발 같은 접촉 특성을 설정하고, 힘 함수는 운동에 입력을 줍니다.


물리 시뮬레이션과 힘의 적용은 게임 월드에 생동감과 반응성을 부여하는 핵심 요소입니다.

액터에 물리 시뮬레이션을 활성화하고, AddForce, AddImpulse, AddRadialForce와 같은 함수들을 사용하여 원하는 물리적 상호작용을 구현할 수 있습니다.

또한 물리 재질을 통해 오브젝트의 표면 특성을 정의하여 더욱 사실적인 물리적 행동을 만들 수 있습니다.