Best way to manage well-defined actor functionality consisting of multiple components?

I have a small piece of enclosed functionality, and I’m wondering what are the best practices in handling the data.

The full “thing” would have at least a staticmeshcomponent, an audio component, and a couple of other things. I want to - instead of it being an actor, have it be an actor component that I can then add to actors I want.

But Unreal does not let components have subcomponents or actors add other actors as components. Inheritance does work, but it is limiting - what if I want to add the same functionality to both an actor and a pawn? Because multiple inheritance is not supported, I cannot really use composition here either.

I’m currently torn between two different approaches that I’ve come up with:

  1. Have the component call NewObject for the sound effects and other components it wishes to add. This way, it’s easy to manage all the functionality because it is enclosed within the single component, and very easy to add directly to blueprints as well. The downside of this is the fact that as they are created in the runtime, the additional components are not directly editable within the parent blueprint.

  2. Separately create all the components, functionality in each class I can using CreateDefaulSubobject. The good thing is I can edit all the objects directly within the derived blueprint. The bad thing is that instead of adding a single component to the parent actor, I now add multiple, including their definitions in the header, and I also have to make sure to add the references of the other components to the main subcomponent so that functionality will be properly executed. Use of static functions can help managing this but it is undoubtedly messier on the C++ front than a single-component approach.

I don’t want to use inheritance because I think it creates too many anchors in the design space.

I’m also not sure about the performance costs of doing NewObject once at runtime vs. calling createdefaultsubobject. So far performance has not been a problem but I’m not deep enough to be aware of all the possible nuances.

Not true, friend:
AttachActorToActor
AttachActorToComponent
AttachComponentToComponent… etc

If you are creating them runtime, assign the reference to a variable then manipulate it there.

Composing an actor with component functionality as you outline is what UE5 is pushing as best practice. You echo exactly their thinking on the limitations of inheritance… just dig a bit harder and you are right there.

2 Likes

I suggest taking a look at the Lyra project. Is basically a full game made to standard levels and they have characters that are something similar with what you are asking.

But is not an easy journey to make the project work if you are a beginner like me. Took three days because I have no clue how these advanced c++/blueprints projects work

1 Like

That’s not quite it - those are simply transform attachment relations and aren’t really making the secondary actor part of the first one. More like a child-parent relation in a 3D software. It wouldn’t let me edit the whole resulting object in the blueprint editor.

I’m leaning towards using a static method which creates the subobject components and passes the references in one go. It still ends up with some extra boilerplate in every actor class I’m going to add the functionality to - Instead of being able to spawn one component which would then bring everything it needs with it, all editable in the editor, I need to add the definitions of every component to the headers of the classes I include it to - but at least it lets me edit the components on the editor side of things.

I haven’t used Unity much, but IIRC you could nest prefabs with prefabs so adding a “component” with multiple subcomponents would be much simpler.

I’m not quite sure why components can’t have their own subcomponents or why actors can’t function as components of other actors, but my best guess is that it’s somehow related to how replication is handled.

1 Like

Yes it is a child/parent relationship, but you can access it through the reference you get when spawning the component. You can call GetParentActor/GetParentComponent/GetOwner to have access to the parents. It’s about as “part of the first one” as you’re gonna get with a ‘has a’ relationship you describe.

If you are spawning at runtime, of course you won’t be able to access via the editor, but if you AddComponent via the editor it will create a reference you can use in editor or runtime. If you add the component/actor at runtime just store the reference in a local variable and use that variable to make the calls.

Maybe I don’t understand what you’re getting at, but it seems like you are describing exactly what Unreal already does.

I think option 1 in your initial post is totally doable, I kind of disagree with the second part of that statement as the component is ‘editable’, you just have to store a reference and use that reference to call the functionality.

2 Likes

It’s more about how the actor component is display in the blueprint components list and in the blueprint editor viewport of a derived class that includes the component. Of course you can get the references and add objects, register them as components but that doesn’t help if you want all the editor functionality for the component as well.

It is possible to use a static method, but calling CreateDefaultSubobject everywhere else but the constructor - even when used in a static method that is called in the constructor won’t work - will, at best, display the component in the list but won’t make it editable in the viewport. Using NewObject doesn’t even show the components in the BP editor but that’s obvious, because it’s runtime.

It would be handy if it was possible to add blueprint editor-friendly components for the parent class in an UActorComponent. So, components that need components would could add components to their parent class which would in turn mean that all the actor classes that include the functionality, would need to only add the main component and all subcomponents would be included with it. Alas, this is not possible - again, as far as the ability to edit them in the blueprint editor is concerned.

The only way I can still find that creates editor-friendly components is to build every component in the actor class constructor. And because the CreateDefaultSubobject needs to be called within the parent class, the only remaining left that I can think of to encapsulate the functionality would be a macro.

Actually, there is a feature in the engine that covers this - Child Actor Blueprint / Child Actor Templates. At least that is as close as it gets.

I have thought about this too before.

For example, if I have a weapon model I want my character to hold, I don’t want to specify just a skeletal mesh. A lot of examples have like a UPROPERTY of what the mesh is for a weapon, but what if I want an entire hierarchy of components, like a particle system, a light, sounds, etc…

And yeah, I ended up just creating actual actors and attaching child actors. It gives the most power.

I do that for many things, like even in our game’s decal system. My weapon impact doesn’t just specify some decal texture to use. I specify an entire actor. That way when my weapon impacts, the impact actor takes care of what sound effect to play on impact by having a sound component. It has the decal component and particle effects, and whatever else. If I want it to look like some arrow periced my target on impact it also has a static mesh for an arrow. Maybe there’s even a dynamic light if there’s an explosion or some fire left behind. And the actor can have custom logic added to make different parts of it fade out after some time, or not spawn certain things depending on quality settings.

