Download

Instanced variables in Blueprint?

Is there an equivalent to the Instanced uproperty specifier available from Blueprint?

In C++ it is possible to do something like this


//pragma once, includes, etc.

UCLASS(EditinlineNew)
class PROJECT_API UMyClass : public UObject
{
	GENERATED_BODY()

public:
	UMyClass()
		: Super()
	{
	}

};

UCLASS()
class PROJECT_API AMyActor : public AActor
{
	GENERATED_BODY()

public:
	AMyActor()
		: Super()
	{
		InstancedMyClassVariable = nullptr;
	}

	UPROPERTY(Category = "MyActor", EditAnywhere, Instanced)
	UMyClass* InstancedMyClassVariable;

};

When placing MyActor into the scene/level, the InstancedMyClassVariable can be edited and assigned newly created instances of MyClass.

How can I achieve the same just with Blueprint? Is there an equivalent to the Instanced uproperty specifier available from Blueprint?

Thank you very much!

In your Class Settings/Defaults near the top there are 2 check marks, Expose on Spawn and Editable. When spawning with Spawn Actor From Class or selecting the actor you will be able to set or edit exposed variables

That’s a different thing entirely. OP is talking about being able to create UObjects through a dropdown within the editor.

@UnrealEverything: Pretty sure it’s not doable in blueprints. Wish it was, along with specifying metadata and a bunch of other stuff, though I can understand why Epic want to refrain from exposing too much stuff that most people will never use.

Oops, I totally read it as editing variables of a spawned/placed instanced blueprint. but that assumes you already have the class.

Thanks to both of you for your responses.

That’s unfortunate but I can also understand Epic’s decision. Does anyone know a reasonable workaround? The only thing I can think of right now would be some kind of wrapper like this


UCLASS()
class PROJECT_API UMyClassWrapper : public UObject
{
	GENERATED_BODY()

public:
	UMyClassWrapper ()
		: Super()
	{
		InstanceClass = NULL;
		InstancedMyClassVariable = nullptr;
	}

	virtual void PostEditChangedProperty(FPropertyChangedEvent & PropertyChangedEvent) override
	{
		if (... test whether InstanceClass has changed and is != NULL ...)
		{
			InstancedMyClassVariable = NewObject<UMyClass>(GetTransientPackage(), *InstanceClass);
		}
	}

	UPROPERTY(Category = "MyActor", EditAnywhere)
	TSubclassOf<UMyClass> InstanceClass;

	UPROPERTY(Category = "MyActor", VisibleAnywhere)
	UMyClass* InstancedMyClassVariable;

};

Any other thoughs?

Edit: Oh wait, a wrapper like that won’t help at all (at least not on its own). Unless maybe it would be a component? Hmmm, I guess I’ll try it later and report back.

Thank you again!

Yeah, that just turns the issue into ‘How can I now instantiate my wrapper?’.

Components can certainly help here, if you’re willing to go that route, since they’re always instanced. Actually, that made me think perhaps marking your UMyClass as DefaultToInstanced along with EditInlineNew might do the trick, but I did a test and it seems that that specifier is ignored for blueprint variables unfortunately.

I think you could probably come up with a workaround for this using detail customizations, which might be less intrusive than a wrapper/component. However it would also be more work, and it would depend on how you need to use this. Do you have existing C++ base classes for the class you want to instance, and for the containing class which you’ll be making a blueprint from, and you only need this behaviour in that context? If so it’s probably doable, but if you need something more generic then probably components are the only option short of an engine mod.

Yes, that’s exactly my context. But if I can make it as generic and reusable as possible I’m all up for it, even if it’s more work :slight_smile:

For now I have ended up with this component and it seems to work ok, not great but ok. At least it does what I wanted. The only downside is the Instanced specifier for InstancedMyClassVariable which allows you to change this value independently from InstanceClass. And I cannot change it to VisibleAnywhere either. That prevents the InstancedMyClassVariable members to show up in the editor and therefore they would not be editable.

.h


// Copyright 2016 UnrealEverything. All Rights Reserved.

#pragma once

#include "Components/ActorComponent.h"
#include "InstanceHelperComponent.generated.h"


UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class PROJECT_API UInstanceHelperComponent : public UActorComponent
{
	GENERATED_BODY()

public:	
	// Sets default values for this component's properties
	UInstanceHelperComponent();
	virtual ~UInstanceHelperComponent();
		

	virtual void PostEditChangeProperty(FPropertyChangedEvent & PropertyChangedEvent) override;


	UPROPERTY(BlueprintReadOnly, Category = "Instancing", EditAnywhere)
	TSubclassOf<UObject> InstanceClass;

	UPROPERTY(BlueprintReadOnly, Category = "Instancing", EditAnywhere, Instanced)
	UObject* InstancedMyClassVariable;
	
};

.cpp


// Copyright 2016 UnrealEverything. All Rights Reserved.

#include "Project.h"
#include "InstanceHelperComponent.h"


// Sets default values for this component's properties
UInstanceHelperComponent::UInstanceHelperComponent()
	: Super()
{
	// Set this component to be initialized when the game starts, and to be ticked every frame.  You can turn these features
	// off to improve performance if you don't need them.
	bWantsBeginPlay = false;
	PrimaryComponentTick.bCanEverTick = false;

	InstanceClass = NULL;

	InstancedMyClassVariable = nullptr;
}

UInstanceHelperComponent::~UInstanceHelperComponent()
{
}

