Custom rules
Custom rules run next to the built-in ones: in the panel, in Data Validation, in the commandlet and from Python. They use the same severity settings and appear in reports like any other rule.
Every rule derives from the UTAAssetDoctorRule class. You can write rules in Blueprint (simple checks, no auto-fix) or in C++ (full access to the context, auto-fix support).
Rule properties
Section titled “Rule properties”Every rule has these properties, set in the Blueprint’s Class Defaults or in the C++ constructor:
| Property | C++ name | Meaning |
|---|---|---|
| Rule Id | RuleId |
Unique id shown in the panel and reports, for example Project.NoUntitled. If empty, the class name is used (without the _C suffix for Blueprints). |
| Display Name | DisplayName |
Name shown in the details panel. If empty, the rule id is shown. |
| Description | Description |
Description of the rule. |
| Default Severity | DefaultSeverity |
Info, Warning (default) or Error. Used unless the Rules setting has an entry for this rule id. |
| Enabled by Default | bEnabledByDefault |
Whether the rule runs (default: enabled). Used unless the Rules setting has an entry for this rule id. |
To change a custom rule’s severity or disable it, add an entry with its Rule Id to Project Settings > Plugins > ta Asset Doctor > Rules.
Blueprint rules
Section titled “Blueprint rules”-
In the Content Browser, create a new Blueprint Class. In the Pick Parent Class dialog, open All Classes, type
Doctorin the search box and selectTAAssetDoctorRule. -
Open the Blueprint. In Class Defaults, set Rule Id (for example
Project.NoUntitled) and optionally Display Name, Description, Default Severity and Enabled by Default. -
In the My Blueprint panel, override Check Asset. It receives the asset’s Asset Data and returns Out Messages, an array of text.
-
Add one message to Out Messages for every problem you find. Return an empty array when the asset is fine. Each message becomes one issue.
-
Compile and save the Blueprint.
-
Open Project Settings > Plugins > ta Asset Doctor and add the Blueprint class to Rules > Custom Rules.
-
Scan again. Issues from your rule appear in the panel with your rule id.
Blueprint rules are called for every asset in scope, so keep them fast:
- Read Asset Registry tags with the Get Tag Value node on Asset Data. This doesn’t load the asset.
- Call Get Asset only when you really need the loaded object. It loads the asset if it isn’t loaded yet, which makes scans much slower.
- Return early for asset classes you don’t care about.
Issues from Blueprint rules have no automatic fix. Use a C++ rule if you need one.
C++ rules
Section titled “C++ rules”Native rules are discovered automatically: every non-abstract C++ subclass of UTAAssetDoctorRule in a loaded module is used. You don’t need to add C++ rules to Custom Rules.
Put your rules in an editor module and add the plugin module to its dependencies:
PrivateDependencyModuleNames.AddRange(new string[] { "TAAssetDoctor" });If the rule lives in a plugin of your own, also list TAAssetDoctor in the Plugins section of that plugin’s .uplugin file.
A simple check
Section titled “A simple check”#pragma once
#include "TAAssetDoctorRule.h"#include "MyRule_NoUntitled.generated.h"
UCLASS()class UMyRule_NoUntitled : public UTAAssetDoctorRule{ GENERATED_BODY()
public: UMyRule_NoUntitled() { RuleId = TEXT("Project.NoUntitled"); DefaultSeverity = ETAAssetDoctorSeverity::Error; }
virtual void CheckAsset(FTAAssetDoctorContext& Context) const override { if (Context.AssetData.AssetName.ToString().Contains(TEXT("Untitled"))) { Context.AddIssue(*this, FText::FromString(TEXT("Give this asset a real name."))); } }};A check with an automatic fix
Section titled “A check with an automatic fix”Report the issue with ETAAssetDoctorFixType::Custom and override ApplyFix. Asset Doctor calls ApplyFix for each of your rule’s issues when the user applies fixes.
#pragma once
#include "TAAssetDoctorRule.h"#include "Engine/Texture2D.h"#include "MyRule_UITextureGroup.generated.h"
/** Textures in UI folders must use the UI texture group. */UCLASS()class UMyRule_UITextureGroup : public UTAAssetDoctorRule{ GENERATED_BODY()
public: UMyRule_UITextureGroup() { RuleId = TEXT("Texture.UIGroup"); DisplayName = NSLOCTEXT("MyGameEditor", "UITextureGroupName", "UI texture group"); DefaultSeverity = ETAAssetDoctorSeverity::Warning; }
virtual void CheckAsset(FTAAssetDoctorContext& Context) const override { // Cheap checks first: class and folder come from the Asset Registry. if (!Context.AssetData.IsInstanceOf(UTexture2D::StaticClass()) || !Context.AssetData.PackagePath.ToString().Contains(TEXT("/UI/"))) { return; }
FString LODGroup; if (Context.GetTag(TEXT("LODGroup"), LODGroup) && !LODGroup.Contains(TEXT("TEXTUREGROUP_UI"))) { Context.AddIssue(*this, FText::FromString(TEXT("Textures in UI folders should use the UI texture group.")), ETAAssetDoctorFixType::Custom); } }
protected: virtual bool ApplyFix(const FTAAssetDoctorIssue& Issue) const override { UTexture* Texture = Cast<UTexture>(Issue.AssetPath.TryLoad()); if (!Texture) { return false; }
Texture->Modify(); Texture->LODGroup = TEXTUREGROUP_UI; Texture->PostEditChange(); Texture->MarkPackageDirty(); return true; // The asset was changed. }};Fix types
Section titled “Fix types”The third argument of AddIssue decides how the issue can be fixed:
| Fix type | Behavior |
|---|---|
ETAAssetDoctorFixType::None (default) |
The issue has to be fixed by hand. |
ETAAssetDoctorFixType::Rename |
Asset Doctor renames the asset to FixData (the new asset name, in the same folder), updates references and saves the renamed asset. |
ETAAssetDoctorFixType::Custom |
Your rule fixes it. Asset Doctor calls your ApplyFixes with all issues of your rule; the default implementation calls ApplyFix for each one and counts how many returned true. FixData is passed through, so you can store whatever your fix needs. |
Custom fixes are applied before renames. Assets changed by a custom fix are not saved automatically; follow the built-in texture fixes and leave them dirty so users can review them.
API reference
Section titled “API reference”FTAAssetDoctorContext
Section titled “FTAAssetDoctorContext”Passed to CheckAsset for every asset.
struct FTAAssetDoctorContext{ const FAssetData& AssetData;
/** Set when the asset is already in memory (e.g. during Data Validation). May be null. */ UObject* LoadedAsset = nullptr;
void AddIssue(const UTAAssetDoctorRule& Rule, const FText& Message, ETAAssetDoctorFixType FixType = ETAAssetDoctorFixType::None, const FString& FixData = FString());
bool GetTag(FName Tag, FString& OutValue) const; int32 GetIntTag(FName Tag, int32 DefaultValue) const; bool GetBoolTag(FName Tag, bool bDefaultValue) const;};| Member | Meaning |
|---|---|
AssetData |
Asset Registry data of the asset being checked. |
LoadedAsset |
The loaded asset, if it is already in memory (for example during Data Validation). nullptr during normal scans. |
AddIssue |
Reports one issue for this asset. Pass *this as Rule. The severity comes from the rule’s settings, the asset path and class are filled in for you. |
GetTag |
Reads an Asset Registry tag as a string. Returns false if the asset doesn’t have the tag. |
GetIntTag |
Reads a numeric tag. Returns DefaultValue if the tag is missing or not a number. |
GetBoolTag |
Reads a boolean tag (for example True/False). Returns bDefaultValue if the tag is missing. |
UTAAssetDoctorRule
Section titled “UTAAssetDoctorRule”| Member | Meaning |
|---|---|
virtual void PrepareForScan() |
Called once before a batch of assets is checked. Cache anything expensive here, for example settings or class lookups. |
virtual void CheckAsset(FTAAssetDoctorContext& Context) const |
Checks one asset. The default implementation calls the Blueprint Check Asset function. |
virtual int32 ApplyFixes(const TArray<FTAAssetDoctorIssue>& Issues) const |
Applies the custom fixes reported by this rule. Returns the number of assets changed. Override it to fix many assets in one go. |
virtual bool ApplyFix(const FTAAssetDoctorIssue& Issue) const |
Protected. Applies one custom fix. Return true if the asset was changed. |
FName GetRuleId() const |
The rule id (falls back to the class name). |
bool IsRuleEnabled() const |
Enabled state after applying the Rules setting. |
ETAAssetDoctorSeverity GetRuleSeverity() const |
Severity after applying the Rules setting. |
Performance
Section titled “Performance”Rules run for every asset in scope, often thousands of them. Asset Doctor’s built-in rules never load assets; they only read Asset Registry data. Follow the same approach:
- Prefer Asset Registry tags (
Context.GetTag,GetIntTag,GetBoolTag) over loading assets. For example, the built-in rules readDimensions,LODGroup,SRGBandCompressionSettingsfor textures andTriangles,LODs,NaniteEnabledandCollisionPrimsfor static meshes. - Filter first: check the asset class or folder before anything else and return early.
- Don’t call
TryLoadinCheckAsset. Load assets only inApplyFix, where it’s expected. If you need the object during Data Validation, useContext.LoadedAssetwhen it isn’tnullptr. - Cache in
PrepareForScan: work that doesn’t depend on the asset, such as reading settings or resolving classes, belongs there instead of inCheckAsset.
Many tags are shown in an asset’s tooltip in the Content Browser. In C++ you can list all of them with FAssetData::TagsAndValues.