Download

how can I override a subobject from a base class?

Hello,
I would like to know how to properly override subobjects from a base class. Let’s say I create a child class that overrides from ACharacter, the class will come with a MovementComponent, however if I have my own created movement component, how can I override the one that comes from the ACharacter class?

In the shooter example, it is done using the ObjectInitializer in the constructor:


AShooterCharacter::AShooterCharacter(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer.SetDefaultSubobjectClass<UShooterCharacterMovement>(ACharacter::CharacterMovementComponentName))

it uses ObjectInitializer.SetDefaultSubObjectClass to do it.

However ObjectInitializers have had their functionality moved directly to the UObject class since 4.22 and SetDefaultSubobjectClass is not present inside UObject.

What is the correct way of doing this now?

Thanks!

Actually this is still the correct way to do it from what I understand.

It looks like you can choose to either have your constructor take const FObjectInitializer& ObjectInitializer as a param or not. I haven’t seen any warnings when I use ObjectInitializer in my constructors and am still doing it regularly. I’m not aware of it being deprecated or anything like that.

Our code base is full of doing it this way. If there’s a more correct way to do it I’d be interested in knowing too.

This thread talks a little more about it. I don’t get any warnings either, and actually ShooterExample uses it and is fully compatible with the newest version of Unreal. Seems like the functionality to override components hasn’t been passed to UObjects, I’m just going to start using ObjectInitializers again.

Oh I see. I think the override components thing still needs to be done with ObjectInitializers. I’m not sure how else to do it. But otherwise I never use Object Initializers either. I use
CreateDefaultSubobject to add a component to an actor, and use ObjectInitializers to override the subclass of a component in a subclass of some actor. And I don’t think there’s a way to do that at all in Blueprint which sucks.

This is frustrating though. I actually went through a lot of my code to remove ObjectInitializer.

I didn’t think this would be a problem but if you have an inheritance hierarchy like this:

ACharacter()

AMyCharacter()

AMySecondCharacter(const FObjectInitializer& ObjectInitializer)

AMySecondCharacter can’t call Super(ObjectInitializer) since AMyCharacter’s constructor doesn’t take it. I kinda assumed there was some magic going on that allowed both constructors. Seems like if it’s an epic class, calling either super method works, but on subclasses, if you stop using FObjectInitializer, a subclass can’t use it any more. They actually write their constructor like this, so either one works:
ACharacter(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get())

const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()

So if you need to override a default subobject class in some subclass, all classes along the way have to use FObjectInitializer.

It seems like it could be a good practice to always do this for actors in case you need to override a component class.

I did some digging into the source. The closest thing I found is somehow calling:

FObjectInitializer::Get().SetDefaultSubobjectClass<USFCharacterMovementComponent>(ACharacter::CharacterMovementComponentName)

But this has to run before the super constructor is called, and the only way I found to do that is to pass up the ObjectInitializer in the constructor.

Internally, when you call CreateDefaultSubobject, this calls FObjectInitializer::Get() and gets the current overrides.

If you call SetDefaultSubobjectClass in the constructor of a subclass it fails saying this can only happen in the base class, which makes sense. It would work this way if you continued to pass ObjectInitializer to the constructor like it used to.

In fact it seems like there are other functions like DoNotCreateDefaultSubobject that require ObjectInitializer. I’m not even sure it’s possible to do these things without ObjectInitializer due to how C++ works, so why they removed it, I’m not sure. It kinda breaks things if any classes along the way don’t take ObjectInitializer in their constructor.