Can I instantiate volumes through code?

Is there any way to create a volume (specifically a post-effects volume) through code, preferably Blueprints?

I have a class of moving actor that affects player’s vision via post-effects closer that they get to it. As such I need a post-effects volume to remain attached to these actors as they move. problem is that I can see no way to add a volume as a component. I can parent a volume to an actor in editor just fine, but I need to be able to do this at runtime. If I try to create a composited Blueprint out of hand-created actor/volume hybrid, it just detaches volume.

If there’s another way to achieve this, I’d also be interested in hearing. My current backup plan is to interpolate effects directly onto player’s camera based on distance to any of these actors every tick, but that sounds both painstaking and inefficient.

Hey SF,

There are certainly many ways to achieve effect you’re looking for. An easy way would be to create a Post Process Volume and set it to Unbound so it affects camera no matter where it is. Then in your Level Blueprint (or character blueprint, or moving object’s blueprint, etc), turn it on or off depending on whether player is within a certain range of moving objects.

But there’s more! I just checked our internal build, and ability to add a Post Process Volume as a component inside a Blueprint is there. It’s on it’s way! I can’t give you a firm date for that, but you should be able to do that soon.

Hi ,

So in future it will be possible to spawn a post process volume on a dynamic actor’s location via Blueprint? Am I correct in assuming that currently there is no solution for blending a post process volume relating to distance a pawn is from a dynamic actor? Hopefully, that makes sense.

Thanks,

— .

Thanks for suggestion with unbound effect! That should work in meanwhile.

And that’s great news about coming feature, thank you very much for checking! Firm date aside, I don’t suppose you have a guess as to whether it’d be coming more on order of days, weeks, or months? Just so I know whether I should wait for it on my current project.

hi everyone!

i just discovered that this code works:

	UObject* PostProcessVolumeObj = FindObject<UObject>(nullptr, TEXT("/Script/Engine.PostProcessVolume"));
	UActorFactory* PostProcessVolumeFactory = GEditor->FindActorFactoryByClassForActorClass(UActorFactoryBoxVolume::StaticClass(), APostProcessVolume::StaticClass());
	APostProcessVolume* PostProcessVolume = Cast<APostProcessVolume>(PostProcessVolumeFactory->CreateActor(PostProcessVolumeObj, ComplementsWorld->PersistentLevel, FTransform()));

first you get uobject that represent PP asset;
then you get factory to construct it;
finally you use that factory to create your final actor!

you can use same code to spawn all others volume related actors, for example:

	UObject* NavMeshBoundsVolumeObj = FindObject<UObject>(nullptr, TEXT("/Script/Engine.NavMeshBoundsVolume"));
	UActorFactory* NavMeshBoundsVolumeFactory = GEditor->FindActorFactoryByClassForActorClass(UActorFactoryBoxVolume::StaticClass(), ANavMeshBoundsVolume::StaticClass());
	ANavMeshBoundsVolume* NavMeshBoundsVolume = Cast<ANavMeshBoundsVolume>(NavMeshBoundsVolumeFactory->CreateActor(NavMeshBoundsVolumeObj, ComplementsWorld->PersistentLevel, FTransform()));

or

	UObject* LightmassImportanceVolumeObj = FindObject<UObject>(nullptr, TEXT("/Script/Engine.LightmassImportanceVolume"));
	UActorFactory* LightmassImportanceVolumeFactory = GEditor->FindActorFactoryByClassForActorClass(UActorFactoryBoxVolume::StaticClass(), ALightmassImportanceVolume::StaticClass());
	ALightmassImportanceVolume* LightmassImportanceVolume = Cast<ALightmassImportanceVolume>(LightmassImportanceVolumeFactory->CreateActor(LightmassImportanceVolumeObj, ComplementsWorld->PersistentLevel, FTransform()));

Bye :wink:

5 Likes

Hey There,
after 6 years , Is there a way to also spawn Audio volumes at runtime and set their brush setting (volume size) ?

Hey there!

