x

Search in
Sort by:

Question Status:

Search help

  • Simple searches use one or more words. Separate the words with spaces (cat dog) to search cat,dog or both. Separate the words with plus signs (cat +dog) to search for items that may contain cat but must contain dog.
  • You can further refine your search on the search results page, where you can search by keywords, author, topic. These can be combined with each other. Examples
    • cat dog --matches anything with cat,dog or both
    • cat +dog --searches for cat +dog where dog is a mandatory term
    • cat -dog -- searches for cat excluding any result containing dog
    • [cats] —will restrict your search to results with topic named "cats"
    • [cats] [dogs] —will restrict your search to results with both topics, "cats", and "dogs"

Office Holiday

Epic Games' offices will be on holiday from June 22nd to July 7th. During this period support will be limited. Our offices will reopen on Monday, July 8th. 

Instance Blueprint like Archetype from C++

Hi,

I am trying to use a data only blueprint to instanciate a component. First I added new code via the wizard, extending USkeletalMeshCompnent. Then I created a blueprint extending MyComponent class. Now I want to add this blueprint component to my character from code (I know this can be done inside the blueprint editor, but this a learning test). Taking a look at how the DefaultPawnClass is set I did this in the constructor of my character:

 static ConstructorHelpers::FObjectFinder ComponentClass(TEXT("Class'/Game/Tests/BP_MyComponent.BP_MyComponent_C'"));
 
 TSubobjectPtr testComponent = PCIP.CreateDefaultSubobject(this, TEXT("MyComponent"));
 testComponent->AttachTo(RootComponent);
 Components.Add(testComponent);

However for CreateDefaultSubobject T is expected to be a class, not what FObjectFinder returns. I also tried FClassFinder but had no luck with that either. Same for replacing FObjectFinder with FObjectFinder.

I have not yet gotten used to all the templated types, so I don't even know what types are compatible so I cannot even get the code to compile.

greetings

Product Version: Not Selected
Tags:
more ▼

asked Mar 11 '14 at 02:25 AM in C++ Programming

avatar image

ue4-archive ♦♦ STAFF
49.9k 3666 1994 9120

(comments are locked)
10|2000 characters needed characters left
Viewable by all users

1 answer: sort voted first

Hi -

The templated type parameter that you pass in to CreateDefaultSubobject needs to be the type you're expecting it to return. In this case:

 TSubobjectPtr testComponent = PCIP.CreateDefaultSubobject(this, TEXT("MyComponent"));

Should give you what you want. Hope that helps!

more ▼

answered Mar 11 '14 at 02:25 AM

avatar image

ue4-archive ♦♦ STAFF
49.9k 3666 1994 9120

avatar image ue4-archive ♦♦ STAFF Mar 11 '14 at 02:25 AM

With this I can instance a component, yes, but I already know how to do that. However I explicitly want to instance a blueprint that is extended from the custom MyComponent class in the way it was possible to create objects with archetypes in UDK.

Currently that is hardcoded for testing, I plan to make a list for the editor. (but I digress, that is not the point here)

avatar image ue4-archive ♦♦ STAFF Mar 11 '14 at 02:25 AM

Oh sorry, I misunderstood.

In order to use that class, you'll have to basically mimic what CreateDefaultSubobject does internally for your component type. There's a lot in that function which handles potential overrides that you shouldn't have to worry about. The salient bits are: 1. Get the default object for your component type 2. Construct a new instance of the object on the instance of your actor 3. Add the result to the default subobjects array IF the current instance is a CDO 4. Add the component to the Components array

That should let you use any class that you have a UClass reference to as a component type.

avatar image ue4-archive ♦♦ STAFF Mar 11 '14 at 02:25 AM

I am trying to mimic the function, however whenever I try to get the default object like this:

 ConstructorHelpers::FObjectFinder MyDefaultObject(TEXT("Class'/Game/Tests/BP_MyComponent.BP_MyComponent_C'"));

I get an error

[0008.80][ 5]Error: CDO Constructor: Failed to find Class'/Game/Tests/BP_MyComponent.BP_MyComponent_C'

