Hitting objects with our projectiles is the easiest sound to quickly set up, but then, we need new functionality on our projectile class to make sure each one knows what sound to play as they hit each type of surface. This can be quite a bit of work to maintain as the number of surface types (physical materials) and projectiles with unique sounds begin to multiply, but this is also the kind of detail-oriented work that sets apart a commercially successful game from the less professional demos or indie titles that don't take the time to add these touches.
We'll start with the absolute basics that we need to address in the project: setting up material types. For the work here, we're just going to set up two types, but of course, you could then follow this pattern and create as many as desired for every type. First, open up the project settings, and under Engine/Physics, we'll add snow and stone like so:
![](assets/980b9d6c-1cfa-4e7a-bc71-71e1f80088cb.png)
In the FirstPerson/Audio section, I added several sound cues, but they're just FX-modified versions of sounds already in the project since, as noted, there are no specific obvious free downloads in the Unreal Marketplace:
![](assets/140c78be-40fb-4b71-bcac-33aa1d051cd9.png)
Next, for projectiles we will need a way of matching material types and a surface that was hit. For now, we'll make a struct in our projectile header. We'll add these two properties:
USTRUCT(BlueprintType) struct FPhysSound { GENERATED_USTRUCT_BODY() UPROPERTY(EditAnywhere, BlueprintReadWrite) TEnumAsByte<EPhysicalSurface> SurfaceType; UPROPERTY(EditAnywhere, BlueprintReadWrite) class USoundCue* SoundCue; };
And, of course, our projectiles will need an array of these:
UPROPERTY(EditAnywhere, BlueprintReadWrite) TArray<FPhysSound> ImpactSounds;
As noted, when the complexity of physics surface types and projectiles gets greater and greater, maintaining a system such as this can get out of hand. And with even the small set here, if decisions made early can mean building in changes as you progress, instead of retro-fitting changes onto a huge number of individual assets, you're always better off making these decisions as early as possible. The level of complexity currently is manageable, but if it were multiplied to a significantly larger number, the work about to be done would be pretty miserable, and the chances for mistakes multiplies. In such a case, I'd recommend perhaps making a DataTable in Unreal and tracking large changes in a .csv spreadsheet or the like. More information on that is provided in the Further reading section. For now, though, let's go to each of our projectiles and begin setting default surfaces to default impact noise, thrown projectiles' snow to thrown-snow impacts, and so on:
![](assets/ef8fe372-77e1-4c50-b5b2-99e6d8a858db.png)
Once all of this is set up, then it's just a matter of a bit of code in the OnHit function for projectiles:
#include "Sound/SoundCue.h"
EPhysicalSurface surfType = SurfaceType_Default; if (OtherComp->GetBodyInstance() != nullptr && OtherComp->GetBodyInstance()->GetSimplePhysicalMaterial() != nullptr) { surfType = OtherComp->GetBodyInstance()->GetSimplePhysicalMaterial()->SurfaceType; } USoundCue* cueToPlay = nullptr; for (auto physSound : ImpactSounds) { if (physSound.SurfaceType == surfType) { cueToPlay = physSound.SoundCue; break; } } const float minVelocity = 400.0f; if (cueToPlay != nullptr && GetVelocity().Size() > minVelocity) { UGameplayStatics::PlaySoundAtLocation(this, cueToPlay, Hit.Location); }
Unfortunately, our FHitResult is for us the projectile in this case. Also note the hardcoded minimum velocity: this is to prevent spamming the game with a huge number of very small bounces towards the end of the projectile's velocity/lifetime, but you could of course handle this in a number of other or more flexible ways. If you are in the future looking to play a sound from that perspective, there is a great accessor to make getting the surface type trivial here:
UGameplayStatics::GetSurfaceType(Hit);
The most obvious facing surface in the default map has been set to a stone type, for testing, and now the different surfaces and different projectiles can be shown, changing their impact sounds! The next section will work briefly in our new (finally visual high quality) map for footfalls and setting reverb based on a sound volume.