[TIL] 251211

๊น€์„ธํฌยท2025๋…„ 12์›” 11์ผ

โœ๏ธToday I Learned

๐Ÿ“… 2025-12-11

  • Physical Material์„ ํ™œ์šฉํ•œ ์žฌ์งˆ๋ณ„ ๋ฐœ์†Œ๋ฆฌ ์‹œ์Šคํ…œ ๊ตฌํ˜„
  • AnimNotify ๊ธฐ๋ฐ˜ ์‚ฌ์šด๋“œ ํŠธ๋ฆฌ๊ฑฐ๋ง
  • ๋ฉ€ํ‹ฐํ”Œ๋ ˆ์ด์–ด ํ™˜๊ฒฝ์—์„œ์˜ ๋กœ์ปฌ ์‚ฌ์šด๋“œ ์žฌ์ƒ

Physical Material์„ ํ™œ์šฉํ•œ ์žฌ์งˆ๋ณ„ ๋ฐœ์†Œ๋ฆฌ ์‹œ์Šคํ…œ ๊ตฌํ˜„

๐ŸŽฏ ๊ตฌํ˜„ ๋ชฉํ‘œ

๋ฐ”๋‹ฅ ์žฌ์งˆ(์ฝ˜ํฌ๋ฆฌํŠธ, ๋‚˜๋ฌด, ๊ธˆ์† ๋“ฑ)์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ๋ฐœ์†Œ๋ฆฌ๋ฅผ ์žฌ์ƒํ•˜๋Š” ์‹œ์Šคํ…œ์„ ๊ตฌํ˜„. ๊ฑท๊ธฐ/๋‹ฌ๋ฆฌ๊ธฐ/์ฐฉ์ง€/์›…ํฌ๋ฆฌ๊ธฐ/๋ฒฝํƒ€๊ธฐ ๊ฐ๊ฐ์— ๋Œ€ํ•ด ์žฌ์งˆ๋ณ„ ์‚ฌ์šด๋“œ ์ง€์›.

๐Ÿ“ ์‹œ์Šคํ…œ ๊ตฌ์กฐ

1. FootstepComponent ์„ค๊ณ„

// ์žฌ์งˆ๋ณ„ ์‚ฌ์šด๋“œ๋ฅผ ๋‹ด๋Š” ๊ตฌ์กฐ์ฒด
USTRUCT()
struct FFootstepSound
{
    GENERATED_BODY()
    TObjectPtr<USoundBase> Left;      // ์™ผ๋ฐœ
    TObjectPtr<USoundBase> Right;     // ์˜ค๋ฅธ๋ฐœ  
    TObjectPtr<USoundBase> Climbing;  // ๋ฒฝํƒ€๊ธฐ
};

// Physical Surface Type๋ณ„๋กœ ์‚ฌ์šด๋“œ ๋งคํ•‘
TMap<TEnumAsByte<EPhysicalSurface>, FFootstepSound> FootstepSounds;

์„ค๊ณ„ ์˜๋„:

  • TMap์„ ์‚ฌ์šฉํ•ด Surface Type์„ ํ‚ค๋กœ ์‚ฌ์šด๋“œ ๊ฒ€์ƒ‰ โ†’ O(1) ์„ฑ๋Šฅ
  • ๊ตฌ์กฐ์ฒด๋กœ ๋ฌถ์–ด ์ขŒ/์šฐ/๋“ฑ๋ฐ˜ ์‚ฌ์šด๋“œ๋ฅผ ํ•˜๋‚˜๋กœ ๊ด€๋ฆฌ
  • TObjectPtr๋กœ ์•ˆ์ „ํ•œ ํฌ์ธํ„ฐ ๊ด€๋ฆฌ

2. ์žฌ์งˆ ๊ฐ์ง€: LineTrace + Physical Material

