AVolume derived class won't show volume in editor

AVolume derived classes that worked in 4.22 are behaving inconsistently between builds and even loads in 4.23.

  1. Make plugin
  2. Use the editor wizard to create an AVolume derived C++ class named “MyVolume”.
  3. Build, reload and drag MyVolume into world. Note that volume bounding box appears and brush is set.
  4. Add some properties to the class in C++. Rebuild and reload.
  5. MyVolume no longer creates bounding box and brush shape is unset. Setting brush shape makes no difference.
  6. Quit editor, delete all binaries folders, saved, intermedia and project. Generate project files. Remove all modifications to MyVolume. Clean build project. Load editor.
  7. Drag new MyVolume 2 into world. Volume bounding box does not appear.

This isn’t a perfect set of steps to recreate it - it’s very inconsistent but I am definitely getting multiple failures as described above.

Update: I moved my code back to 4.22 and the problem manifested itself there too. There must be something wrong with my code. Here is the header: https://pastebin.com/VeFgHuEj

I cannot for the life of me work out what the issue is, nor why the above behaviour still manifests after the class has been reverted.

1 Like

I have also run into a similar problem.

It looks like what’s happening is that the correct Actor Factory isn’t being set and passed to the Item Details (wherever that gets set: I delved in and ended up template code that I couldn’t reverse engineer to find where it was called from). So the engine tries to work out what factory to use. The way it does this is by looping through the available factories and finding the first one that claims to be able to construct your object.

(This is done in the function: AActor* FActorFactoryAssetProxy::AddActorForAsset( UObject* AssetObj, bool SelectActor, EObjectFlags ObjectFlags, UActorFactory* FactoryToUse /*= NULL*/, const FName Name ) in file \ue4\Engine\Source\Editor\UnrealEd\Private\AssetSelection.cpp

If it gets another factory first with higher priority, it uses that one instead of the correct factory. In my case I had a blueprint subclass, so it was using the actor blueprint factory instead of the actor box volume factory.

Sorry this isn’t necessarily particularly helpful, but it may a) offer some insight for how you can work around it, and b) offer the devs at Epic some insight as to how to fix this.

So you reckon just adding a factory for the custom type that extends the original AVolume factory would fix it? I’ll give it a go either way.

I often wish there was an option for the factory to just be a function in the class you’re working on instead of all the code bureaucracy.

I hadn’t even thought of that! I found switching to a C++ only class worked, since it stopped the blueprint factory taking priority.

Let me know how it goes, though :slight_smile:

Got around to trying it finally. I can confirm that it’s running the constructor but that’s it, it never runs CanCreateActorFrom(), which is all that the BoxVolume ActorFactory runs.

UCLASS()
class HYPERFOLIAGEPLUGIN_API UActorFactoryHyperFoliageVolume : public UActorFactoryBoxVolume
{
	GENERATED_UCLASS_BODY()

	virtual bool CanCreateActorFrom(const FAssetData& AssetData, FText& OutErrorMsg) override;
	virtual void PostSpawnActor(UObject* Asset, AActor* NewActor) override;
};


UActorFactoryHyperFoliageVolume::UActorFactoryHyperFoliageVolume(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	DisplayName = LOCTEXT("HyperFoliageVolumeDisplayName", "HyperFoliage Volume");
	NewActorClass = AHyperFoliageVolume::StaticClass();
}

bool UActorFactoryHyperFoliageVolume::CanCreateActorFrom(const FAssetData& AssetData, FText& OutErrorMsg)
{
	if (UActorFactory::CanCreateActorFrom(AssetData, OutErrorMsg)) {
		return true;
	}

	if (AssetData.IsValid() && !AssetData.GetClass()->IsChildOf(AHyperFoliageVolume::StaticClass())) {
		return false;
	}

	return true;
}

My theory is that some other factory is intercepting it. I’m just not good enough at debugging yet to figure it out. It must be specific to AVolume derived user classes because otherwise it would have been picked up quickly. Could UEditorEngine::CreateVolumeFactoriesForNewClasses() be part of the problem?

Alternately, line 540 of PlacementModeModule.cpp has this:

const bool IsVolume = ClassIt->IsChildOf(AVolume::StaticClass());

			if (IsVolume)
			{
				ActorFactory = GEditor->FindActorFactoryByClassForActorClass(UActorFactoryBoxVolume::StaticClass(), *ClassIt);
			}
			else if (ActorFactory && !ActorFactory->CanCreateActorFrom(NoAssetData, UnusedErrorMessage))
			{
				continue;
			}

			Category->Items.Add(CreateID(), MakeShareable(new FPlaceableItem(ActorFactory, FAssetData(*ClassIt))));

…which feels like it might be forcing it to run the wrong actor factory.

I know this is an old question but I ran into this exact issue in 4.27 and the problem was I had my plugin LoadingPhase set to PostEngineInit in the uplugin file. This meant my Volume wasn’t getting associated with the correct factories during the InitEditor call in EditorEngine.cpp. If you search for ‘NewFactory->NewActorClass = VolumeClass’ you should see where this happens. Changing the LoadingPhase back to Default in the uplugin file fixed it for me.

My plugin LoadingPhase is PostEngineInit.
In 4.24 everythins is ok
But in 4.26 volume can not apprear in level editor viewport.
The reason in above replies, and my plugin LoadingPhase can not set to Default.
So how to fix this?

  1. Create a actor factory, AssetData.AssetClass already set to actor, but can use class path do this.
bool UTestActorFactory::CanCreateActorFrom(const FAssetData& AssetData, FText& OutErrorMsg)
{
    FString PathName = ATestVolume::StaticClass()->GetPathName();
    if(AssetData.ObjectPath==FName(*PathName))
    {
	    return true;
    }
    return false;
}
  1. Register your actor factory in plugin module
void TestEditorModule::StartupModule()
{
	if (GEditor)
	{
		UTestActorFactory* ActorFactory = NewObject<UTestActorFactory>(GetTransientPackage(), UTestActorFactory::StaticClass());
		GEditor->ActorFactories.Add(ActorFactory);
	}
}

Create your derived volume, now it can show in editor.