Init for ActorComponents and Owning Actors not yet spawned.

I know same stuff (in different flavor) exists in Blueprints (famous delay 2.0 some years ago).
But now i have problem with ActorComponents, needing to initialize on class init or begin play.

However problem is that actor is created after components, so component for eg. cannot search actor for anything when is created. Yes i can make delegate in actor that component assigns to, and does init when actor triggers it.

However this whole thing defeats purpose of using components, i want them independent from actor they are placed on.

For eg. i want MeshManager_ActorComponent. that searches owning actor for mesh with tag, and changes its material.

Cannot do it on component begin play, it works after famous 2.0f seconds just fine.
However then what? Do i keep on tick looking for mesh component and changing it again and again every tick?

Yes bool varialbe bActorReady? but such code is triggering me.

Are there any nice solutions to this, that do not require tick, and are executed only once?
I see editor can do it, when i expose tag in editor, it is set up at component begin play.
However tag was just example, there are more things (like finding StaticMesh component and storing reference to it, again cannot do it until actor loads, and i cannot set it from blueprints)

i done this, but it really triggers me:

	float CurrentTime = GetWorld()->GetTimeSeconds();
	if (CurrentTime >= LastSlowTick + 0.5f)
	{
		if (!bAllReady)
		{
			DelayedBeginPlay();
		}
		
		LastSlowTick = GetWorld()->GetTimeSeconds();
		SlowTick();
	}
}

void  UPlanetManagerComponent::SlowTick() const
{
	UE_LOG(LogTemp, Error, TEXT("slow tick."));
}

void UPlanetManagerComponent::DelayedBeginPlay()
{
	bAllReady = true;
	UE_LOG(LogTemp, Error, TEXT("DELAYED BEGIN PLAY"));
	BodyMesh = FindOwnerMesh();
}

OMG WTF I did almost exactly this JUST TODAY!!!

Yep

Same

Samee

WTF I WAS ALSO STORING TAGGED STATIC MESH COMPONENTS OF THE ACTOR THAT MY ACTOR COMPONENT IS LISTENING TO :open_mouth: :open_mouth: :rofl: :rofl:

Me too :joy: :joy:

Dude believe me I’ve spent the ENTIRE DAY thinking about how to optimize this. I tried a BUNCH of different approaches, compared their performance implications, all that.

Key detail though, because I mainly used the actor component for organization purposes, this wasn’t an issue in my case:

Well yeah, we don’t want the actor to know about the component, but if the component also doesn’t know it, how can it detect when a certain function in it is triggered? Without doing that, you can just bind a function to be called every time any actor is spawned and in there filter with if statements. But then, you’re very obviously defining which guys should be searched for and sounds like you don’t want that, plus it’s not really optimal for that function to be unnecessarily called every time an actor’s spawned right?

Yes to both, but used delegates :confused: Hope it’s not a dealbreaker, cuz really the more I dove into it I just complicated things without having any gain. Came to realize that they’re already very efficient!

Also, I had another actor component responsible of spawning the actors have a soft pointer ref and a static class ref for caching, for like no reason but you can perhaps utilize that to achieve something similar to “adding the generic component to actors”. Again, this too didn’t need to be an actor, what I mean was using whatever you use as a manager to dynamically add “owning” actors.

Quoted “owning” because in my case they didn’t have my other actor component (different from the one above, it’s the one in question all along, in my case it didn’t own the actors with the tagged static mesh comps to be stored, and that’s also what I’m suggesting, so it too doesn’t need to be actor component in your case as well). And you already know, had delegates to trigger the function that’s gonna store the new instances.

So at the end it looked something like this:

The Actor:

.h:

// #include...

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMyActorUpdateDelegate, AMyActor*, MyActor);

// ...

public:

	static FMyActorUpdateDelegate OnMyActorSet;

.cpp:

void AMyActor::MyActorSet() // in my case was triggered when a TL was completed
{
    OnMyActorSet.Broadcast(this);

}

The Actor Component:

.h:

#include "MyActor.h"

// ...

protected:	

	UFUNCTION()
	void MyActorUpdate(AMyActor* MyActor);

	TArray<UStaticMeshComponent*> MeshCompArr;

.cpp:

void UMyActorComponent::BeginPlay()
{
    AMyActor::OnMyActorSet.AddDynamic(this, &UMyActorComponent::MyActorUpdate);

}

// ...

void UMyActorComponent::MyActorUpdate(AMyActor* MyActor)
{
    for (UActorComponent* MeshComp: MyActor->GetComponentsByTag(UStaticMeshComponent::StaticClass(), FName("MeshComp")))
    {
        MeshCompArr.Add(Cast<UStaticMeshComponent>(MeshComp));

    }

}

I probably didn’t show you anything new, you already mentioned. It was just SOOOO UNBELIEVABLY SURPRISING to see how we were working on such a similar thing today and wanned to share what I came up with! Anyways it’s very late, and I’m very tired.

Hope this can help! :smiling_face_with_three_hearts:

Me to entire day, and i ditched whole component thing.
I wanted code to be simpler not more complex.

I wanted it to be independent, get owner class, find mesh check if it has TAG and act on it. For testing it was tag.green tag.red etc. Check actor tag, apply correct instanced material.

