informa
3 min read
article

UE4Cookery CPP001: Injecting subobjects with FObjectInitializer

Topic: Injecting subobjects with FObjectInitializer Source for UE4.23: https://github.com/klauth86/UE4Cookery/tree/main/CPP001

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?

Latest Jobs

Treyarch

Playa Vista, California
6.20.22
Audio Engineer

Digital Extremes

London, Ontario, Canada
6.20.22
Communications Director

High Moon Studios

Carlsbad, California
6.20.22
Senior Producer

Build a Rocket Boy Games

Edinburgh, Scotland
6.20.22
Lead UI Programmer
More Jobs   

CONNECT WITH US

Register for a
Subscribe to
Follow us

Game Developer Account

Game Developer Newsletter

@gamedevdotcom

Register for a

Game Developer Account

Gain full access to resources (events, white paper, webinars, reports, etc)
Single sign-on to all Informa products

Register
Subscribe to

Game Developer Newsletter

Get daily Game Developer top stories every morning straight into your inbox

Subscribe
Follow us

@gamedevdotcom

Follow us @gamedevdotcom to stay up-to-date with the latest news & insider information about events & more