일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 |
- 언리얼
- Random Map Generator
- Unreal Engine 4
- splinemeshcomponent scale
- ue4 error
- ue4 Crash
- LittleNightMare
- 13iew
- register component
- unreal engine redirection crash
- unreal engine
- 리디렉션 크래쉬
- unreal engine skill
- change textblock color
- Unreal Engine Error
- UnrealEngine
- redirection crash
- staticmesh mobility
- skill system
- UE
- tscriptinterface
- Ai
- Unreal Engine 5
- UE4
- UE5
- unity
- deltaTime
- redirector crash
- 랜덤 맵 생성
- 리디렉터 크래쉬
- Today
- Total
Class GameDev* SheepAdult
[Unreal Engine 4] OnComponentHit Sound C++ (Physics Collision Sound) 본문
[Unreal Engine 4] OnComponentHit Sound C++ (Physics Collision Sound)
SheepAdult 2022. 12. 5. 03:31(2022-12-27 수정)
물건이 떨어져서 바닥에 닿았을 때 소리가 나는 경우를 구현하고자 할 때, OnComponentHit을 사용해서 구현해야 한다는 생각이 자연스럽게 들 것이다. 하지만, OnComponentHit은 우리가 생각했던 것보다 더 많은 빈도수로 호출된다. 만약 어떠한 처리도 하지 않고 물건을 떨어뜨리면 바닥에 한 번만 닿아도 수십 번의 소리가 재생되어 원치 않던 소리가 나므로 적절한 처리를 해주어야 한다. 본문에서 사용할 처리 방법은 OnComponentHit의 매개변수인 NormalImpulse에 임계점을 주어 강한 충돌만 다룰 것이다.
여러 오브젝트에 이식할 수 있도록 컴포넌트로 만들었다. 아래는 PhysicsCollisionSoundComponent.cpp 이다.
// PhysicsCollisionSoundComponent.cpp
void UPhysicsCollisionSoundComponent::BeginPlay()
{
Super::BeginPlay();
auto* PrimitiveComponent = Cast<UPrimitiveComponent>(GetOwner()->GetRootComponent());
if (PrimitiveComponent)
{
PrimitiveComponent->SetNotifyRigidBodyCollision(true);
PrimitiveComponent->OnComponentHit.AddDynamic(this, &UPhysicsCollisionSoundComponent::OnHit);
}
}
SetNotifyRigidBodyCollision는 충돌 시 이벤트를 전달하는 역할로, 블루프린트에서 Simulation Generates Hit Events이다. 해당 옵션을 켜야 바인딩된 함수를 호출할 수 있다.
아래는 OnComponentHit에 바인딩된 함수이다. 기본적인 뼈대는 아래와 같다.
void UPhysicsCollisionSoundComponent::OnHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)
{
...
if (NormalImpulse.Size() > ImpulseThreshold * 10000)
{
UGameplayStatics::PlaySoundAtLocation(GetWorld(), ThrowableSound, Hit.Location);
}
...
}
ImpulseThreshold는 float으로 충돌 크기에 제한을 주기 위한 변수이다. 값이 크면 클 수록 소리 재생의 빈도 수는 적어진다. 여러 요인에 따라 다르겠지만 본인은 해당 값이 10000을 곱한 것을 포함하여 50000 ~ 100000 정도가 되어야 자연스러운 느낌을 주었다.
충돌 크기가 Threshold 값보다 커야지만 소리가 나므로 한 번 바닥에 충돌하고 그다음 작은 충돌들은 무시되는 원리이다. 만약 닿는 바닥이나 물체 자체의 Physics Material에 따라 소리를 다르게 하고 싶다면 매개 변수를 적절히 가져와 코드를 짜면 될 것이다.
::2022-12-27 수정::
좀 더 관리하기 편한 방향으로 수정했다. Owner Actor의 Physical Material에 따라 소리를 다르게 했으며 충돌 세기에 비례하여 Volume도 다르게 설정하여 재생하도록 했다. 해당 cpp파일에 FObjectFinder로 Physical Material들을 배열로 저장한 후 하나하나 Owner의 Physical Material과 비교하여 맞는 Index를 Cue의 switch에 대입하여 이에 대응하는 소리를 재생하게 했다. Owner Actor가 될 cpp파일에 Audio 컴포넌트와 PhysicsCollisionSound 컴포넌트만 추가하면 작동한다.
* 해당 cpp파일에서 owner에 audio컴포넌트를 추가까지하여 해당 컴포넌트만 추가하면 되도록 하려했으나 알 수 없는 이유로 소리가 재생되지 않았다. Audio 컴포너트와 PhysicsCollisionSound 컴포넌트도 Owner Actor에 잘 추가 되었고 cue도 잘 대입 되었지만 소리만 나지 않아 후추에 좀 더 손 볼 예정이다.
// PhysicsCollisionSoundComponent.cpp
void UPhysicsCollisionSoundComponent::BeginPlay()
{
Super::BeginPlay();
SetAudioComponent();
SetPrimitiveComponent();
SetPhysicsMateialIndex();
}
// 부모 오브젝트에서 AudioComponent를 불러와 맞는 Cue를 대입합니다.
void UPhysicsCollisionSoundComponent::SetAudioComponent()
{
AudioComponent = GetOwner()->FindComponentByClass<UAudioComponent>();
auto* SoundManager = Cast<ASoundManager>(UGameplayStatics::GetActorOfClass(GetOwner()->GetWorld(), ASoundManager::StaticClass()));
if (SoundManager && AudioComponent)
{
AudioComponent->SetSound(SoundManager->PhysicsCollisionSound);
}
}
void UPhysicsCollisionSoundComponent::SetPrimitiveComponent()
{
PrimitiveComponent = Cast<UPrimitiveComponent>(GetOwner()->GetComponentByClass(UStaticMeshComponent::StaticClass()));
if (PrimitiveComponent)
{
PrimitiveComponent->SetNotifyRigidBodyCollision(true);
PrimitiveComponent->OnComponentHit.AddDynamic(this, &UPhysicsCollisionSoundComponent::OnHit);
}
}
// 저장된 Physical Material배열과 현재 Onwer오브젝트의 Physical Material을 비교하여 맞는
// Index의 Sound를 Parameter을 통해 수정합니다.
void UPhysicsCollisionSoundComponent::SetPhysicsMateialIndex()
{
for (int i = 0; i < PhysicalMaterials.Num(); i++)
{
if (PrimitiveComponent->GetMaterial(0)->GetPhysicalMaterial() == PhysicalMaterials[i])
{
AudioComponent->SetIntParameter(FName("PhysicsCollisionParam"), i);
break;
}
}
}
void UPhysicsCollisionSoundComponent::OnHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)
{
...
// 만약 특정 소리가 일정 임계값 이상이면 재생합니다.
// ImpulseThreshold 는 10, VolumeSensitivity 는 50 이 기본값입니다.
if (NormalImpulse.Size() > ImpulseThreshold * 10000)
{
float Volume = UKismetMathLibrary::FMin(NormalImpulse.Size() / (VolumeSensitivity * 10000), 1.f);
// 충동 세기에 비례하여 Volume 조절
AudioComponent->VolumeMultiplier = Volume;
AudioComponent->Play();
}
}
실행 결과:
https://www.youtube.com/watch?v=Ohqj-1psTSk
:: 2023-01-12 추가
위의 영상과는 달리 소리의 음량이 충돌 세기에 비례하여 조절되며 physics material에 따라 소리를 다르게 했다. 소리는 임의로 넣었다.