void PlayFootstepSound(const FVector& Location, bool& IsLeft)
{
    FCollisionQueryParams Params;
    Params.bReturnPhysicalMaterial = true; // โญ ํ•ต์‹ฌ!
    
    GetWorld()->LineTraceSingleByChannel(HitResult, Start, End, ECC_Visibility, Params);
    
    EPhysicalSurface SurfaceType = EPhysicalSurface::SurfaceType_Default;
    if (HitResult.PhysMaterial.IsValid())
    {
        SurfaceType = HitResult.PhysMaterial->SurfaceType;
    }
    
    // TMap์—์„œ ์‚ฌ์šด๋“œ ์ฐพ๊ธฐ
    if (USoundBase* Sound = IsLeft 
        ? FootstepSounds.FindRef(SurfaceType).Left 
        : FootstepSounds.FindRef(SurfaceType).Right)
    {
        UGameplayStatics::PlaySoundAtLocation(GetWorld(), Sound, Location);
    }
}

ํ•ต์‹ฌ ํฌ์ธํŠธ:

  • bReturnPhysicalMaterial = true ์—†์œผ๋ฉด PhysMaterial ์ •๋ณด๋ฅผ ๋ฐ›์„ ์ˆ˜ ์—†์Œ
  • FindRef๋Š” ํ‚ค๊ฐ€ ์—†์–ด๋„ ์•ˆ์ „ (๊ธฐ๋ณธ๊ฐ’ ๋ฐ˜ํ™˜)
  • Physical Material ๋ฏธํ• ๋‹น ์‹œ โ†’ SurfaceType_Default ๋ฐ˜ํ™˜

๐ŸŽฌ AnimNotify ๊ธฐ๋ฐ˜ ํŠธ๋ฆฌ๊ฑฐ๋ง

AnimNotify ๊ตฌํ˜„

// AN_Footstep.h
UCLASS()
class UAN_Footstep : public UAnimNotify
{
    GENERATED_BODY()
public:
    UPROPERTY(EditAnywhere)
    FName SocketName = TEXT("foot_l");
    
    UPROPERTY(EditAnywhere)
    bool IsLeft = true;
    
    virtual void Notify(USkeletalMeshComponent* MeshComp, 
                       UAnimSequenceBase* Animation, 
                       const FAnimNotifyEventReference& EventReference) override;
};

์ฃผ์˜์‚ฌํ•ญ:

  • Super::Notify() ํ˜ธ์ถœ ์‹œ Blueprint ์ด๋ฒคํŠธ์™€ ์ถฉ๋Œ ๊ฐ€๋Šฅ โ†’ ํ˜ธ์ถœํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์„ ๊ถŒ์žฅ
  • Received_Notify๋Š” BlueprintImplementableEvent โ†’ C++์—์„œ override ๋ถˆ๊ฐ€
  • UE 5.0+ ์ดํ›„ ์‹œ๊ทธ๋‹ˆ์ฒ˜์— FAnimNotifyEventReference ํŒŒ๋ผ๋ฏธํ„ฐ ์ถ”๊ฐ€๋จ

๐ŸŽฏ ํŠน์ˆ˜ ์ผ€์ด์Šค ์ฒ˜๋ฆฌ

1. ์ฐฉ์ง€ ์†Œ๋ฆฌ

// TSCharacter.cpp
void ATSCharacter::Landed(const FHitResult& Hit)
{
    Super::Landed(Hit);
    FootstepComponent->PlayFootstepSoundFromHit(Hit);
}

๋ฌธ์ œ: Landed()์˜ Hit๋Š” PhysMaterial ์ •๋ณด๊ฐ€ ์—†์Œ

  • CharacterMovementComponent์˜ ๋‚ด๋ถ€ Sweep์ด Physical Material์„ ์š”์ฒญํ•˜์ง€ ์•Š์Œ

ํ•ด๊ฒฐ: Hit ์œ„์น˜์—์„œ ๋‹ค์‹œ ์งง์€ LineTrace ์ˆ˜ํ–‰

void PlayFootstepSoundFromHit(const FHitResult& Hit)
{
    // Hit ์œ„์น˜์—์„œ ์žฌ์งˆ ๊ฐ์ง€๋ฅผ ์œ„ํ•œ ์žฌ LineTrace
    FVector Start = Hit.ImpactPoint;
    FVector End = Hit.ImpactPoint - FVector(0, 0, 50);
    
    FCollisionQueryParams Params;
    Params.bReturnPhysicalMaterial = true; // ํ•„์ˆ˜!
    
    GetWorld()->LineTraceSingleByChannel(...);
}