It also seems to be impossible to mimic what CreateDefaultSubobject does, because it uses a ComponentOverrides Array which is private.

avatar image ue4-archive ♦♦ STAFF Mar 11 '14 at 02:25 AM

For the CreateDefaultSubobject portion, the ComponentOverrides is the part you can leave out. That's the reason that the function call is templated. I haven't compiled this, so there may be a compile error, but this is the stripped down version of the function you need: (Assume MyCompClass is the UClass* you got from the finder, and Outer is your object)

 UObject* Template = MyCompClass->GetDefaultObject(); // force the CDO to be created if it hasn't already
 const EObjectFlags SubobjectFlags = Outer->GetMaskedFlags(RF_PropagateToSubObjects);
 Result = StaticConstructObject(MyCompClass, Outer, SubobjectFName, SubobjectFlags, Template);
 if ( !Outer->GetArchetype()->GetClass()->HasAnyClassFlags(CLASS_Native | CLASS_Intrinsic) )
 {
     // The archetype of the outer is not native, so we need to copy properties to the subobjects after the C++ constructor chain for the outer has run (because those sets properties on the subobjects)
     UObject* MaybeTemplate = Outer->GetArchetype()->GetClass()->GetDefaultSubobjectByName(SubobjectFName);
     if (MaybeTemplate && MaybeTemplate->IsA(MyCompClass) && Template != MaybeTemplate)
     {
         ComponentInits.Add(Result, MaybeTemplate);
     }
 }
 
 if (Outer->HasAnyFlags(RF_ClassDefaultObject) && Outer->GetClass()->GetSuperClass())
 {
     Outer->GetClass()->AddDefaultSubobject(Result, MyCompClass);
 }
 Result->SetFlags(RF_DefaultSubObject);

That will get you the spawned component type you need based off a runtime-specified class.

For the finder part, the problem is that you're telling it to find a component, but specifying a class. So, it fails to find it because the BP_MyComponent_C is a UBlueprintGeneratedClass, not a UMyComponent. What you need to do there is:

 ConstructorHelpers::FObjectFinder MyDefaultObject(TEXT("Class'/Game/Tests/BP_MyComponent.BP_MyComponent_C'"));

And that will give you the class to use in the code to spawn the component in the first snippet. Hope that clears things up!

avatar image ue4-archive ♦♦ STAFF Mar 11 '14 at 02:25 AM

Alright I almost got it, but ComponentInits is marked private in FPostConstructInitializeProperties. I have also seen that created objects are returned as TSubobjectPtrConstructor which is also private so I cannot use that as well.

Everything seems to be programmed in a way to prevent me from doing this which makes me wonder if I am supposed to do it at all?

My ideal solution would be where I could have a component attribute or an array of componets that can be filled from editor. I would have a blueprint character for example and add components to it in the graph editor. Then in the defaults I would assign the added components to the array or, in case there is only one attribute, to the single property.

So far I have only been able to make attributes editiable that are "class MyClass* testSetAttribute" but I can only assign objects from the content browser and I think using smart pointers would be better?

greetings

avatar image ue4-archive ♦♦ STAFF Mar 11 '14 at 02:25 AM

We prevent people from doing it because there's a lot of potential for non-obvious things to happen, unfortunately!

The constructor, where you're attempting to add the component, actually runs before serialization of the class. So, if you were using a property to dictate which class to spawn, it wouldn't actually be valid at that time! You'd have to move it to later, after the instance properties have been serialized in.

You also lose the ability to change and override the class in children using the PCIP, which is also non-obvious.

The best way to deal with dynamic components like this is to use the construction script or AddComponent nodes in the blueprint to add them. Alternatively, you could spawn the components in ::PostLoad() or BeginPlay() or some other time, which would allow the serialization to be valid.

(comments are locked)
10|2000 characters needed characters left
Viewable by all users
Your answer
toggle preview:

Up to 5 attachments (including images) can be used with a maximum of 5.2 MB each and 5.2 MB total.

Follow this question

Once you sign in you will be able to subscribe for any updates here

Answers to this question