void UInstanceHelperComponent::PostEditChangeProperty(FPropertyChangedEvent & PropertyChangedEvent)
{
	Super::PostEditChangeProperty(PropertyChangedEvent);

	UProperty* MemberProperty = PropertyChangedEvent.MemberProperty;
	if (MemberProperty)
	{
		const FName MemberPropertyName(MemberProperty->GetFName());
		if (MemberPropertyName == GET_MEMBER_NAME_CHECKED(UInstanceHelperComponent, InstanceClass))
		{
			UClass* Class = *InstanceClass;
			if (Class)
			{
				//TODO specific outer, other arguments, etc.
				InstancedMyClassVariable = NewObject<UObject>(InstancedMyClassVariable ? InstancedMyClassVariable->GetOuter() : GetOuter(), Class);
			}
			else
			{
				InstancedMyClassVariable = nullptr;
			}
		}
		else
		{
			//handling for other property names
		}
	}
	else
	{
		//errorhandling for MemberProperty == nullptr
	}
}

I guess an engine mod would be the cleanest solution, I’d only have to lift this restriction (and hope that it does not cause any problems down the line). After spending some time searching and reading some code (starting with UBlueprint and finding my way to FBPVariableDescription, FKismetCompilerContext::CreateClassVariablesFromBlueprint most importantly FBlueprintVarActionDetails::CustomizeDetails) it may be as simple as adding a new custom row to the “Variable” category which is a checkbox like “Editable” and controls whether to add the Instanced specifier to the property flags or not. So using detail customization. And then make sure it actually has an effect and is not disabled anywhere…

I’ll investigate this route further and can possibly create a pull request for this if Epic is willing to change their mind and expose this flag.

Edit: After a bit more testing, the above component approach does not really work either. A blueprint Actor class with one of these components which has been placed in the editor does not properly update InstancedMyClassVariable when InstanceClass is changed e.g. when changing from class “None” while InstancedMyClassVariable is also None to any other class, InstancedMyClassVariable remains unchanged (at least in the Editor UI). Not sure whether I am missing some update function or why it doesn’t update.

Yup, I think if engine mod is an option, it’s definitely the way to go about this. I agree it’s probably not too much work. If you select a variable in a blueprint and expand the advanced section of the ‘Variable’ category in the details panel, you can see it lists some property flags (read only). If you toggle, for example, the ‘Transient’ check box, it will get added to that list. So you should just be able to see how that is implemented and duplicate the functionality.

In theory, that is. It’s rarely that straightforward with the engine code though. A quick look at the UHT parser shows the following on encountering the ‘Instanced’ specifier:


Flags |= CPF_PersistentInstance | CPF_ExportObject | CPF_InstancedReference;

Not one but three property flags. Maybe you just need to add them all and it’ll work… :wink:

Okay after a wasted hour or two of debugging the engine (after adding the Instanced customization and following what happens when toggling that new checkbox) turns out it was working already. Probably took an engine restart though.
I would have done it as a plugin but as far as I can tell the blueprint editor/compiler is pretty much hardcoded. If you hadn’t pointed it out I would have probably forgotten the ExportObject flag (if UHT adds it, it is probably needed I guess) so that was actually very helpful, thank you :slight_smile:

Now for a pull request I should probably update my engine version to the most recent commit of the master branch first correct?

Good stuff. Yeah I doubt this is possible as a plugin, the various asset editors do not expose much for extension.

For the PR, I’m not the person to ask. Git just confuses the hell out of me. Basically, when you try to submit, it should tell you if the branches are automatically mergeable. If they are, I’d just go ahead and do it.

Alright, thanks for your help.

Just in case someone else is wondering, UHT also adds the “EditinlineNew” - “True” metadata to instanced properties. And arrays with the instanced keyword are a different story as well, those have CPF_ExportObject | CPF_ContainsInstancedReference flags set and the inner property must have the same flags and metadata as the regular non-array property.
Took me a while to get everything right and to make sure it works correctly. Here are the links to my branch and PR.

Interesting discussion here!

I did something similar, it’s almost identical to @UnrealEverything’s implementation, with 2 exception:

  1. I deffer array property flags/metadata tampering to KismetCompiler without store them first (that simplify logic)


			// Set property flags and metadata on instanced array property
			if (Variable.VarType.bIsArray && NewProperty->HasAllPropertyFlags(CPF_ExportObject | CPF_ContainsInstancedReference))
			{
				UArrayProperty* NewArrayProperty = Cast<UArrayProperty>(NewProperty);
				if (NewArrayProperty->Inner && Cast<UObjectProperty>(NewArrayProperty->Inner))
				{
					NewArrayProperty->Inner->SetPropertyFlags(CPF_PersistentInstance | CPF_ExportObject | CPF_InstancedReference);
					NewArrayProperty->Inner->SetMetaData(TEXT("EditInline"), TEXT("true"));
				}
			}


  1. I added “bGenerateEditInlineNewClass” property to blueprint, so you can make a uobject blueprint "EditInline"able inside editor.

This is super useful, but @UnrealEverything and @Nate, when I try to implement your solution, I’m having one issue, which is that if I create an instanced variable in a blueprint and then try to set its default, after I re-compile the blueprint, it resets back to “None” rather than the inline default I’ve set. Any thoughts?

To be honest I am not even using the changes in my current main engine branch anymore. Partly because I changed stuff around so I didn’t need it and partly because of the response from ‘danroconnor’ on the PR: “…] instanced object properties are quite buggy and we can’t expand their footprint until stabilizing them”.
I cannot remember whether I was having the problem you’ve encountered or not. Is there a chance it’s caused by something else? Is the property transient or the class being instanced?

Maybe I have run into the same issue.
I try to work with an array of instanced objects in a component.
The problem is that component properties with the “CPF_ContainsInstancedReference” flag are skipped from serialisation when we read back the values (they have been written though).
I don’t really know why. Any idea?

any update 3 years later?

Yep I’m still waiting for instanced / editinline support for Blueprints without C++ usage.