What is the correct way to add components to actors outside constructors ?
I can’t find any function to do so.
What is the correct way to add components to actors outside constructors ?
I can’t find any function to do so.
Creating and Registering Components During Runtime
//in some AActor class
void AYourActor::CreateComponent(UClass* CompClass,const FVector& Location, const FRotator& Rotation, const FName& AttachSocket=NAME_None)
{
FName YourObjectName("Hiiii");
//CompClass can be a BP
UPrimitiveComponent* NewComp = ConstructObject<UPrimitiveComponent>( CompClass, this, YourObjectName);
if(!NewComp)
{
return NULL;
}
//~~~~~~~~~~~~~
NewComp->RegisterComponent(); //You must ConstructObject with a valid Outer that has world, see above
NewComp->SetWorldLocation(Location);
NewComp->SetWorldRotation(Rotation);
NewComp->AttachTo(GetRootComponent(),SocketName,EAttachLocation::KeepWorldPosition);
//could use different than Root Comp
}
The most notable line is this
UPrimitiveComponent* NewComp = ConstructObject<UPrimitiveComponent>( CompClass, this, YourObjectName);
in this case, “this” is the owning Actor that is creating its own component, and I am passing in “this” to ConstructObject
NewComp->RegisterComponent() will not work unless “this” is valid and has a valid world associated with itself, as most AActors will
Attach Location
Keep in mind you could use SnapToTarget or KeepRelativePosition
I am choosing here to specify world space starting location and THEN attach.
You could always use Root Comp or add parameter to specify which component to attach to.
You'll probably want to use "Mesh" for any Character :)
It's up to you!
Enjoy!
Rama
Hi Rama!
Thanks for your answer.
Actually I’m already using a very similar code to the one you posted.
However I noticed that InitializeComponent doesn’t gets called when components are created that way (even if bWantsInitializeComponent is set to true), which made me think that maybe there’s some other steps missing too and made me unconfident to follow that method.
That’s why I was asking for the “correct way” or some kind of built-in function.
My fault for not being more explicit in my answer. Sorry.
Strictly speaking, InitializeComponent is actually concept of actor initialization, not component initialization. It’s a sort of contract that lets the component do basic initialization stuff at a time where state is mostly initialized, compared to the constructor where your component won’t even know its parent actor. As such, it is called automatically when actors initialize their child components, and this is why it’s not getting called automatically in this case.
Outside of that, feel free to call it manually or, if you still want it automated, look into OnRegister, which will happen in the RegisterComponentWithWorld call that you have to do anyway.
Furthermore, note that all attaching the component and setting transforms is only necessary if you’re dealing with a scene component. If your component doesn’t need a spatial representation, save yourself the trouble and overhead and inherit from actor component directly.
Hi cmartel, thank you for your answer. It led me to look into the AActor source code and I think that aside InitializeComponent the only other missing step is calling OnComponentCreated.
So my code ended looking something like this :
UMyActorComponent* ActorComponent = ConstructObject<UMyActorComponent>(UMyActorComponent::StaticClass(), this);
ActorComponent->OnComponentCreated();
ActorComponent->RegisterComponent();
if (ActorComponent->bWantsInitializeComponent) ActorComponent->InitializeComponent();
And in case the component inherits from USceneComponent then it should be attached too.
Edit: Hey, it’s me from the future! Now OnComponentCreated() and InitializeComponent() are called from RegisterComponent() so don’t call them manually anymore.
You don’t need to call OnComponentCreated, though, unless you actually do something inside it yourself. It does nothing beyond setting a flag that allows OnComponentDestroyed to be called. If you’re not planning to do anything in either of these functions, save yourself the overhead.
I have fully tested just this workflow as working
PrimitiveComponent was my chosen base class, you could use Scene component for components that dont have collision.
UPrimitiveComponent* NewComp = ConstructObject<UPrimitiveComponent>( CompClass, TheOwner, YourObjectName);
if(NewComp)
{
NewComp->RegisterComponent();
//attach or set location, etc
}
Rama, thank you so much for this! This post here is golden.
It was the straw that broke the camel’s back that got my physics system working.
Thank you a million, again.
And what is the correct way to remove/destroy components at runtime ?
The docs tell you to to call UActorComponent::UnregisterComponent(). However UActorComponent::DestroyComponent() seems like the way to go, actually.
Also, don’t forget to null any pointer holding a reference to the component.
Somehow I think the following lines are more correct for constructing components:
USphereComponent* SphereComponent = CreateDefaultSubobject<USphereComponent>(TEXT(“RootComponent”));
RootComponent = SphereComponent;
or
UStaticMeshComponent* SphereVisual = CreateDefaultSubobject<UStaticMeshComponent>(TEXT(“VisualRepresentation”));
SphereVisual->AttachTo(RootComponent);
They’re according to this official tutorial.
That code is only valid within a constructor. The question was specifically asking how to do it dynamically, outside of a constructor.
Whoa that’s big news!
So do you know how to add Blueprint component in C++ code?
I mean designing a Add Component function which could accepts both C++ and BP Component class.
I’m reading this function AActor::AddComponent(), but this one is BlueprintInternalUseOnly, so I think I shouldn’t use it in my C++ code.
Thanks for your reply earlier on the Q&A post over at Answerhub, kamrann.
I’m fairly new to the engine (coming from Unity), but I’ve poked into something related to this as I’m used to manipulating components like this for some objects. For all I know, I might just be doing it wrong.
As an FYI though, if you use this method on a component that you want a designer to go crazy with, you may want to consider that components created at runtime only seem to expose their properties to the editor via their parent but not if you examine the component itself.
I’m not sure if this is true in blueprint, but if it crops up, have a read of kamrann’s answer in the following:
https://answers.unrealengine.com/questions/372595/uproperties-and-dynamically-created-components.html
Perhaps someone else could also chime in on this, just to see if this really isn’t an inherent limitation as he mentions.
@Hatsune-Miku
I don’t see how that issue relates to exposing a component for a designer. Since you’re talking about dynamic creation, I’d assumed your AH question was only relevant to modifying component properties during a Simulate In Editor session - something designers won’t be doing and is only really useful in some specific testing/debugging scenarios. When a designer is working in the blueprint or level editor, dynamic components don’t exist. Am I missing something?
@marsonmao
Not sure exactly what you’re asking. The C++ equivalent to AddComponent is what is discussed in this thread. It’s a bit outdated now though, see Hatsune’s answer hub link for an example of the code to do it now.
I’ve read the link of Hatsune now; To take that link for example, I was asking that how to put Blueprint component class in this line: Audio = NewObject<UAudioComponent>(this);
So I’ve checked the source code of NewObject, looks like I could give the UClass parameter, so I might be able to do it like NewObject<SomeBaseClass>(this, The BP Component Class)?
But there’s a concern, that’s why I mentioned the function Actor::AddComponent, which is called by Blueprint’s Add Component node. Comparing Hatsune’s link to Actor::AddComponent, you can see that Actor::AddComponent is a lot more complicated than it, so I’m not sure if Hastune’s link is able to completely add a BP Component.
I have a question, hope someone knows the answer, I create new (NewObject()) UStaticMeshComponents at runtime and place them in an TArray. They are registered and i can succesfully set a mesh and manipulate their placement right after creation. However, if i try to access the same TArray, which was declared in the header file, at another time, the array is empty… the meshes are still visible in the editor, but for some reason the TArray is empty? any clue??? tahnks a billion!
Hey, I am not sure if you guys found it out or not, in the newer versions, when you need to use SetupAttachment(), it has to be called before RegisterComponent(), other wise, the attachment will not work.
If you are using array with pointer, please note by adding/removing elements to array, it’s content can get invalidated. That holds especially true for pointers… Eg if you plan to use and add dynamically to it, consider pre-allocate some space in array first. When array is resizing pointers might get lost…
I’m using
ASimpleActor* SpawnActor = World->SpawnActorDeferred<ASimpleActor>(ASimpleActor::StaticClass(), SpawnTransform, this);
SpawnActor->Init();
SpawnActor->FinishSpawning(SpawnTransform);
Does anyone knows why my Actor does not spawn at the Location I set? It always spawn at 0,0,0.
I’m creating the Scene Component and set as Root like this in the Init() function:
RootComp = NewObject<USceneComponent>(this, USceneComponent::StaticClass(), TEXT("RootComponent"));
SetRootComponent(RootComp);
RootComp->RegisterComponent();
(if I change and create the Scene/Root component on constructor using CreateDefaulSubobject, the location works)