Download

Creating a sub-ActorComponent inside another ActorComponent

I’m currently attempting to create/spawn ActorComponents from inside another. Let’s call the spawning component A and the spawned components B.
I basically want A to spawn 2 instances of B as it relies on the existence of those instances and I want it to spawn them so you don’t have to keep adding them in manually.
I also want those instances to be replicated and accessible in the blueprint defaults so their properties can be edited so my A header is approx. the following:



class UMyAComponent : public UActorComponent
{
    ....
    UPROPERTY(VisibleDefaultsOnly, Category = Config, Replicated)
    class UMyBComponent BComp1;

    UPROPERTY(VisibleDefaultsOnly, Category = Config, Replicated)
    class UMyBComponent BComp2;
    ....
}


Then I have the lifetime props implemented as



void UMyAComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);

	DOREPLIFETIME_CONDITION(UMyAComponent, BComp1, COND_OwnerOnly);
	DOREPLIFETIME_CONDITION(UMyAComponent, BComp2, COND_OwnerOnly);
}


Then in the CPP file I’ve attempted several different ways to get it to do what I want, sadly to no avail:

  1. Instantiate the components inside the InitializeComponent function of A.
    -> Not available for editing

  2. Instantiate the components inside the constructor of A using CreateDefaultSubComponent.
    -> Not replicated.
    2a. Override ReplicateSubobjects in A to write the Bs to the actor channel.
    -> Game crashes as soon as data has to be replicated to a client.

  3. Instantiate the components inside the constructor of A but with the outer argument not set to ‘this’ but to ‘GetOuter()’.
    -> Rerun the editor and the value will have become stale: “Warning: MyBComponent_1 has an inappropriate outermost, it was probably saved with a deprecated outer.”.

Does anyone have a clue as to solve this problem?

By default Epic only assumes one level of replication. If you look at their replication code for components they always assume the outer of them is the owner actor and not another component. Furthermore, the actor and actorcomponent code that handles registration and initialization only assumes a one level parent-child hiearchy. I attempted what you are doing and found out its not possible out of the box to have components within components networked correctly.

It is possible to have subobjects within a component. I haven’t looked into it for a while, but there is some logic inside the gameplay abilities component that does whats required afaik.

For that exact reason I tried to supply the GetOuter to the ActorComponent I’m spawning.
And the replication is not the main problem. I can get it to replicate just fine. Just not if I also want it to be editable in the defaults.

Basically I have 2 requirements:

  1. Be able to edit the spawned components in BPs.
  2. Replicate the components.

Now the problem is that so far I’ve only been able to do either 1 or 2. Not both at the same time and the ways in which I did this and when I have what problem is what I described above.
Only if I supply ‘this’ as the outer to CreateDefaultSubobject I have a replication problem, which I already expected but tried to solve with ReplicateSubobjects. And I only attempted this because I was originally looking for a way to use GetOuter() instead but failed and needed something else. :frowning:

Ok for now I’ve opted to require the developer to place the depended components himself as I’ve not yet been able to get it to work.
When the abilities system is released I’ll have another look at it. I suspect this component may actually profit from that system anyway.

If someone does know of a solution, I’ll still be happy to hear it though!

Hey I just got hit by the same issue, but for me when I try to set component B to replicate, it crashes the moment I play.
How did you got it to replicate properly?

Was going to try to set the outer of the CreateDefaultSubobject to the GetOwner() and try it out, but there is no such parameter if you trying to do it in the default constructor. Only with the FObjectInitializer does the CreateDefaultSubobject have the outer parameter.

Edit:
Without an FObjectInitializer its possible using NewObject() which has an outer parameter.