I was looking for a solution to spawn AAudioVolume at runtime as well.
After searching and digging through the source code, I think I found a way to do it.
It does require some C++ knowledge, though.

Here’s what worked for me :slightly_smiling_face::

AAudioVolume* UAmbientSoundComponent::CreateVolume(const FAudioVolumeData& Data)
{
    UWorld* const World = GetWorld();
    check(World != nullptr);

	// Data.Transform is a local transform (it might not be the case for you)
	// If your data already contains a world transform, you can use it directly
    FTransform SpawnTransform = Data.Transform * GetOwner()->GetTransform();

    FActorSpawnParameters SpawnParams;
    SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;

    // Spawn the audio volume
    AAudioVolume* AudioVolume = World->SpawnActor<AAudioVolume>(AAudioVolume::StaticClass(), SpawnTransform, SpawnParams);
    if (AudioVolume == nullptr)
    {
        UE_LOG(AmbientSoundComponentLog, Error, TEXT("Failed to create an audio volume."));
        return nullptr;
    }

   	// Disable the volume before modifying it
	// This removes the proxy from the audio thread, 
	// preventing it from being registered with an incorrect shape
    AudioVolume->SetEnabled(false);

    if (UBrushComponent* Brush = AudioVolume->GetBrushComponent())
    {
        if (Brush->BrushBodySetup == nullptr)
        {
            // Ensure it has a valid BodySetup
            Brush->BrushBodySetup = NewObject<UBodySetup>(Brush);
            Brush->BrushBodySetup->CollisionTraceFlag = CTF_UseSimpleAsComplex;
        }

        // Clear any existing geometry 
        Brush->BrushBodySetup->AggGeom.BoxElems.Reset();

        // Set the box extent
		// Note that FKBoxElem expects full dimensions, 
		// but BoxExtent represents half-extents
        FKBoxElem NewBoxElem;
        NewBoxElem.X = Data.BoxExtent.X * 2;
        NewBoxElem.Y = Data.BoxExtent.Y * 2;
        NewBoxElem.Z = Data.BoxExtent.Z * 2;

        // Since the `UBrushBuilder` is normally responsible for defining the BodySetup when placed in the editor.
		// We can not use it directly here, so we manually add a simple FKBoxElem shape here to ensure it has a valid BodySetup.
        Brush->BrushBodySetup->AggGeom.BoxElems.Add(NewBoxElem);

        // Rebuild the BodyInstance with the new shape and update the physics state
        Brush->GetBodyInstance()->InitBody(ToRawPtr(Brush->BrushBodySetup), AudioVolume->GetActorTransform(), Brush, World->GetPhysicsScene());
        Brush->RecreatePhysicsState();
    }

    // Apply audio settings
    FReverbSettings ReverbSettings;
    ReverbSettings.bApplyReverb = true;
    ReverbSettings.ReverbEffect = Data.ReverbSettings.ReverbEffect;
    ReverbSettings.FadeTime = Data.ReverbSettings.FadeTime;
    ReverbSettings.Volume = Data.ReverbSettings.Volume;

    AudioVolume->SetReverbSettings(ReverbSettings);
    AudioVolume->SetInteriorSettings(Data.AmbientSettings);
    AudioVolume->SetSubmixSendSettings(Data.SubmixSendSettings);
    AudioVolume->SetPriority(Data.Priority);

    // Re-enable the volume now that it has the correct settings
	// This adds the proxy back with the correct shape and ensures it works as expected
    AudioVolume->SetEnabled(true);

    return AudioVolume;
}

Don’t mind the FAudioVolumeData, it’s just a custom struct I made to store all the AAudioVolume settings like transform, box extent, reverb settings, priority, and interior/submix settings.
You can replace it with whatever structure works for your setup.

This only creates a box-shaped volume, but you can modify it to create spheres, capsules, or any other shape.
I also think this method could be used to create any volume that extends AVolume.

Note that I haven’t tested this in Shipping builds yet, but it should work.

1 Like