Using PerObjectConfig

PerObjectConfig looks like some obscure Unreal Engine feature that no one talks about (this is the first result when searching “unreal engine PerObjectConfig” on Google). I myself lost many days trying to figure it out. I give an answer in the hope that it will spare some time to someone else.

I tested successfully this feature with the version 4.24.3.
Place an actor MyActor in the level.


UCLASS(Config = Game, PerObjectConfig)
class TEST2_API AMyActor : public AActor
{
    GENERATED_BODY()
protected:
    UPROPERTY(EditAnywhere, Config)
    FString SomeString;
public:
    AMyActor();
protected:
    virtual void BeginPlay() override
    {
        Super::BeginPlay();
        if (this->GetName() != TEXT("FooBar")) {
            FActorSpawnParameters Params;
            Params.Name = TEXT("FooBar");
            // Params.ObjectFlags = EObjectFlags::RF_Transient; // This line has no effect
            // The actor we placed (MyActor1) will spawn another MyActorr called FooBar
            this->GetWorld()->SpawnActor<AMyActor>(Params);
        }
    }
};

In order to config use the following (inside Config/DefaultGame.ini or another file set in the UCLASS macro)



[LevelName:PersistentLevel.MyActor_1 MyActor]
SomeString = "This is MyActor1"

[LevelName:PersistentLevel.FooBar MyActor]
SomeString = "This is FooBar"



Hit play and verify that it works also for the spawned actor:

As you can see the name of the section in the .ini file is quite different from what is told in the documentation [ObjectName ClassName].
For more details look at the method (UObject::LoadConfig) this is the one called when (automatically) loading documentation from configuration files.


const bool bPerObject = UsesPerObjectConfig(this);
// ...
if (bPerObject)
{
    FString PathNameString;
    UObject* Outermost = GetOutermost();

    // line 2171 at the time of writing
    if (Outermost == GetTransientPackage())
    {
        PathNameString = GetName();
    }
    else
    {
        GetPathName(Outermost, PathNameString);
        LongCommitName = Outermost->GetFName();
    }

    ClassSection = PathNameString + TEXT(" ") + GetClass()->GetName();

    OverridePerObjectConfigSection(ClassSection);
}

The issue is at the highlighted line: if the object is part of the transient package then its expected section name uses the GetName(), otherwise GetPathName(…). I tried without success setting EObjectFlags::RF_Transient in the Object flags of the spawned actor parameters.

Finally, to answer question 1, Yes it is supposed to work with UObject derived classes (I didn’t test it though). Just figure out what is object’s GetOutermost() result and the difference of per object section naming in case it is part of the transient package.