informa
3 min read
article

UE4Cookery CPP009: Referencing assets for C++ lovers

Topic: Referencing assets for C++ lovers Source for UE4.26: https://github.com/klauth86/UE4Cookery/tree/main/CPP009

Many developers prefer to move all logic that they can in C++ to gain more control and optimization. However, this can cause another problem and that problem is referencing assets. For example, one can face it while refactoring UMG components to custom Slate widgets. Of course, referencing can be done by creating static class, that will be a container for asset path names. But in this case renaming and assets dependencies are out, so there must be a more handy way!

Suppose, that we have many different fonts, button styles, textures and so on, imported and designed for our UI. If all our widgets are in C++ (custom Slate widgets), then to apply that suff we need a way to reference them in code. How can we solve that? First of all, we can create custom asset

 

UIData.h

#pragma once

#include "UObject/NoExportTypes.h"
#include "Fonts/SlateFontInfo.h"
#include "UIData.generated.h"

UCLASS()
class CPP009_API UUIData : public UObject {

	GENERATED_BODY()

public:

	const FSlateFontInfo& GetMenuFontInfo() const { return MenuFontInfo; }

	const FSlateFontInfo& GetGameFontInfo() const { return GameFontInfo; }

protected:

	UPROPERTY(EditDefaultsOnly, Category = "UI Data: Menu Font Info")
		FSlateFontInfo MenuFontInfo;

	UPROPERTY(EditDefaultsOnly, Category = "UI Data: Game Font Info")
		FSlateFontInfo GameFontInfo;
};

 

Ok, so that is our UI Data asset. To be able to create it right in Editor we need to add Editor module to our project and create two classes based on UFactory and UAssetTypeActions_Base. After that is done, we can create UI Data asset right in Content folder

Playing with asset in Editor

 

So, we have referenced all stuff around UI and that is fine, but how can we reference UI Data asset itself? Let see. We can create another object class and that will be

 

#include "GameDataSingleton.h"

#pragma once

#include "UObject/NoExportTypes.h"
#include "GameDataSingleton.generated.h"

class UUIData;

UCLASS(Blueprintable)
class CPP009_API UGameDataSingleton : public UObject {

	GENERATED_UCLASS_BODY()

public:

	UUIData* GetUIData() const { return MyUIData; }

protected:

	UPROPERTY(EditDefaultsOnly)
		UUIData* MyUIData;

public:

	static UGameDataSingleton* GetInstance() { 
		static UGameDataSingleton* instance = Cast<UGameDataSingleton>(GEngine->GameSingleton);
		return instance;
	}
};

UGameDataSingleton::UGameDataSingleton(const FObjectInitializer& ObjInitializer) :Super(ObjInitializer) {}

 

Here we have created static function to access singleton instance, however we don't create it by our selves, rather we are trying just to pick it up from GEngine->GameSingleton. We can create blueprint around UGameDataSingleton and fill all props in it, lets name it BP_GameDataSingleton. To make it all happen to work, there is only one step left - we need to change Project Settings:

Project Settings

It will be Engine, who will create UGameDataSingleton by provided class and will store it in GEngine->GameSingleton.

 

For demonstration purposes we can create Slate widget in project Game mode class and set some of widget props by accessing UGameDataSingleton:

 

CPP009GameModeBase.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "CPP009GameModeBase.generated.h"

class STextBlock;

UCLASS()
class CPP009_API ACPP009GameModeBase : public AGameModeBase
{
	GENERATED_BODY()
	
public:

	virtual void BeginPlay() override;

	virtual void EndPlay(EEndPlayReason::Type reason) override;

protected:

	TSharedPtr<STextBlock> MyTextBlock;
};

 

CPP009GameModeBase.cpp

#include "CPP009GameModeBase.h"
#include "Kismet/GameplayStatics.h"
#include "Slate/SGameLayerManager.h"
#include "Slate/SceneViewport.h"
#include "Widgets/Text/STextBlock.h"
#include "GameDataSingleton.h"
#include "UIData.h"

void ACPP009GameModeBase::BeginPlay() {
	Super::BeginPlay();

	auto uiData = UGameDataSingleton::GetInstance()->GetUIData();

	if (auto controller = UGameplayStatics::GetPlayerController(this, 0)) {
		auto localPlayer = Cast<ULocalPlayer>(controller->Player);
		if (localPlayer && localPlayer->ViewportClient) {
			auto layerManager = localPlayer->ViewportClient->GetGameLayerManager();
			if (layerManager.Get()) {
				layerManager->AddWidgetForPlayer(localPlayer, SAssignNew(MyTextBlock, STextBlock).Text(FText::FromString("Hello, Game Data Singleton!")).Font(uiData->GetMenuFontInfo()), 0);
			}
		}
	}
}

void ACPP009GameModeBase::EndPlay(EEndPlayReason::Type reason) {
	Super::EndPlay(reason);

	if (MyTextBlock.IsValid()) {	
		if (auto controller = UGameplayStatics::GetPlayerController(this, 0)) {
			auto localPlayer = Cast<ULocalPlayer>(controller->Player);
			if (localPlayer && localPlayer->ViewportClient) {
				auto layerManager = localPlayer->ViewportClient->GetGameLayerManager();
				if (layerManager.Get()) {
					layerManager->RemoveWidgetForPlayer(localPlayer, MyTextBlock.ToSharedRef());
				}
			}
		}
		MyTextBlock.Reset();
	}
}

 

Finally, we have

Hello, Game Data Singleton!

Latest Jobs

Studio Pixanoh LLC

Los Angeles, California
05.20.22
Combat Designer

Treyarch

Playa Vista, California or Vancouver, BC
05.20.22
AI Engineer

Question

Remote
05.20.22
Lead Level Designer (South Park)

Remedy Entertainment

Helsinki Metropolitan Area, Finland
05.23.22
Rigging Artist
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