Download

How to create "Instanced" objects from C++

So I have a UPROPERTY of EditInlineNew objects.

UCLASS(BlueprintType, Blueprintable, DefaultToInstanced, EditInlineNew, Within = SomeObjectManager)
class  SomeObject: public UObject

UPROPERTY(EditAnywhere, BlueprintReadOnly, Instanced, meta = (AllowPrivateAccess = "true"), Category = "Test")
TArray<SomeObject*> SomeObjects;

In blueprint and in the editor, I’m able to create instances of the object and they’re added to the array.

But I want to initialize the array contents in the constructor of a C++ actor because I have a base class written in C++.

I tried something like this in my constructor:

SomeObject* SomeObjectInstance= NewObject<SomeObject>(SomeManager, SomeObject::StaticClass());
SomeManager->SomeObjects.Add(SomeObjectInstance);

It kindof works and in the editor I can see the instances of the objects but I’m not able to save the blueprint class in editor. I get errors like: "Can’t save Blah.uasset. Graph is linked to private objects in an external package. External Objects: " And then a list of the names of the SomeObjectInstances. I must not be setting something up correctly even though I made sure the “Outer” objects are passed in.

I’m basically forcefully adding the objects into the array of “Instanced” objects that are “EditInlineNew” only I manually created the object in C++ instead of using the editor to create those instances.

Without knowing what SomeObject does and what its purpose is, constructors are not the place to do what you want to do in most cases. If SomeObject was an actor, AActor::OnConstruction and associated functions would be a good start. For UObjects it gets trickier. You write that you can “see” the objects. If they are neither actors nor actor components, what are they and what’s their visual representation in the scene?

The outer issue could be due to you doing this in a constructor, SomeManager may not be valid or could be pointing somewhere strange.

SomeManager was definitely valid. OnConstruction seems to happen when the actor is placed in the level but doesn’t affect the CDO of the class.

If I was doing this in Blueprint, I’d have some BaseClass where I prepopulate the manager with some objects and the subclass would have those objects exist since they were built in the base class. I’m trying to instead do this in C++.

If you’re using the editor, I’m sure the editor does something special when creating instanced objects at the C++ level, which I haven’t been able to figure out yet.

All of my code works if I do it at runtime, like in BeginPlay but then my C++ class doesn’t have these objects preconfigured properly. I suppose if I can’t figure this out, I’d just make this base class in blueprint and precreate the instanced objects I want in the editor.

The FObjectInitializer destructor does a bunch of stuff related to instancing. Did you try using CreateDefaultSubobject in the constructor? Since you seem to be using SomeManager as the outer this may need to happen in the constructor of whatever class SomeManager corresponds to.

I managed to possibly figure it out for now. I haven’t seen any problems yet. I put a breakpoint in NewObject and managed to capture it at just the right moment when creating an instanced inline class in the editor.

It’s based on this code: https://github.com/EpicGames/UnrealEngine/blob/c3caf7b6bf12ae4c8e09b606f10a09776b4d1f38/Engine/Source/Editor/PropertyEditor/Private/UserInterface/PropertyEditor/SPropertyEditorEditInline.cpp#L224

This is my real code, and it seems to work.

	EObjectFlags MaskedOuterFlags = GetMaskedFlags(RF_PropagateToSubObjects);
	if (HasAnyFlags(RF_ClassDefaultObject | RF_ArchetypeObject))
	{
		MaskedOuterFlags |= RF_ArchetypeObject;
	}
	
	URDBaseHierarchicalRotationController* Controller = NewObject<URDBaseHierarchicalRotationController>(this, ControllerClass, NAME_None, MaskedOuterFlags);

This is based on my modified forum post code:

EObjectFlags MaskedOuterFlags = SomeManager->GetMaskedFlags(RF_PropagateToSubObjects);
if (SomeManager->HasAnyFlags(RF_ClassDefaultObject | RF_ArchetypeObject))
{
	MaskedOuterFlags |= RF_ArchetypeObject;
}

SomeObject* SomeObjectInstance= NewObject<SomeObject>(SomeManager, SomeObject::StaticClass(), NAME_None, MaskedOuterFlags);
SomeManager->SomeObjects.Add(SomeObjectInstance);

This is code that ultimately gets called from the constructor of an actor that has a component that manages instances of an object. It’s basically for a system that lets me control how different body parts of a character face the world depending on AI focus modes and NPC states and other such things.

template <typename T>
T* CreateRotationController(TSubclassOf<URDBaseHierarchicalRotationController> ControllerClass = T::StaticClass(), int32 Index = -1)
{
	return Cast<T>(CreateRotationController(ControllerClass, Index));
}
	