2. ๋ฒฝํƒ€๊ธฐ ์†Œ๋ฆฌ

void PlayClimbingSound(const FVector& Location)
{
    // ์บ๋ฆญํ„ฐ ์•ž ๋ฐฉํ–ฅ์œผ๋กœ LineTrace
    FVector Forward = GetOwner()->GetActorForwardVector(); // ์ •๊ทœํ™”๋จ (ํฌ๊ธฐ=1)
    FVector End = Location + Forward * 100.f;
    
    // ๋ฒฝ ์žฌ์งˆ ๊ฐ์ง€ ํ›„ Climbing ์‚ฌ์šด๋“œ ์žฌ์ƒ
}

ํ•™์Šต:

  • GetActorForwardVector()๋Š” ํ•ญ์ƒ ์ •๊ทœํ™”๋œ ๋ฒกํ„ฐ ๋ฐ˜ํ™˜ (ํฌ๊ธฐ = 1)
  • ๋ฒกํ„ฐ ์—ฐ์‚ฐ ํ›„ ํฌ๊ธฐ ๋ณ€๊ฒฝ ์‹œ GetSafeNormal() ์žฌ์ •๊ทœํ™” ํ•„์š”

๐ŸŒ ๋ฉ€ํ‹ฐํ”Œ๋ ˆ์ด์–ด ๊ณ ๋ ค์‚ฌํ•ญ

๋ณต์ œ ์›๋ฆฌ

์• ๋‹ˆ๋ฉ”์ด์…˜ ๋ณต์ œ๋จ (Character Movement ์ž๋™)
  โ†“
๋ชจ๋“  ํด๋ผ์ด์–ธํŠธ์—์„œ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์žฌ์ƒ
  โ†“
๊ฐ ํด๋ผ์ด์–ธํŠธ์—์„œ AnimNotify ์‹คํ–‰
  โ†“
๊ฐ ํด๋ผ์ด์–ธํŠธ์—์„œ ๋กœ์ปฌ ์‚ฌ์šด๋“œ ์žฌ์ƒ (RPC ๋ถˆํ•„์š”!)

ํ•ต์‹ฌ:

  • RPC๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œ โ†’ ๋„คํŠธ์›Œํฌ ๋ถ€ํ•˜ ์—†์Œ
  • ์• ๋‹ˆ๋ฉ”์ด์…˜์ด ๋ณต์ œ๋˜๋ฏ€๋กœ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋™๊ธฐํ™”
  • "์• ๋‹ˆ๋ฉ”์ด์…˜์ด ๋ณด์ด๋ฉด ์†Œ๋ฆฌ๋„ ๋“ค๋ฆฐ๋‹ค" ์›์น™ ์„ฑ๋ฆฝ

์ฃผ์˜:

// โŒ ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์•ˆ ๋จ
if (Owner->IsLocallyControlled()) 
{
    PlaySound(); // ์ž๊ธฐ ์บ๋ฆญํ„ฐ๋งŒ ์†Œ๋ฆฌ ๋‚จ
}

// โœ… ์กฐ๊ฑด ์—†์ด ๋ชจ๋“  ํด๋ผ์ด์–ธํŠธ์—์„œ ์žฌ์ƒ
PlaySound();

๐Ÿ› ๏ธ ์ตœ์ ํ™” & ์•ˆ์ •์„ฑ

1. GC ๋ฐฉ์ง€

void BeginPlay()
{
    // ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ์‚ฌ์šด๋“œ๋ฅผ GC๋กœ๋ถ€ํ„ฐ ๋ณดํ˜ธ
    for (const auto& Pair : FootstepSounds)
    {
        if (Pair.Value.Left) Pair.Value.Left->AddToRoot();
        if (Pair.Value.Right) Pair.Value.Right->AddToRoot();
        if (Pair.Value.Climbing) Pair.Value.Climbing->AddToRoot();
    }
}

