ActorComponent with sub-components

I have a project where I’ve created an Actor that consists of an inner and outer collision sphere. (It’s a VR project, and I blend different hand animations depending on where my controller is in relation to the spheres). I want to turn this into an ActorComponent so that I can attach these objects to other actors. What is the best way to do that?

I tried creating a blueprint based on the ActorComponent, but there’s no viewport, so I don’t see how to easily set up the hierarchy of components. I could use the ChildActor component, but everything I’ve read advises that I generally stay away from that?

So what’s the right way to attach an object like this to other objects…?

1 Like

Are you sure there isn’t blue text near the top of the window to open the full blueprint view? (I haven’t used that type of blueprint in quite a while)

Yes I’m sure

In general, the Unreal Way is not for components to manage sub-components, but instead for Actors to manage components. If you have components that need to talk to each other, make those in an Actor, and then subclass that Actor for whatever other re-use you need to make.

This runs into trouble when you need two separate bases – e g, “YourActor” and “Character” in the same class. You’ll then end up porting all the actor code from YourActor to a custom subclass of Character, that you can then in turn subclass for whatever the desired behavior is.

3 Likes

Well… that’s frustrating. That doesn’t seem to resonate with the object-oriented design paradigm of favoring composition over inheritance.

Surely I’m doing something wrong. I have a little object that has some collision spheres and some functions. I want it to be a component so that other actors can contain one or more of them. Is that really not possible?

1 Like

You can make it a child actor, or you can “harvest components” from the one actor and put them into the second actor.

1 Like

You can use the same method as the constraint component:
Make an actor component. Get a reference to the owning actor. Get the names of the shape components and then the references. Work off the references.

ActorComponent:

Actor:

Result:

If you want your component to include the shapes as well, you can use shape traces per tick in the component, but this may be less performant.


Though, like jwatte said, you would do this in the actor, not the component. Scene components (have transforms) usually only do work on themselves, while actor components (don’t have transforms) modify some property of the actor they are attached to. A simpler method than what I made above is to do what jwatte said: make a base actor class that has the two spheres & the logic already in them. Then in the child classes, allow setting the size of those. My method is only useful if you want to attach them to any actor type without needing a base class, but since you’re only going to be doing this on pickable items, using a base class is easier.

1 Like

Wow, I appreciate the thorough reply! Although… you’ve just created an ActorComponent within the one actor that i’m trying to turn into an ActorComponent. I’m trying to make multiple instances of these components so that they can be attached to other actors.

Which is why inheritance is a no go for something like this. What if I want two of the components attached to one actor? What if I want it to be a base class, so that some instances have two shapes and some have three? etc.

It genuinely sounds like the engine isn’t designed to support this kind of thing, apart from using the ChildActor component (which is advised to be used sparingly).

Inheritance works with this, too: you would just get all components that are shape components, and bind them to an event:

Base Actor Event Graph:

Child (Inherited) Actor Viewport:
image

Result:


This let’s you add as many shapes as you want. The “inner_” & “outer_” determine whether it’s an inner or outer shape & bind it to a different event.

1 Like

But I’d still have to create all the individual shape objects, per actor. Instead of just having a component that encapsulates those things. Like an object. Like an “object oriented” approach.

This is one of the primary reasons for the design principle “Composition over Inheritance”, and I’m surprised to find Unreal is limited in this regard. I guess the Child Actor component is the answer here, but it’s apparently clunky and unreliable.

Is there not a C++ way to do it? Can I not create my own component through code that contains the objects and functions I want?

1 Like

Yes, you can do that. Just duplicate the sphere component and modify it to support more than one sphere. Though, I don’t know if it’s as simple as that since I haven’t seen it. But I’ve made components in C++ before and it’s pretty simple. Edit: Though, I just realized, you’re not going to be able to move them in the viewport since scene components have only one transform. You would have to set their location through the details panel only.

I would just stick with either my or jwatte’s idea; I whipped them both up quickly and they’re easy & straightforward.

