UE5 Water System — 물 시스템 구현
UE5의 Water Plugin은 물리 기반 물 시스템으로 강(River), 호수(Lake), 바다(Ocean), 커스텀 WaterBody를 제공합니다. Gerstner Wave 시뮬레이션, 부력, 수중 포스트 프로세스를 내장하며 Nanite Tessellation(UE 5.3+)과 결합해 사실적인 수면을 렌더링합니다.
1. 플러그인 활성화
섹션 제목: “1. 플러그인 활성화”Edit > Plugins > Water → 활성화Edit > Plugins > Water Extras → 활성화 (선택)
프로젝트 재시작 후:Place Actors > Water > WaterBodyOcean / WaterBodyLake / WaterBodyRiver2. WaterBody 배치 및 설정
섹션 제목: “2. WaterBody 배치 및 설정”// WaterBodyOcean 기본 설정 (BP에서도 가능)// 파도 설정// Details > Wave Settings:// - Gerstner Wave: 진폭, 파장, 속도, 방향 설정// - Wave Source: 단일 방향 / 멀티 방향
// 머티리얼 설정// Water Surface Material: M_Ocean (내장)// Underwater Post Process Material: 수중 뷰 색조
// 물리 설정// Collision: Water 채널 활성화// Buoyancy: 자동 부력 지원3. 부력 컴포넌트
섹션 제목: “3. 부력 컴포넌트”#include "BuoyancyComponent.h"
// 배 또는 부유 오브젝트에 부력 추가UCLASS()class ABoat : public APawn{ GENERATED_BODY()
UPROPERTY(VisibleAnywhere) UStaticMeshComponent* HullMesh;
UPROPERTY(VisibleAnywhere) UBuoyancyComponent* BuoyancyComp;
public: ABoat() { HullMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Hull")); HullMesh->SetSimulatePhysics(true);
BuoyancyComp = CreateDefaultSubobject<UBuoyancyComponent>( TEXT("Buoyancy"));
// 부력 폰툰 포인트 설정 (선체의 4개 모서리) FSphericalPontoon Pontoon; Pontoon.Radius = 50.f;
Pontoon.CenterSocket = FName("PontoonFL"); BuoyancyComp->Pontoons.Add(Pontoon); Pontoon.CenterSocket = FName("PontoonFR"); BuoyancyComp->Pontoons.Add(Pontoon); Pontoon.CenterSocket = FName("PontoonRL"); BuoyancyComp->Pontoons.Add(Pontoon); Pontoon.CenterSocket = FName("PontoonRR"); BuoyancyComp->Pontoons.Add(Pontoon); }};4. 수면 높이 쿼리
섹션 제목: “4. 수면 높이 쿼리”#include "WaterBodyActor.h"#include "WaterSubsystem.h"
// 특정 위치의 수면 높이 조회float GetWaterHeightAt(UWorld* World, FVector Location){ UWaterSubsystem* WaterSubsystem = UWaterSubsystem::GetWaterSubsystem(World);
if (!WaterSubsystem) return 0.f;
FWaterBodyQueryResult Result; EWaterBodyQueryFlags Flags = EWaterBodyQueryFlags::ComputeLocation | EWaterBodyQueryFlags::ComputeNormal | EWaterBodyQueryFlags::ComputeDepth;
WaterSubsystem->QueryWaterBodyAtLocation(Location, Flags, Result);
if (Result.IsInWater()) { return Result.GetWaterSurfaceLocation().Z; } return 0.f;}
// 물 안에 있는지 확인bool IsUnderwater(AActor* Actor){ auto* WaterSub = UWaterSubsystem::GetWaterSubsystem( Actor->GetWorld()); if (!WaterSub) return false;
FWaterBodyQueryResult Result; WaterSub->QueryWaterBodyAtLocation( Actor->GetActorLocation(), EWaterBodyQueryFlags::ComputeDepth, Result);
return Result.IsInWater() && Result.GetWaterPlaneDepth() > 0.f;}5. 수중 포스트 프로세스
섹션 제목: “5. 수중 포스트 프로세스”// 수중 진입/탈출 감지 및 포스트 프로세스 적용UCLASS()class AMyCharacter : public ACharacter{ GENERATED_BODY()
UPROPERTY(VisibleAnywhere) UPostProcessComponent* UnderwaterPP;
bool bIsUnderwater = false;
virtual void Tick(float DeltaTime) override { Super::Tick(DeltaTime);
bool bNowUnderwater = CheckUnderwater();
if (bNowUnderwater != bIsUnderwater) { bIsUnderwater = bNowUnderwater; UnderwaterPP->bEnabled = bIsUnderwater;
if (bIsUnderwater) OnEnterWater(); else OnExitWater(); } }
void OnEnterWater() { // 수중 음향 스냅샷 적용 // 이동 속도 감소 GetCharacterMovement()->MaxWalkSpeed *= 0.5f; }
void OnExitWater() { GetCharacterMovement()->MaxWalkSpeed *= 2.f; }};6. 런타임 수위 변경
섹션 제목: “6. 런타임 수위 변경”// 홍수 / 댐 열기 시뮬레이션UCLASS()class AFloodController : public AActor{ GENERATED_BODY()
UPROPERTY(EditInstanceOnly) AWaterBodyLake* TargetLake;
UPROPERTY(EditAnywhere) float FloodRate = 10.f; // cm/s
float _targetHeight;
public: void StartFlood(float TargetWaterHeight) { _targetHeight = TargetWaterHeight; GetWorldTimerManager().SetTimer( _floodTimer, this, &AFloodController::UpdateFloodLevel, 0.1f, true); }
private: FTimerHandle _floodTimer;
void UpdateFloodLevel() { if (!TargetLake) return;
FVector Loc = TargetLake->GetActorLocation(); float currentZ = Loc.Z;
if (FMath::Abs(currentZ - _targetHeight) < 1.f) { GetWorldTimerManager().ClearTimer(_floodTimer); return; }
float newZ = FMath::FInterpTo( currentZ, _targetHeight, 0.1f, FloodRate); TargetLake->SetActorLocation( FVector(Loc.X, Loc.Y, newZ)); }};7. 파도 파라미터 런타임 조정
섹션 제목: “7. 파도 파라미터 런타임 조정”// 날씨 시스템 연동: 폭풍 시 파도 증가void AWeatherSystem::SetStormIntensity(float Intensity){ TArray<AActor*> WaterBodies; UGameplayStatics::GetAllActorsOfClass( GetWorld(), AWaterBody::StaticClass(), WaterBodies);
for (AActor* Actor : WaterBodies) { if (auto* Ocean = Cast<AWaterBodyOcean>(Actor)) { // 파도 진폭 조정 (머티리얼 파라미터) Ocean->GetWaterMeshComponent() ->SetScalarParameterValueOnMaterials( "WaveAmplitude", FMath::Lerp(0.3f, 2.0f, Intensity)); } }}UE5 Water System은 WaterBody 액터 배치만으로 물리 기반 물을 즉시 얻을 수 있습니다. 부유 오브젝트는 UBuoyancyComponent에 폰툰 포인트를 설정하고, 수면 높이는 UWaterSubsystem::QueryWaterBodyAtLocation으로 정확하게 쿼리하세요. 수중 포스트 프로세스와 음향 스냅샷을 수위 체크와 연동하면 완성도 높은 수중 경험을 구현할 수 있습니다.