This way my impact system goes from something like this:

UPROPERTY(EditAnywhere, ...)
UMaterialInterface* DecalMaterial;

UPROPERTY(EditAnywhere, ...)
USoundCueue* ImpactSound;

UPROPERTY(EditAnywhere, ...)
UParticleSystem* ImpactEffect;

UPROPERTY(EditAnywhere, ...)
UStaticMesh* ImpactMesh;

etc... Lights? Looping sounds? Looping particle systems? etc...

It’s now just as simple as specifying which actor to spawn and that actor takes care of all the effects an artist can imagine wanting to spawn.

1 Like

I think the reasons are related to performance. They don’t even recommend going too crazy on components for that same reason.

The child actor blueprints / templates kind of work, but I can see why the feature wasn’t the first one I came across. There is no instance editability whatsoever for the child actor components, which kind of makes the system useless as anything but as some sort of a prefab replacement in a pinch.

Therefore, even if two actors have “SomeSystem” with “CustomComponent, AudioComponent, ParticleSystem”, for the sake of editor editability, instead of making CustomComponent create the AudioComponent and ParticleSystem with NewObject, it’s better to specify all the needed components in the Actor constructors and then pass the references of the additional components to the CustomComponent.

1 Like

Yeah, I do hope I don’t end up in a situation where that becomes bad for performance. One thing I was thinking about is if you’re in the editor, and you’re dragging a bunch of static meshes, particle systems, lights, etc… into your scene, those are all wrapped by an actor.

If the default workflow for a level designer is to fill the world with so many actors for something as simple as placing a mesh, are actors really that bad for performance?

1 Like

Actors aren’t, but actors which have “excessive amount” of components aren’t recommended as each component needs to be serialized etc. So naturally you use all the components you need, but you don’t want to i.e have a building and all the gadgets inside of it be part of the same actor.

Rather what I’m thinking is that if we have something like, an alarm, which has a static mesh, an alarm sound, and a point light, if I want them to be editable in the editor, I need to add all three separately in the editor and then add assign the references, instead of just being able to add the alarm component which then brings with it the other components.

1 Like

I see what you’re saying now. Having a component editor with viewport like the actor that lets you compose a re-usable component that contains all of it’s dependencies. Then being able to tweak that component per-instance in the editor if needed.

Working around this myself, I generally have the components check for dependencies when set up and throw an error if not found. Your solution would be cleaner… would be a good one for the suggestion box.

2 Likes

What makes having multiple actors vs one actor with multiple components more or less efficient? I actually assumed there’s some optimization to be made with merging actors too if you are trying to release a final version of a level. Like in an actor, groups of components can be considered for rendering, shadows, etc… as opposed to each one being indivudually batched.

1 Like

Honestly I cannot admit to possessing any deeper knowledge, but I do know that in some of the Epic training materials it has been recommended to keep object sizes - i.e. quantity of components - in moderation.

There is moderation. From a rendering perspective, for example, having multiple smaller objects would increase the amount of drawcalls. However, it’s not recommended to take everything and turn it all into one big static mesh either, because that causes bottlenecks elsewhere and prevents culling.

If I were to hazard a guess, the recommendation of not creating very large objects with multiple components has to do with replication. If you have a change in the state of an object and communicate that over a network, if that object was large, the network would be stressed out more, and possibly send more data than would be necessary than if the object was smaller.

1 Like

Some things don’t necessarily need to replicate either. Like bullet impact actors can be local only. Especially if the local client has different quality settings and wants to limit how many decals they see versus another player.

Though that could result in weirdness where you shoot a guy in the head and see the blood impact on their head, but the server didn’t actually register the headshot, like I’ve seen so many times in Counterstrike. But that’s kindof evidence to me that even AAA titles sometimes do things this way for the sake of optimizing.

1 Like

It is actually possible to do this. The following code is modified from Add component to actor in C++ (the final word!) - #15 by Curs0

At the top:

#if WITH_EDITOR
#include "UnrealEd.h"
#endif

In your constructor:

	if(GetOwner() == nullptr)
	{
		return;
	}
	UActorComponent* MyBox = GetOwner()->GetComponentByClass(UBoxComponent::StaticClass());
	if(MyBox == nullptr)
	{
		UBoxComponent* BoxComponent = NewObject<UBoxComponent>(GetOwner(), TEXT("NewBoxComponent"));
		if (IsValid(BoxComponent))
		{
			GetOwner()->AddInstanceComponent(BoxComponent);
			BoxComponent->AttachToComponent(GetOwner()->GetRootComponent(), FAttachmentTransformRules::KeepRelativeTransform);
			// deselect/select this actor to update Editor details
			#if WITH_EDITOR
			USelection* Selection = GEditor->GetSelectedActors();
			Selection->Deselect(GetOwner());
			GEditor->SelectActor(GetOwner(), true, true);
#endif
		}	
	}

This will add the box component if you add this custom component to an actor in a level, but not if you add the custom component in the Blueprint Editor. However, if you’re doing this often enough in the Blueprint Editor that it’s too frustrating to add them manually, then you should probably take a look at your program design or at the very least do child classes. I think there is also some macro functionality that will allow you to just add a bunch of stuff with a click of a button, but I haven’t played around with it to know how.

1 Like

A rather satisfactory way I found to do this was just have an editor callable function with spawn actor for all the rest of the “components” and then attach them to the actor. If there is a need for references they can be set as they are spawned. At least this way the whole “bundle” is viewable in the editor rather than once the simulation starts.

There aren’t too many actors which would need an approach like this, so even though it’s not ideal, I found it good enough.

It’s much like the AttachActorToActor mentioned in this thread. But anyway, I wish there was a way to bundle a “bunch of components” together and then have that as something you can assign to actors.

1 Like