Why?
With all the assets that make up a game, it’s easy to forget a setting or two sometimes. One of our most common mistakes is forgetting to set up Sound Attenuation for a given sound wave / cue. If you forget to assign attenuation settings for a sound, it’ll always play at full volume regardless of where it is in the world in relation to a player.
In a multipplayer game like ours, it can be even easier to miss because if the sounds are made by the player, you don’t notice that they’re not being attenuated. But as soon as you get multiple players in a level, you realise that you can hear the other player’s noises at full volume even when you’re not next to them. It’s something we constantly had to fix after adding new sounds, because every Sound Wave or Sound Cue has to have this setting.
Let’s Automate That Check
Instead of relying on spotting these errors, let’s make it so that we can bulk-validate our assets, and even stop them being saved unless they conform to some rules. This is referred to as Asset Validation, and there are a few resources for it already:
So, why am I writing a blog post as well? Well, it’s because…Blueprints just can’t do everything. 😕
Let’s Get Our Hands Dirty
The trouble with all the resources on validation is that they only describe doing it in Blueprints. And guess what? Attenuation settings aren’t exposed to Blueprints. Why? Who knows. But that’s the thing with Unreal, most of the tutorials available are Blueprint-heavy, but Blueprints just don’t have access to everything.
So, we’re going to have to do this in C++. If what you need to do can be done in Blueprints then you don’t have to read the rest of this post, just use the resources linked above instead. But if you need to wade into the C++ mud too, grab your wellies and follow me. 😄
Step 1: Add an Editor Module
I’m assuming you have a C++ project already, but you might not have an Editor Module yet. We need one of those to contain our editor-only validator class. Luckily there’s a tutorial for creating an editor module on the wiki.
Step 2: Add your Validator Class
Add a new C++ class in your Editor module, derived from UEditorValidatorBase:
#pragma once
#include "CoreMinimal.h"
#include "EditorValidatorBase.h"
#include "SoundAssetValidator.generated.h"
/**
* Our sound validator
*/
UCLASS()
class USoundAssetValidator : public UEditorValidatorBase
{
GENERATED_BODY()
public:
virtual bool CanValidateAsset_Implementation(const FAssetData& InAssetData,
UObject* InObject,
FDataValidationContext& InContext) const override;
virtual EDataValidationResult ValidateLoadedAsset_Implementation(const FAssetData& InAssetData,
UObject* InAsset,
FDataValidationContext& Context) override;
};
As you can see we’ve overridden 2 methods, CanValidateAsset_Implementation and ValidateLoadedAsset_Implementation.
These are the same ones you override in Blueprints, just with the _Implementation suffix because they’re BlueprintNativeEvent.
The source for these two methods is fairly easy. For “Can Validate Asset”, we just need to check the type:
#include "SoundAssetValidator.h"
#include "Sound/SoundCue.h"
#include "Sound/SoundWave.h"
bool USoundAssetValidator::CanValidateAsset_Implementation(const FAssetData& InAssetData,
UObject* InObject,
FDataValidationContext& InContext) const
{
// Only check SoundCue and SoundWave
UClass* AssetClass = InAssetData.GetClass();
return AssetClass == USoundWave::StaticClass() ||
AssetClass == USoundCue::StaticClass();
}
We could check all subclasses of
USoundBaseif we wanted, but I’m limiting it to the two I use right now.
Next, let’s implement the actual validation:
EDataValidationResult USoundAssetValidator::ValidateLoadedAsset_Implementation(
const FAssetData& InAssetData,
UObject* InAsset,
FDataValidationContext& Context)
{
if (auto S = Cast<USoundBase>(InAsset))
{
if (!S->AttenuationSettings)
{
AssetFails(InAsset, INVTEXT("Missing Attenuation"));
return EDataValidationResult::Invalid;
}
}
AssetPasses(InAsset);
return EDataValidationResult::Valid;
}
A validator has to both return a result of valid/invalid, and call the AssetFails or AssetPasses function as well.
I’m not sure why.
In my case I’m pulling out the AttenuationSettings from the asset, which is only available to C++, and if it’s null,
failing the validation, so we can never leave the attenuation settings blank. For global sounds which shouldn’t be
distance attenuated (like UI sounds, or music), we have a “global sound” attenuation asset which doesn’t do distance or
spatial attenuation, so that we can always set this property to something. An alternative would be to allow it to be
blank in certain asset folders which should only have global sounds in it, but that’s more error prone.
That’s it! Just the existence of this class will cause it to be used, so you’re good to go.
Validating Assets
Option 1: Dynamic Validate on Save
As soon as you add your validator, whenever you save that type of asset it will be validated automatically. This means you’re alerted to the problems immediately - although you will need to bulk validate everything you’ve saved previously at least once (see below).
If for some reason you want to disable validation on save, go to Editor Preferences > Advanced > Data Validation, and uncheck “Validate on Save”.
Option 2: Bulk Validate in the Editor
The easiest bulk validation method, and the one I use, is to right-click a content folder in the editor, and select “Validate Assets in Folder”. The results appear in the Message Log under Asset Checks, and you can work your way through the errors, fixing the problems you’ve checked for.
Warning: Saving an asset clears the Asset Checks log
Be aware that saving any validated asset will clear the “Asset Checks” section of the Message Log, resetting it just to the result of validating what you saved. So if you’re working through a list of errors from “Validate Assets In Folder”, be aware that as you fix things, try avoiding saving until the end, or you’ll have to run the folder validate again.
Option 3: Command line check
You can also run the validation outside the editor:
UnrealEditor-Cmd.exe YourProjectName.uproject -run=DataValidation
That’s it!
You can take off your wellies now, we’re done. It wasn’t that bad, right? I hope this helps someone out, asset validation is a very useful feature, but Blueprints are a bit limited sometimes.