Gamasutra is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Gamasutra: The Art & Business of Making Gamesspacer
View All     RSS
July 2, 2022
arrowPress Releases
If you enjoy reading this site, you might also want to check out these UBM Tech sites:


 

UE4Cookery CPP001: Injecting subobjects with FObjectInitializer

by Artur Kh on 03/01/21 10:19:00 am

The following blog post, unless otherwise noted, was written by a member of Gamasutra’s community.
The thoughts and opinions expressed are those of the writer and not Gamasutra or its parent company.

 

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?


Related Jobs

PerBlue
PerBlue — Madison, Wisconsin, United States
[07.01.22]

Unity Gameplay Developer (remote)
PerBlue
PerBlue — Madison, Wisconsin, United States
[07.01.22]

Senior Back-End Engineer (remote)
Build a Rocket Boy Games
Build a Rocket Boy Games — Edinburgh, Scotland, United Kingdom
[07.01.22]

Senior Animation Programmer
Build a Rocket Boy Games
Build a Rocket Boy Games — Edinburgh, Scotland, United Kingdom
[07.01.22]

Senior Core Systems Programmer





Loading Comments

loader image