2. Build.cs ์„ค์ •

PublicDependencyModuleNames.AddRange(new string[] 
{ 
    "PhysicsCore"  // Physical Material ์‚ฌ์šฉ ์‹œ ํ•„์ˆ˜!
});
  • ๋ˆ„๋ฝ ์‹œ ๋งํฌ ์—๋Ÿฌ: Z_Construct_UEnum_PhysicsCore_EPhysicalSurface

3. Project Settings

  • Physics > Physical Surface: SurfaceType1~3 ์ •์˜
  • Physics > Default Physical Material: ๋ฏธํ• ๋‹น ์žฌ์งˆ์˜ ๊ธฐ๋ณธ๊ฐ’ ์„ค์ •

๐Ÿ’ก ๋А๋‚€ ์  (What I Felt)

Physical Material ์‹œ์Šคํ…œ
์—”์ง„ ํ‘œ์ค€ ๊ธฐ๋Šฅ์„ ํ™œ์šฉํ•˜๋‹ˆ ํŒŒํ‹ฐํด, ๋ฐ์นผ ๋“ฑ ๋‹ค๋ฅธ ์‹œ์Šคํ…œ๊ณผ๋„ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์—ฐ๋™๋˜๋Š” ๊ฒƒ์ด ์ธ์ƒ์ ์ด์—ˆ๋‹ค. ๋‹จ์ˆœํžˆ ๋ฐœ์†Œ๋ฆฌ๋งŒ์ด ์•„๋‹ˆ๋ผ ๋ฐœ์ž๊ตญ ํŒŒํ‹ฐํด, ์žฌ์งˆ๋ณ„ ๋งˆ์ฐฐ์Œ ๋“ฑ์œผ๋กœ ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ์„ค๊ณ„๋ผ๋Š” ์ ์ด ์ข‹์•˜๋‹ค.

๋ฉ€ํ‹ฐํ”Œ๋ ˆ์ด์–ด ์‚ฌ์šด๋“œ ๋ณต์ œ
RPC ์—†์ด๋„ ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋ณต์ œ๋งŒ์œผ๋กœ ์‚ฌ์šด๋“œ๊ฐ€ ๋™๊ธฐํ™”๋˜๋Š” ๊ตฌ์กฐ๊ฐ€ ๋งค์šฐ ํŽธ๋ฆฌํ–ˆ๋‹ค. ๊ฐ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋กœ์ปฌ์—์„œ ์žฌ์ƒํ•˜๋ฏ€๋กœ ๋„คํŠธ์›Œํฌ ๋ถ€ํ•˜๋„ ์—†๊ณ , ํƒ€์ด๋ฐ๋„ ์ •ํ™•ํ•˜๋‹ค. Listen Server ๊ตฌ์กฐ์—์„œ ํŠนํžˆ ํšจ์œจ์ ์ด๋‹ค.

Landed()์˜ PhysMaterial ๋ฏธ์ œ๊ณต ์ด์Šˆ
CharacterMovementComponent์˜ ๋‚ด๋ถ€ ๋กœ์ง์ด Physical Material์„ ์š”์ฒญํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์ ์ด ๊นŒ๋‹ค๋กœ์› ๋‹ค. Hit ์œ„์น˜์—์„œ ์žฌ LineTrace๋ฅผ ํ•ด์•ผ ํ•œ๋‹ค๋Š” ํ•ด๊ฒฐ์ฑ…์„ ์ฐพ๊ธฐ๊นŒ์ง€ ์‹œ๊ฐ„์ด ๊ฑธ๋ ธ๋‹ค. ์—”์ง„์˜ ๋‚ด๋ถ€ ๋™์ž‘์„ ์™„์ „ํžˆ ์ดํ•ดํ•˜๋Š” ๊ฒƒ์ด ์–ผ๋งˆ๋‚˜ ์ค‘์š”ํ•œ์ง€ ๋‹ค์‹œ ๊นจ๋‹ฌ์•˜๋‹ค.

0๊ฐœ์˜ ๋Œ“๊ธ€