Using SpawnActor and other BP nodes

SpawnActor nodes and some others aren’t available by default in UObject child classes.

This is caused by the default implementation of GetWorld. You need to override it, and make sure the default implementation never gets called.

Important: Your GetWorld code should not call Super::GetWorld(). The default implementation will make BP’s think you don’t have it correctly implemented.

UObjects by default don’t have a valid reference to a world. You need some way of referencing one in your GetWorld. For example, if you always pass an Outer object which is an Actor (since actors always have a world), the following implementation works well:

const AActor* Outer = Cast<AActor>(GetOuter());
if(Outer)
{
	return Outer->GetWorld();
}
 
return nullptr;

Custom serialization

The class loading logic below might not work for actors or actor components. Example code from TCFX on Unreal Source Discord.

/** Serialize objects array */
//First need to remove any invalid object from the array
if (Ar.IsSaving())
{
	MyObjects.RemoveAll([](UObject* Obj)
	{
		return (Obj == nullptr || Obj->IsPendingKill());
	});
}
 
//Serialize array num;
const int32 ArrayNum = MyObjects.Num();
Ar << ArrayNum;
 
if (Ar.IsSaving())
{
	for (UObject* Object : MyObjects)
	{
		FString ObjectClassName = Object->GetClass()->GetFName().ToString();
		Ar << ObjectClassName;
		Obj->Serialize(Ar);
	}
}
 
if (Ar.IsLoading())
{
	MyObjects.Reserve(ArrayNum);
	for (int32 Index=0; Index < ArrayNum; ++Index)
	{
		FString ObjectClassName;
		Ar << ObjectClassName;
		if (!ObjectClassName.IsEmpty())
		{
			UClass* FoundClass = FindObject<UClass>(ANY_PACKAGE, *ObjectClassName);
			if (FoundClass)
			{
				UObject* Obj = StaticAllocateObject(FoundClass, GetTransientPackage(), NAME_None, EObjectFlags::RF_NoFlags, EInternalObjectFlags::None, false);
				if (Obj)
				{
					Obj->Serialize(Ar);
					MyObjects.Add(Obj);
				}				
			}
		}
	}
}