UFUNCTION(BlueprintCallable, Category = "Rotation")
URDBaseHierarchicalRotationController* CreateRotationController(TSubclassOf<URDBaseHierarchicalRotationController> ControllerClass, int32 Index = -1);
URDBaseHierarchicalRotationController* URDBaseHierarchicalRotationControlManager::CreateRotationController(TSubclassOf<URDBaseHierarchicalRotationController> ControllerClass, int32 Index)
{
	if (!ControllerClass)
	{
		return nullptr;
	}

	EObjectFlags MaskedOuterFlags = GetMaskedFlags(RF_PropagateToSubObjects);
	if (HasAnyFlags(RF_ClassDefaultObject | RF_ArchetypeObject))
	{
		MaskedOuterFlags |= RF_ArchetypeObject;
	}
	
	URDBaseHierarchicalRotationController* Controller = NewObject<URDBaseHierarchicalRotationController>(this, ControllerClass, NAME_None, MaskedOuterFlags);
	SetRotationControllerIndex(Controller, Index);
	
	return Controller;
}
ANewRotationTestCharacter::ANewRotationTestCharacter(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	bUseControllerRotationPitch = true;
	bUseControllerRotationYaw = true;
	bUseControllerRotationRoll = true;
	
	HierarchicalRotationControlComponent = CreateDefaultSubobject<URDBaseHierarchicalRotationControlComponent>("HierarchicalRotationControlComponent");
		
	HeadRotationController = HierarchicalRotationControlComponent->GetRotationControlManager()->CreateRotationController<URDBaseHierarchicalRotationController>();
	
	HeadRotationController->PitchAxisInfo.RelativeLimitMax = 45.f;
	HeadRotationController->PitchAxisInfo.RelativeLimitMin = 50.f;
	
	HeadRotationController->YawAxisInfo.RelativeLimitMax = 70.f;
	HeadRotationController->YawAxisInfo.RelativeLimitMin = 70.f;
	
	HeadRotationController->RollAxisInfo.RelativeLimitMax = 30.f;
	HeadRotationController->RollAxisInfo.RelativeLimitMin = 30.f;
	
	
	TorsoRotationController = HierarchicalRotationControlComponent->GetRotationControlManager()->CreateRotationController<USFCharacterTorsoHierarchicalRotationController>();
	
	TorsoRotationController->PitchAxisInfo.RelativeLimitMax = 45.f;
	TorsoRotationController->PitchAxisInfo.RelativeLimitMin = 50.f;
	
	TorsoRotationController->YawAxisInfo.RelativeLimitMax = 70.f;
	TorsoRotationController->YawAxisInfo.RelativeLimitMin = 70.f;
	
	TorsoRotationController->RollAxisInfo.bFaceRotationAxisEnabled = false;
	TorsoRotationController->RollAxisInfo.RelativeLimitMin = 30.f;
	TorsoRotationController->RollAxisInfo.RelativeLimitMax = 30.f;
	
	
	ActorRotationController = HierarchicalRotationControlComponent->GetRotationControlManager()->CreateRotationController<USFCharacterActorHierarchicalRotationController>();
	
	ActorRotationController->PitchAxisInfo.bFaceRotationAxisEnabled = false;
	ActorRotationController->PitchAxisInfo.RelativeLimitMax = 22.5f;
	ActorRotationController->PitchAxisInfo.RelativeLimitMin = 22.5f;
		
	ActorRotationController->RollAxisInfo.bFaceRotationAxisEnabled = false;
	ActorRotationController->RollAxisInfo.RelativeLimitMin = 22.5f;
	ActorRotationController->RollAxisInfo.RelativeLimitMax = 22.5f;
}

I have properties in my actor for HeadRotationController, TorsoRotationController, and ActorRotationController and they all point to the same instance that’s in the array managed by the manager. That way when I put my actor in a level I can also make my characters have customizeable limits and modes on how they rotate to face their focus. This way a monster, shop keeper, or other NPC can have easily customizeable modes for how different parts of their body turn towards their targets. This is the system I’m trying to build and it’s just a matter of getting the right combination of UPROPERTY flags and C++ code to get it working right.

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Rotation")
	URDBaseHierarchicalRotationControlComponent* HierarchicalRotationControlComponent;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Instanced, Category = "Rotation")
	URDBaseHierarchicalRotationController* HeadRotationController;
	
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Instanced, Category = "Rotation")
	USFCharacterTorsoHierarchicalRotationController* TorsoRotationController;
	
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Instanced, Category = "Rotation")
	USFCharacterActorHierarchicalRotationController* ActorRotationController;

If you’re doing this in a constructor, you should just be using CreateDefaultSubobject. NewObject isn’t meant for use in a Constructor.

UParent::UParent(const FObjectInitializer& OI)
	: Super(OI)
{
	SomeObject = OI.CreateDefaultSubobject<USomeObject>(this, FName("SubObjectName"));
}

Oh that looks like it’s working. And the properties that point to them no longer have to be instanced.

I use this helper method in my manager object that acts as their “Outer” to spawn them.

URDBaseHierarchicalRotationController* URDBaseHierarchicalRotationControlManager::CreateDefaultSubobjectRotationController(FName SubobjectName, TSubclassOf<URDBaseHierarchicalRotationController> ControllerClass, int32 Index, bool bTransient)
{
	FObjectInitializer* CurrentInitializer = FUObjectThreadContext::Get().TopInitializer();
	UE_CLOG(!CurrentInitializer, RDBase, Fatal, TEXT("No object initializer found during construction."));

	URDBaseHierarchicalRotationController* Controller = Cast<URDBaseHierarchicalRotationController>(CurrentInitializer->CreateDefaultSubobject(this, SubobjectName, ControllerClass, ControllerClass, true, bTransient));
	SetRotationControllerIndex(Controller, Index);
	
	return Controller;
}

One issue I’m having is I created a blueprint subclass of my C++ class and modified a few values. When I place the blueprint actor, the properties are set to the C++ class values in the details panel but when I select reset to default it sets it to the blueprint subclass values.

Another issue is for some reason in the array I can’t see or edit the values of properties when editing the actor BP. But I can see and edit the properties when I view it through the properties themselves. And in the editor details I can see and edit the properties of the subobjects in both the array and the properties.

These 2 seem like possible engine bugs.

Not editable inline in the array. Pitch axis info, Yaw axis info, and Roll axis info are structs that I can’t expand. I can edit one of the enum props.

Editable when I’m editing one of the properties directly that points at one of the instances in the array. This is at array index 2.

Editable in the Details Panel when actor is placed in the level