물리 시뮬레이션과 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로 설정되어 있어야 합니다.
#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; // 물리 시뮬레이션될 메시 컴포넌트
};#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 반복과 AddImpulse 한 번이라는 독립 입력 방식의 메시지/경과 상태를 비교한다. Chaos 내부 서브스텝·측정 변위로 확대하지 않는다.
UNREAL C++ · CH5-3
지속 힘·단발 충격량·적용 대상을 구분합니다
본문 API 소개와 실제 함수 호출을 비교합니다. 같은 벡터 크기라도 Force와 Impulse의 시간 의미·질량 처리·대상 범위가 다릅니다.
두 비교는 독립된 입력 방식입니다. 메시지 개수만으로 같은 변위를 보장하지 않으며, 원 코드의 ApplyExplosionForce는 MyMesh 수신 대상에 한 번 힘을 보내는 호출입니다.
| 호출 | 시간·질량 해석 | 이 예제에서 읽을 범위 |
|---|---|---|
| AddForce | 유지할 동안 반복 호출. 기본값은 질량의 영향 받음 | API 소개이며 아래 코드에는 실제 AddForce 호출이 없음 |
| ApplyUpwardImpulse | 한 번의 충격량. 코드의 bVelChange는 false | MyMesh 한 바디에 적용하며 속도 변화는 질량에 따라 달라짐 |
| ApplyExplosionForce | 호출할 때 방사형 힘 적용 | MyMesh의 반경 내 바디만 대상. 한 번 호출했다고 지속 힘이나 월드 전체 폭발이 되지 않음 |
- AddForce
- 시간·질량 해석: 유지할 동안 반복 호출. 기본값은 질량의 영향 받음이 예제에서 읽을 범위: API 소개이며 아래 코드에는 실제 AddForce 호출이 없음
- ApplyUpwardImpulse
- 시간·질량 해석: 한 번의 충격량. 코드의 bVelChange는 false이 예제에서 읽을 범위: MyMesh 한 바디에 적용하며 속도 변화는 질량에 따라 달라짐
- ApplyExplosionForce
- 시간·질량 해석: 호출할 때 방사형 힘 적용이 예제에서 읽을 범위: MyMesh의 반경 내 바디만 대상. 한 번 호출했다고 지속 힘이나 월드 전체 폭발이 되지 않음
ApplyUpwardImpulse의 로그는 호출을 알립니다. 실제 변위·충돌을 측정한 결과가 아닙니다. 순간 방사형 충격에는 AddRadialImpulse를 구분하며, 여러 물체에 적용할 대상 검색도 별도로 설계합니다.
이 예의 조건
- 본문과 원 도표에 제시된 사례의 입력·조건을 사용합니다. 설명용 수치나 독립 비교 경로는 각 단계에 표시합니다.
- 현재 상태·값과 전환 근거의 위치는 모든 단계에서 같습니다. 조건이 맞지 않거나 실행되지 않은 경로를 성공한 동작으로 해석하지 않습니다.
관계도의 보충 설명
| 범위 | 원문 |
|---|---|
| 관계·조건 설명 | 왼쪽 또는 위쪽은 같은 힘을 유지할 기간에 AddForce를 반복해서 같은 대상 바디에 보내는 예이고, 오른쪽 또는 아래쪽은 AddImpulse를 한 번 보내는 별도 비교다. 선은 호출 메시지이며 시간은 아래로 진행한다. 세 호출은 예시 게임 프레임이지 Chaos 내부 서브스텝이나 실측 변위가 아니다. |
| 원 표의 셀 | 유지할 동안 반복 호출. 기본값에서는 질량의 영향을 받음 |
| 관찰·상황 | 값·보충 정보 | 변화·이유 |
|---|---|---|
| 첫 프레임 두 독립 방식에서 같은 대상을 비교한다. |
|
|
| 뒤 프레임들 설명용 세 프레임의 메시지를 비교한다. |
|
|
| 해석 범위 호출 지속과 대상 범위를 읽는다. |
|
|
지속 힘·단발 충격량·적용 대상을 구분합니다
세 예시 프레임에서 AddForce 반복과 AddImpulse 한 번이라는 독립 입력 방식의 메시지/경과 상태를 비교한다. Chaos 내부 서브스텝·측정 변위로 확대하지 않는다.
UNREAL C++ · CH5-3
지속 힘·단발 충격량·적용 대상을 구분합니다
본문 API 소개와 실제 함수 호출을 비교합니다. 같은 벡터 크기라도 Force와 Impulse의 시간 의미·질량 처리·대상 범위가 다릅니다.
두 비교는 독립된 입력 방식입니다. 메시지 개수만으로 같은 변위를 보장하지 않으며, 원 코드의 ApplyExplosionForce는 MyMesh 수신 대상에 한 번 힘을 보내는 호출입니다.
| 호출 | 시간·질량 해석 | 이 예제에서 읽을 범위 |
|---|---|---|
| AddForce | 유지할 동안 반복 호출. 기본값은 질량의 영향 받음 | API 소개이며 아래 코드에는 실제 AddForce 호출이 없음 |
| ApplyUpwardImpulse | 한 번의 충격량. 코드의 bVelChange는 false | MyMesh 한 바디에 적용하며 속도 변화는 질량에 따라 달라짐 |
| ApplyExplosionForce | 호출할 때 방사형 힘 적용 | MyMesh의 반경 내 바디만 대상. 한 번 호출했다고 지속 힘이나 월드 전체 폭발이 되지 않음 |
- AddForce
- 시간·질량 해석: 유지할 동안 반복 호출. 기본값은 질량의 영향 받음이 예제에서 읽을 범위: API 소개이며 아래 코드에는 실제 AddForce 호출이 없음
- ApplyUpwardImpulse
- 시간·질량 해석: 한 번의 충격량. 코드의 bVelChange는 false이 예제에서 읽을 범위: MyMesh 한 바디에 적용하며 속도 변화는 질량에 따라 달라짐
- ApplyExplosionForce
- 시간·질량 해석: 호출할 때 방사형 힘 적용이 예제에서 읽을 범위: MyMesh의 반경 내 바디만 대상. 한 번 호출했다고 지속 힘이나 월드 전체 폭발이 되지 않음
ApplyUpwardImpulse의 로그는 호출을 알립니다. 실제 변위·충돌을 측정한 결과가 아닙니다. 순간 방사형 충격에는 AddRadialImpulse를 구분하며, 여러 물체에 적용할 대상 검색도 별도로 설계합니다.
이 예의 조건
- 본문과 원 도표에 제시된 사례의 입력·조건을 사용합니다. 설명용 수치나 독립 비교 경로는 각 단계에 표시합니다.
- 현재 상태·값과 전환 근거의 위치는 모든 단계에서 같습니다. 조건이 맞지 않거나 실행되지 않은 경로를 성공한 동작으로 해석하지 않습니다.
관계도의 보충 설명
| 범위 | 원문 |
|---|---|
| 관계·조건 설명 | 왼쪽 또는 위쪽은 같은 힘을 유지할 기간에 AddForce를 반복해서 같은 대상 바디에 보내는 예이고, 오른쪽 또는 아래쪽은 AddImpulse를 한 번 보내는 별도 비교다. 선은 호출 메시지이며 시간은 아래로 진행한다. 세 호출은 예시 게임 프레임이지 Chaos 내부 서브스텝이나 실측 변위가 아니다. |
| 원 표의 셀 | 유지할 동안 반복 호출. 기본값에서는 질량의 영향을 받음 |
| 관찰·상황 | 값·보충 정보 | 변화·이유 |
|---|---|---|
| 첫 프레임 두 독립 방식에서 같은 대상을 비교한다. |
|
|
| 뒤 프레임들 설명용 세 프레임의 메시지를 비교한다. |
|
|
| 해석 범위 호출 지속과 대상 범위를 읽는다. |
|
|
C++에서 Force 적용 예시
이제 AMyPhysicsActor에 마우스 클릭 시 위쪽으로 임펄스를 가하는 기능을 추가해 봅시다.
#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);
};#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());
}
}두 번째 헤더와 .cpp는 앞의 MyPhysicsActor에 함수를 추가하는 발췌입니다. 같은 클래스의 생성자·BeginPlay를 두 번 정의하지 않고 기존 정의에 새 함수만 연결합니다. ... 기존 생성자 코드 ...를 빈 생성자로 바꿔 앞의 메시 생성·루트·충돌 설정을 지우지 않습니다. 아래 클릭 실습에서는 각 배치 인스턴스에 메시 에셋·유효한 물리 바디를 지정하고 시뮬레이션 상태를 확인한 뒤 해당 인스턴스 함수를 호출합니다.
예시: 같은 입력이라도 질량과 시간이 달라지는 경우
원 ApplyUpwardImpulse 호출의 마지막 인자는 false이므로 질량의 영향을 받는 충격량입니다. 다른 힘과 접촉을 제외한 이상화 조건에서 같은 충격량 J를 질량 m과 2m의 바디에 한 번씩 주면 속도 변화는 J/m과 J/(2m)으로, 두 번째가 첫 번째의 절반입니다. 로그는 호출 사실을 알려 줄 뿐 이 속도 변화가 실제로 관측되었다는 기록은 아닙니다.
반면 AddForce는 힘을 유지하려는 기간에 반복 호출하는 계약입니다. 같은 힘 F를 다른 힘 없이 같은 질량 m에 시간 t 동안 유지하면 속도 변화는 F×t/m으로 계산할 수 있습니다. 이 이상화 식에서 시간을 두 배로 하면 변화도 두 배지만, 충돌·중력·저항이 있는 실제 장면 전체의 결과까지 두 배라고 단정하지 않습니다. 엔진의 AddForce 입력에 힘의 시간 적분을 한 번 더 곱하는 일과, 이 식으로 결과를 예상하는 일도 구별합니다.
시간·질량·대상 판단하기
같은 충격량을 받은 두 바디의 질량이 m과 3m입니다. 질량의 영향을 받는 기본 모드에서 두 속도 변화의 비를 구하세요. 또한 원 ApplyExplosionForce를 첫 번째 액터에서 한 번 호출하면 반경 안의 두 번째 액터에도 자동으로 적용되거나 다음 프레임까지 계속 추진되는지 판단하세요.
입력 계약을 따른 풀이
속도 변화는 J/m과 J/(3m)이므로 3:1입니다. 질량과 무관한 속도 변화를 원한다면 bVelChange가 true인 별도 계약을 검토하지만, 원 코드의 false를 읽을 때 그 동작으로 바꾸어 해석하지 않습니다. 원 방사형 힘 호출은 수신자인 MyMesh의 반경 안 바디를 대상으로 하며, 다른 액터를 찾거나 지속 호출하는 로직은 없습니다. 여러 물체에 한 번의 폭발 충격을 주는 요구라면 대상 검색과 방사형 충격량 경로를 따로 설계합니다.
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와 같은 함수들을 사용하여 원하는 물리적 상호작용을 구현할 수 있습니다.
또한 물리 재질을 통해 오브젝트의 표면 특성을 정의하여 더욱 사실적인 물리적 행동을 만들 수 있습니다.