Adding Custom Map Checks in UE4
⚠️ This post was last updated in 2018, meaning its contents may be outdated.
The map check function in UE4 offers level designers an insight into things that are going wrong inside their levels - for example a static mesh actor with no mesh.
You can also harness this power in your own project to run anything in the game world through a custom set of rules specific to your content.
For example, in Estranged, all surfaces in all levels need to have a physical material assigned to them.
This assignment has various functions, but most importantly dictates the footstep sound when the player walks on the material, and the bullet decal and particle effect to spawn when the surface is shot.
Basic Syntax
To get this working, all that is required is to override the CheckForErrors()
method in a custom actor. A very simple header looks like this:
UCLASS()
class ESTCORE_API AMyActor : public AActor
{
GENERATED_BODY()
UPROPERTY(Category = "Bad Things", EditAnywhere, BlueprintReadWrite)
bool bBadThing;
#if WITH_EDITOR
virtual void CheckForErrors() override;
#endif
};
An example implementation checking the state of that boolean property might look like this:
#include "Logging/TokenizedMessage.h"
#include "Logging/MessageLog.h"
#include "Misc/UObjectToken.h"
#include "Misc/MapErrors.h"
#include "MyActor.h"
#if WITH_EDITOR
void AMyActor::CheckForErrors()
{
Super::CheckForErrors();
FMessageLog MapCheck("MapCheck");
if (bBadThing)
{
MapCheck.Warning()
->AddToken(FUObjectToken::Create(this))
->AddToken(FTextToken::Create(FText::FromString("has a bad thing!")));
}
}
#endif
The output when running the map check in the editor looks like this:
Complex Example
As I said above, Estranged needs a physical material assigned to each surface in the game to ensure things like footsteps and bullet impacts work properly. To achieve that, I use an empty actor which contains only a CheckForErrors()
override as above.
This method is responsible for looping all actors in the world, and checking their materials for a physical material. This can be easily extended to check many other things, and this actor is dropped into every level in Estranged to ensure consistency across the entire game.
#if WITH_EDITOR
void AEstMapErrorChecker::CheckForErrors()
{
Super::CheckForErrors();
FMessageLog MapCheck("MapCheck");
TSet<const UMaterialInterface*> SeenMaterials;
for (TActorIterator<AActor> ActorItr(GetWorld()); ActorItr; ++ActorItr)
{
const AActor *Actor = *ActorItr;
if (Actor->IsEditorOnly())
{
// Ignore editor only actors
continue;
}
for (const UActorComponent* Component : Actor->GetComponents())
{
const UPrimitiveComponent* SceneComponent = Cast<UPrimitiveComponent>(Component);
if (SceneComponent == nullptr)
{
// If there isn't a primitive component at this actor's root, ignore it
continue;
}
if (SceneComponent->IsEditorOnly())
{
// Ensure the scene component is a game component
continue;
}
if (!SceneComponent->GetCollisionEnabled())
{
// Physical materials not important for non-simulated components
continue;
}
int32 NumMaterials = SceneComponent->GetNumMaterials();
for (int32 i = 0; i < NumMaterials; i++)
{
const UMaterialInterface* Material = SceneComponent->GetMaterial(i);
if (Material == nullptr)
{
continue;
}
if (SeenMaterials.Contains(Material))
{
continue;
}
if (Material->GetBlendMode() == EBlendMode::BLEND_Additive)
{
// Additive materials aren't likely to be valid for footsteps/bullets
continue;
}
SeenMaterials.Add(Material);
// If the physical material is null or default, log a warning
if (Material->GetPhysicalMaterial() == nullptr || Material->GetPhysicalMaterial() == GEngine->DefaultPhysMaterial)
{
MapCheck.Warning()
->AddToken(FUObjectToken::Create(this))
->AddToken(FTextToken::Create(FText::FromString("Actor")))
->AddToken(FUObjectToken::Create(Actor))
->AddToken(FTextToken::Create(FText::FromString("has material")))
->AddToken(FUObjectToken::Create(Material))
->AddToken(FTextToken::Create(FText::FromString("which does not have a valid physical material")));
}
}
}
}
}
#endif
This can be extended to check any piece of data in a level. If it is accessible from the world in code, it can be checked.
The performance of this actor should not be a big concern as the WITH_EDITOR
guards mean it is excluded from a non-editor build, and the map check log provides an execution time counter for free:
Artists can then click on the highlighted words in the messages to navigate to the relevant content which needs fixing.
🏷️ map actor custom level material mesh game world estranged editor ue4 unreal engine output bullet
Please click here to load comments.