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?

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.

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.

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);

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?

Is Component->bWantsBeginPlay set to true?

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

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?

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?

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

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 :slight_smile:

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.

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

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;
}