๐
2025-12-11
๋ฐ๋ฅ ์ฌ์ง(์ฝํฌ๋ฆฌํธ, ๋๋ฌด, ๊ธ์ ๋ฑ)์ ๋ฐ๋ผ ๋ค๋ฅธ ๋ฐ์๋ฆฌ๋ฅผ ์ฌ์ํ๋ ์์คํ ์ ๊ตฌํ. ๊ฑท๊ธฐ/๋ฌ๋ฆฌ๊ธฐ/์ฐฉ์ง/์ ํฌ๋ฆฌ๊ธฐ/๋ฒฝํ๊ธฐ ๊ฐ๊ฐ์ ๋ํด ์ฌ์ง๋ณ ์ฌ์ด๋ ์ง์.
// ์ฌ์ง๋ณ ์ฌ์ด๋๋ฅผ ๋ด๋ ๊ตฌ์กฐ์ฒด
USTRUCT()
struct FFootstepSound
{
GENERATED_BODY()
TObjectPtr<USoundBase> Left; // ์ผ๋ฐ
TObjectPtr<USoundBase> Right; // ์ค๋ฅธ๋ฐ
TObjectPtr<USoundBase> Climbing; // ๋ฒฝํ๊ธฐ
};
// Physical Surface Type๋ณ๋ก ์ฌ์ด๋ ๋งคํ
TMap<TEnumAsByte<EPhysicalSurface>, FFootstepSound> FootstepSounds;
์ค๊ณ ์๋:
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๋ ํค๊ฐ ์์ด๋ ์์ (๊ธฐ๋ณธ๊ฐ ๋ฐํ)SurfaceType_Default ๋ฐํ// 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 ๋ถ๊ฐFAnimNotifyEventReference ํ๋ผ๋ฏธํฐ ์ถ๊ฐ๋จ// TSCharacter.cpp
void ATSCharacter::Landed(const FHitResult& Hit)
{
Super::Landed(Hit);
FootstepComponent->PlayFootstepSoundFromHit(Hit);
}
๋ฌธ์ : Landed()์ Hit๋ PhysMaterial ์ ๋ณด๊ฐ ์์
ํด๊ฒฐ: 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(...);
}
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 ๋ถํ์!)
ํต์ฌ:
์ฃผ์:
// โ ์ด๋ ๊ฒ ํ๋ฉด ์ ๋จ
if (Owner->IsLocallyControlled())
{
PlaySound(); // ์๊ธฐ ์บ๋ฆญํฐ๋ง ์๋ฆฌ ๋จ
}
// โ
์กฐ๊ฑด ์์ด ๋ชจ๋ ํด๋ผ์ด์ธํธ์์ ์ฌ์
PlaySound();
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();
}
}
PublicDependencyModuleNames.AddRange(new string[]
{
"PhysicsCore" // Physical Material ์ฌ์ฉ ์ ํ์!
});
Z_Construct_UEnum_PhysicsCore_EPhysicalSurfacePhysical Material ์์คํ
์์ง ํ์ค ๊ธฐ๋ฅ์ ํ์ฉํ๋ ํํฐํด, ๋ฐ์นผ ๋ฑ ๋ค๋ฅธ ์์คํ
๊ณผ๋ ์์ฐ์ค๋ฝ๊ฒ ์ฐ๋๋๋ ๊ฒ์ด ์ธ์์ ์ด์๋ค. ๋จ์ํ ๋ฐ์๋ฆฌ๋ง์ด ์๋๋ผ ๋ฐ์๊ตญ ํํฐํด, ์ฌ์ง๋ณ ๋ง์ฐฐ์ ๋ฑ์ผ๋ก ํ์ฅ ๊ฐ๋ฅํ ์ค๊ณ๋ผ๋ ์ ์ด ์ข์๋ค.
๋ฉํฐํ๋ ์ด์ด ์ฌ์ด๋ ๋ณต์
RPC ์์ด๋ ์ ๋๋ฉ์ด์
๋ณต์ ๋ง์ผ๋ก ์ฌ์ด๋๊ฐ ๋๊ธฐํ๋๋ ๊ตฌ์กฐ๊ฐ ๋งค์ฐ ํธ๋ฆฌํ๋ค. ๊ฐ ํด๋ผ์ด์ธํธ๊ฐ ๋ก์ปฌ์์ ์ฌ์ํ๋ฏ๋ก ๋คํธ์ํฌ ๋ถํ๋ ์๊ณ , ํ์ด๋ฐ๋ ์ ํํ๋ค. Listen Server ๊ตฌ์กฐ์์ ํนํ ํจ์จ์ ์ด๋ค.
Landed()์ PhysMaterial ๋ฏธ์ ๊ณต ์ด์
CharacterMovementComponent์ ๋ด๋ถ ๋ก์ง์ด Physical Material์ ์์ฒญํ์ง ์๋๋ค๋ ์ ์ด ๊น๋ค๋ก์ ๋ค. Hit ์์น์์ ์ฌ LineTrace๋ฅผ ํด์ผ ํ๋ค๋ ํด๊ฒฐ์ฑ
์ ์ฐพ๊ธฐ๊น์ง ์๊ฐ์ด ๊ฑธ๋ ธ๋ค. ์์ง์ ๋ด๋ถ ๋์์ ์์ ํ ์ดํดํ๋ ๊ฒ์ด ์ผ๋ง๋ ์ค์ํ์ง ๋ค์ ๊นจ๋ฌ์๋ค.