Sponsored By

UE4Cookery CPP001: Injecting subobjects with FObjectInitializer

Topic: Injecting subobjects with FObjectInitializer

Source for UE4.23: https://github.com/klauth86/UE4Cookery/tree/main/CPP001

Artur Kh, Blogger

March 1, 2021

3 Min Read

When you are a noob and you have just started developing your own project, you probably want to use existing engine content as mush as possible. However, sometimes here rises dilemma between usage of existing things and trying to adapt them to your project needs. One frequent case of that sort is dealing with Actor's subobjects.

Assume for example, that you want to use certain engine class, but in the same time you need to replace one or more of it's components by your own versions. In several past months I have faced this situation twice and my solution was simple and straightforward - I just took this Actor class code and recreated it as a new CustomActor class, replacing all stuff by hands. This approach gives very clean result with small things, but it quickly turns to nightmare and "engine in engine" if you try to walk with it through all such cases. All these problems can be resolved better. Let us try some example!

Let's say, we want to use our own braking logic when moving NPCs with Behavior Trees. Two classes that one can find connected with AI movement are UPathFollowingComponent and AAIController. We can easily create child classes from them - UMyPathFollowingComponent and AMyAIController. The next step to replace UPathFollowingComponent of Super class with newly created UMyPathFollowingComponent in AMyAIController. When we will try to implement this step we will shortly see something like

 

Constructor in AIController.cpp:


AAIController::AAIController(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	bSetControlRotationFromPawnOrientation = true;
	PathFollowingComponent = CreateDefaultSubobject<UPathFollowingComponent>(TEXT("PathFollowingComponent"));
	PathFollowingComponent->OnRequestFinished.AddUObject(this, &AAIController::OnMoveCompleted);

	ActionsComp = CreateDefaultSubobject<UPawnActionsComponent>("ActionsComp");

	bSkipExtraLOSChecks = true;
	bWantsPlayerState = false;
	TeamID = FGenericTeamId::NoTeam;

	bStopAILogicOnUnposses = true;
}

 

So, there is a subscription to OnRequestFinished... We can think, that all that is needed - to unsubscribe from this subscription, create our subobject and at last subscribe again. But

 

Somewhere in AIController.h:


private:

	/** Component used for moving along a path. */
	UPROPERTY(VisibleDefaultsOnly, Category = AI)
	UPathFollowingComponent* PathFollowingComponent;

 

This member is private and we can access it only through getter and setter. As result, our AMyAIController constructor code promises to become really sloppy! By far better way for us - to use FObjectInitializer and as you can see this will be equally nice and effective:

 

MyAIController.h:


// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "AIController.h"
#include "MyAIController.generated.h"

UCLASS()
class CPP001_API AMyAIController : public AAIController {

	GENERATED_UCLASS_BODY()	
};

 

MyAIController.cpp:


// Fill out your copyright notice in the Description page of Project Settings.


#include "MyAIController.h"
#include "MyPathFollowingComponent.h"

AMyAIController::AMyAIController(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer.SetDefaultSubobjectClass<UMyPathFollowingComponent>(TEXT("PathFollowingComponent"))) {}

 

MyPathFollowingComponent.h:


// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "Navigation/PathFollowingComponent.h"
#include "MyPathFollowingComponent.generated.h"

UCLASS()
class CPP001_API UMyPathFollowingComponent : public UPathFollowingComponent {

	GENERATED_BODY()
	
	virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
};

 

MyPathFollowingComponent.cpp:


// Fill out your copyright notice in the Description page of Project Settings.


#include "MyPathFollowingComponent.h"

void UMyPathFollowingComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) {
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

	UE_LOG(LogTemp, Warning, TEXT("I am MyPathFollowingComponent!"));
}

 

And that's it! UMyPathFollowingComponent class was injected and created in Super class constructor. We can simply check that out by adding NPC to default level and providing it with controller:

 

CPP001_01 Picture

 

PS. Maybe someone can help me to set up background color for code samples?

Read more about:

Blogs

About the Author(s)

Daily news, dev blogs, and stories from Game Developer straight to your inbox

You May Also Like