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"

Component added at runtime doesnt have BeginPlay called on it.

Hello, So i created a Blueprint Function Library in C++, to allow me to add a component to an actor based on a passed in class, the only problem is, the component that is getting added to the actor is never having BeginPlay called on it.

Note: I am calling my AddComponentByClass custom BP function, in the Actor's BeginPlay event in the Actor's blueprint. Yet my Component's BeginPlay event, in its Blueprint, is never getting called.

From what i understand, all Default Components of an Actor have its BeginPlay called before the Actor's BeginPlay if im not mistaken, so how can i go about calling the newly added Component's BeginPlay event, when it may be added after the Actor has already been Initialized?

Product Version: UE 4.8
Tags:
more ▼

asked Aug 20 '15 at 09:58 AM in Using UE4

avatar image

Diddykonga
100 14 18 25

avatar image cancel Aug 20 '15 at 03:04 PM

Are you calling RegisterComponent? You'll have to do all of this work yourself if you're creating and adding it via your own C++ code at game runtime. You can check out some existing examples in the UE4 source of this, I think.

avatar image Diddykonga Aug 20 '15 at 08:10 PM

Ive tried calling RegisterComopnent, but it doesnt do anything, cause the component is already automaticlly registered when its created. Ive also already tried calling BeginPlay on the component after it is created and that crashes the Engine with an Assertion Failed: bRegistered.

avatar image cancel Aug 20 '15 at 08:24 PM

Components are not automatically registered when they are created. If you are creating components via NewObject during runtime, you must call RegisterComponent() on the component yourself.

 // Make sure you pass in 'this' or whatever Actor or Component as a valid outer object
 UStaticMeshComponent* CreatedComponent = NewObject<UStaticMeshComponent>(this, UStaticMeshComponent::StaticClass());
 // Example of setting some properties
 CreatedComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision);
 CreatedComponent->SetWorldTransform(ComponentTransform);
 CreatedComponent->SetStaticMesh(Mesh);
 CreatedComponent->SetCastShadow(bCastShadow);
 // Now, register the component.
 CreatedComponent->RegisterComponent();
 // begin play will be called within RegisterComponent (which calls RegisterComponentWithWorld), you don't need to call it yourself
 
 // This code is running inside of a custom component, so add it to my list of created components, that way we can remove them later if we want
 MyDynamicComponents.Add(CreatedComponent);
avatar image Diddykonga Aug 20 '15 at 08:33 PM

Here is my code for my BP Function.

 //Adds a UActorComponent Subclass, that is based on the passed in Class, and added to the Outer(Actor) object
 UActorComponent* UFSBlueprintFunctionLibrary::AddComponentByClass(TSubclassOf<UActorComponent> Class, UObject* Outer)
 {
     if (Class != nullptr && Outer != nullptr)
     {
         UActorComponent* Component = NewObject<UActorComponent>(Outer, *Class);
         if (Component != nullptr)
         {
             Component->RegisterComponent();
             return Component;
         }
         else
         {
             return nullptr;
         }
     }
 
     return nullptr;
 }

Ive tested in the Actor Blueprint, that the return value is valid, so Component->RegisterComponent() is definitely being called, but BeginPlay is still never getting called for the component.

Edit: After i added the RegisterComponent(), i was then able to call BeginPlay() on the component afterwards, but it still seems like the Actor should call BeginPlay instead of me having to call it myself?

avatar image cancel Aug 20 '15 at 08:42 PM

Is Component->bWantsBeginPlay set to true?

avatar image Diddykonga Aug 20 '15 at 08:51 PM

Yes, ive tried setting it to true, before and after ive called RegisterComponent.

avatar image cancel Aug 20 '15 at 08:56 PM

Hmm, I don't know, then. It definitely has to be true before you call RegisterComponent. But it should call BeginPlay once that happens.

Is the component you're adding one that you've subclassed in C++ yourself? If so, did you override BeginPlay on that subclass? And if so, are you remember to call Super::BeginPlay() within your own definition of BeginPlay?

avatar image Diddykonga Aug 20 '15 at 09:00 PM

The component i am adding is a BlueprintableComponent that inherits from ActorComponent, so no C++ is involved, and added a Call to the Parent:BeginPlay in the component, but that doesnt seem to work either. Maybe its a bug?

avatar image cancel Aug 20 '15 at 09:04 PM

It could be a bug, but it's more likely that we're missing something else. When is UFSBlueprintFunctionLibrary::AddComponentByClass being called?

avatar image Diddykonga Aug 20 '15 at 09:09 PM

Im calling it at the end of the Actors BeginPlay in Blueprints.

II tried calling it, after the Actor had been spawned in (So after it had BeginPlay called on it), and it works now :)

(comments are locked)
10|2000 characters needed characters left

1 answer: sort voted first

Here is an explanation for why this happens. I've marked my own comments with ---- so that they don't get confused with actual comments in the UE4 source:

 void AActor::BeginPlay()
 {
     ensure(!bActorHasBegunPlay);
     SetLifeSpan( InitialLifeSpan );

     ---- At this point in time, bActorHasBegunPlay is false. We get all of
     ---- the existing components that were already a part of this actor,
     ---- and then call BeginPlay() on them.

     TInlineComponentArray<UActorComponent*> Components;
     GetComponents(Components);

     for (UActorComponent* Component : Components)
     {
         if (Component->IsRegistered())
         {
             Component->BeginPlay();
         }
         else
         {
             // When an Actor begins play we expect only the not bAutoRegister false components to not be registered
             //check(!Component->bAutoRegister);
         }
     }

     ---- ReceiveBeginPlay() will cause the BeginPlay Blueprint node to
     ---- fire (if it exists)

     ReceiveBeginPlay();

     ---- You were creating components within the BeginPlay node graph in
     ---- your Blueprint. When a component is executing
     ---- RegisterComponent(), it checks to see if the Actor it is a part
     ---- of has already begun play. If it has already begun play, then the
     ---- component calls BeginPlay() on itself. Otherwise, the component
     ---- does not call BeginPlay() on itself, and waits for the Actor to
     ---- call BeginPlay() on the component when the Actor is executing
     ---- BeginPlay(), which you can see already happened earlier in this
     ---- function.

     bActorHasBegunPlay = true;

     ---- The flag is now set to true, but it's too late -- your components
     ---- created in your Blueprint BeginPlay will never have BeginPlay
     ---- called on them.
     
 }


If you're going to call BeginPlay() in your own code, make sure to first check that bHasBegunPlay on the component is false.

more ▼

answered Aug 20 '15 at 09:21 PM

avatar image

cancel
2.3k 100 70 120

avatar image Diddykonga Aug 20 '15 at 09:29 PM

Thank you for the help, you provided much information and i thank you for that! :)

For all that are interested, here is my final code for my BP Function that works wherever you call it.

 //Adds a UActorComponent Subclass, and adds it to the Outer Actor.
 UActorComponent* UFSBlueprintFunctionLibrary::AddComponentByClass(TSubclassOf<UActorComponent> Class, AActor* Outer)
 {
     if (Class != nullptr && Outer != nullptr)
     {
         UActorComponent* Component = NewObject<UActorComponent>(Outer, *Class);
         if (Component != nullptr)
         {
             if (Outer->HasActorBegunPlay())
             {
                 Component->bWantsBeginPlay = true;
             }
 
             Component->RegisterComponent();
 
             if (!Outer->HasActorBegunPlay())
             {
                 Component->BeginPlay();
             }
 
             return Component;
         }
         else
         {
             return nullptr;
         }
     }
 
     return nullptr;
 }
(comments are locked)
10|2000 characters needed characters left
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