Then you’re up to Tick, unfortunately. Cuz you need to know the class you wanna listen to. Maybe you can have a parent class for all of your spawnable actors and have a delegate there and sort of achieve what you’re looking for :thinking:


Oh and this, almost forgot:

I did what i wanted, solution is Interface:

#pragma once

#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "CPP_BeginPlay_Interface.generated.h"

UINTERFACE(MinimalAPI)
class UCPP_BeginPlay_Interface : public UInterface
{
	GENERATED_BODY()
};

class SCIFI_SPACE_API ICPP_BeginPlay_Interface
{
	GENERATED_BODY()

public:
	
	virtual void OwnerActorIsReady() = 0;
};

in my ActyorComponent .h :

virtual void OwnerActorIsReady() override;

in my ActyorComponent .cpp :

void UCPP_BodyManager_ActorComponent::OwnerActorIsReady()
{
	AActor* OwnerActor = GetOwner();
	FString OwnerName = OwnerActor ? OwnerActor->GetName() : TEXT("Unknown Actor");
	FString ComponentName = GetName();
	
	UE_LOG(LogTemp, Error, TEXT(">>>>>>>>>>>>>>>>>>>>> My Owner is Alive! <<<<<<<<<<<<<<<<<<<<<<<<"));
	UE_LOG(LogTemp, Error, TEXT("Owner: %s, Component: %s"), *OwnerName, *ComponentName);
}

and in actor call this at begin play or when you feel it is ready for sure:

void ACPP_CelestialBody::NotifyComponentsOwnerIsReady()
{
	TArray<UActorComponent*> Components;
	GetComponents(Components);

	for (UActorComponent* Comp : Components)
	{
		if (ICPP_BeginPlay_Interface* Interface = Cast<ICPP_BeginPlay_Interface>(Comp))
		{
			Interface->OwnerActorIsReady();
		}
	}
}

this way ALL component with implemented interface can be notified.

but then, all this code so my actor can trigger some silly function on itself.
Easier is just implementing it in actor. or in function library

Oh and one more discovery:

with this, checking on Begin Play in actor:

// Called when the game starts or when spawned
void ACPP_CelestialBody::BeginPlay()
{
	Super::BeginPlay();
	NotifyComponentsOwnerIsReady();
}

No components have tag that is set in C++

LogTemp: Error: >>>>>>>>>>>>>>>>>>>>> My Owner is Alive! <<<<<<<<<<<<<<<<<<<<<<<<
LogTemp: Error: Owner: BP_CelestialBody_C_0, Component: BodyManagerComponent
LogTemp: Error: Checking: Root
LogTemp: Component: Root does not have tag. 
LogTemp: Error: Checking: RootVisual
LogTemp: Component: RootVisual does not have tag. 
LogTemp: Error: Checking: RootGui
LogTemp: Component: RootGui does not have tag. 
LogTemp: Error: Checking: BodyMesh
LogTemp: Component: BodyMesh does not have tag. 
LogTemp: Error: Checking: 3dWidget
LogTemp: Component: 3dWidget does not have tag.

however with this, using int of component class:

ACPP_CelestialBody::ACPP_CelestialBody()
{
	PrimaryActorTick.bCanEverTick = true;
	// adding all components here and setting their tags like:
	RootGui = CreateDefaultSubobject<USceneComponent>(TEXT("RootGui"));
	if (RootGui)
	{
		RootGui->SetupAttachment(RootComponent);
		RootGui->ComponentTags.Add(FName(K2G_MANAGE_ROOT_WIDGET));
	}
	NotifyComponentsOwnerIsReady();
}

all tags are there:


LogTemp: Error: >>>>>>>>>>>>>>>>>>>>> My Owner is Alive! <<<<<<<<<<<<<<<<<<<<<<<<
LogTemp: Error: Owner: BP_CelestialBody_C_0, Component: BodyManagerComponent
LogTemp: Error: Checking: Root
LogTemp: Component: Root does not have tag. 
LogTemp: Error: Checking: RootVisual
LogTemp: Manage.PlanetRoot in: RootVisual
LogTemp: Error: Checking: RootGui
LogTemp: Manage.WidgetRoot in: RootGui
LogTemp: Error: Checking: BodyMesh
LogTemp: Manage.MyMesh in: BodyMesh

So it looks that blueprint (child class from actor) happily removes all tags set in C++.

and it does not matter what i put first:

Super::BeginPlay();
NotifyComponentsOwnerIsReady();

So is there any way to tell BP_Actor, to keep its dirty paws from variables set in C++ in parent class? Yes EditAnywhere. But cannot set it for TAGS of components.

As it looks now Custom Components in C++ are almost useless (unless they do not care about owning actor, do not interact with it , do not read its variables etc), but then with that all excluded what they are for?

Well kind of not, i made InertialShipMovementComponent and it is great. But cannot make this one that should manage 64bit coordinates rebasing visuals etc.

I finally found solution, but it is nowhere near simple:

  • create that interface with that function “ActorIsReady”
  • in actor create: void ACPP_CelestialBody::PostInitializeComponents()
  • in this post initialize components set all tags (or variables) that you want to be defaults from c++
  • then from PostInitializeComponents call interface function “ActorIsReady” on every component

so TL/DR:
PostInitializeComponents calls interface function, component implements it.

ps
can we has
UPROPERTY(EditAnywhere, BlueprintReadWrite, BlueprintsLeaveBritneyAlone)