I get you want it all contained in one component, but the only types of objects that are going to be using this are pickable ones. Think about it this way: for every new object you create, you’re going to have to add this component to it manually. It would be faster if it came automatically with the object. That’s what a base class is for. Any pickable object inherits from “PickableObject” and comes with the logic for handling the overlap and anything else. For every child actor, all you need to do is add the shapes. Since the shapes will vary per-object, this is a necessary step anyway.

A better example: What if you wanted to animate the shapes? Or attach them to separate components that will be moving? What if you wanted to change the size of the shapes over time. What if a property of the shape is determined by some gameplay element? Put simply: what if the shapes are dynamic. Having them in a single component wouldn’t allow this, but having each shape as a separate component will.

" but the only types of objects that are going to be using this are pickable ones"

…that’s not true. That’s not how I’m planning to use these things. Inheritance is not the right tool for the job here. Seriously, this is a classic problem related to a design paradigm that emerged as object-oriented programming languages evolved.

I’ve already started creating a C++ class based on ActorComponent, it’s definitely how I want to tackle this. Especially since I can just nativize the actor blueprint I’ve already created and bring that logic over to the component.

Can you explain how you’re going to use this, then? Your first post made it seem as you were going to use this for pickable objects since you were using it for blend hand animations.

The first method I posted does this. The logic is in the actor component, and you make the spheres in the actor. You can also spawn new spheres at runtime if that’s what you’re trying to do.

Sure, they’re like spots where I want to blend the animation. I want to be able to attach a variable number of these spots to several actors, with different parameters assigned to each one.

I want a single component that contains the spheres and logic, where the only thing I’m changing are the parameters i’ve exposed.

Then you only need the data (floats, animation sequences, etc.) in the spheres, then put the logic (animation handling) in the hand. Whenever the hand overlaps a sphere, it gets the data from that sphere.

Since you can’t make a child blueprint class from a sphere component, you’ll have to do it in C++, which is simple.

Sorry to revive this topic, but this seems to be exactly what I’m looking for and still after reading all the replies haven’t figured out all my problems.

In my particualr case I’m developing a conversation system. I’ve designed an ActorComponent which you can attach to an NPC and this particular NPC will (for example) bump a message when the player walks nearby. For this kind of logic I need (among other things) a CollisionBox to detect the player.
I don’t want all the NPC’s to have this CollisionBox, only the ones that will have dialogues, so I’m trying to attach it to the Actor(Owner) via de ActorComponent in the Constructor(), with the (what I understood) same result as @Inconceivable: I can’t move the CollisionBox in the viewport (or even in the world) to adapt it’s size to the NPC.
As @midgunner66 explained I could get the NPC base BP to have this CollisionBox and from the ActorComponent side, make the logic, but this seems very confusing and I might have miss understand it.
I dont want every NPC to talk, so I won’t add the ActorComponent, but they will still have the CollisionBox for nothing (apart from the CollisionBox I need other components that will make the hierarchy of the baseNPC a mess)
Should I create TalkingNPC derived from baseNPC for those who talk? But then whats the point of the ActorComponent instead of making all the logic in the Actor?

I’m in UE5.3 and the ActorComponent is created via C++ if that changes anything.

Whoops, haven’t checked my account in a while, sorry!

So yeah I think it’s just kind of a blind spot in the engine’s design. I haven’t found a good way to edit components that are created in other components. You can’t even be sure of the order of component creation to search for it in the component’s OnComponentCreated function. I personally settled for a system of defining the position/scale of the collision shapes in Blender that I have a custom exporter for so I can know the full details of the shapes at component creation time, but it’s annoying that I can’t use the blueprint editor for it, as I have to reexport-reimport every time i make a change.

For your specific case though, maybe if you just add both a collision shape and your ActorComponent to the actors that can talk, and in InitializeComponent your component can search its owner’s components for a UShapeComponent (possibly with a specific name or tag) and then add callbacks like:

shape->OnComponentBeginOverlap.AddDynamic(this, &USpeechComponent::BeginOverlap);
shape->OnComponentEndOverlap.AddDynamic(this, &USpeechComponent::EndSnagOverlap);

It will still be annoying that you have to manually add the shape yourself, but at least the rest of it should work as expected.

1 Like