Access global ViewModel set in a C++ class from a Blueprint (UMVVMSubsystem)

Hi, I am exploring the plugin UMG ViewModel and I am trying stuff in C++ and Blueprint to learn more about this new and interesting plugin.

I understand this is a bit of a wild question, but maybe someone in the community can help me and spot my error.

So basically, I access the UMVVMSubsystem in C++ in BeginPlay() of my HUD class.

const TObjectPtr<UMVVMSubsystem> ViewModelSubsystem =GEngine->
	GetEngineSubsystem<UMVVMSubsystem>();
check(ViewModelSubsystem);

Then, I access the Global ViewModel Collection.

const TObjectPtr<UMVVMViewModelCollectionObject> GlobalViewModelCollection = ViewModelSubsystem->
	GetGlobalViewModelCollection();
check(GlobalViewModelCollection);

Then, I add set the members of the context (a protected FMVVMViewModelContext in my HUD) and initialize a new ViewModel into the collection.
*Note that UControlsViewModel derives from UMVVMViewModelBase.

// declared earlier in .h file
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Viewmodel")
FMVVMViewModelContext  Context;
...
...
...
Context.ContextName = FName("ControlsViewModel");
Context.ContextType = UControlsViewModel::StaticClass();

GlobalViewModelCollection->AddViewModelInstance(Context, 		
	NewObject<UControlsViewModel>(this, FName("ControlsViewModel")));

Following this, I try to access the Subsystem in a blueprint and using the protected FMVVMViewModelContext structure to recover my global ViewModel, but it does not work and stays "None.

What went wrong?

Edit
OK Jackpot. Got it working on all tries

.h

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

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/HUD.h" 
#include "Types/MVVMViewModelContext.h"
#include "MVVMActor.generated.h"

UCLASS()
class YOUR_API AMVVMActor : public AHUD
{
	GENERATED_BODY()
	
public:	

	AMVVMActor();

protected:

	virtual void BeginPlay() override;

public:	

	virtual void Tick(float DeltaTime) override;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Viewmodel")
		FMVVMViewModelContext  Context;
		
};

.cpp


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


#include "MVVMActor.h"
#include "MVVMSubsystem.h"
#include "ControlsViewModel.h"


AMVVMActor::AMVVMActor()
{

	PrimaryActorTick.bCanEverTick = true;
	Context.ContextName = "CVM";
	Context.ContextClass = UControlsViewModel::StaticClass();
	
}


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

	const TObjectPtr<UMVVMSubsystem> ViewModelSubsystem = GEngine->GetEngineSubsystem<UMVVMSubsystem>();
	check(ViewModelSubsystem)

	const TObjectPtr<UMVVMViewModelCollectionObject> GlobalViewModelCollection = ViewModelSubsystem->GetGlobalViewModelCollection();
	check(GlobalViewModelCollection);
	
	UMVVMViewModelBase* mb = GlobalViewModelCollection->FindViewModelInstance(Context);		
	if(mb == nullptr)
	{
		UControlsViewModel* ucvm =  NewObject<UControlsViewModel>();
				
		bool addedVM = GlobalViewModelCollection.Get()->AddViewModelInstance(Context, ucvm);
		if(addedVM)
			GEngine->AddOnScreenDebugMessage(-1, 1, FColor::Cyan, "Added");
		else
			GEngine->AddOnScreenDebugMessage(-1, 1, FColor::Red, "NOT Added");
	}	
}


void AMVVMActor::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}

The element that was causing errors was the fact that adding name and parameters to NewObject for UControlsViewModel, caused it to not be found.

Once reset to default’s it all started registering ok!.

In engine:

2 Likes

Thanks! You helped me get it working.

There is still a problem for me, I wonder if you have it as well. The very first time I run the project, its still None, but after I run the game on try 2 and beyond, the ViewModel is properly recovered.

I wonder if there is something about Engine Subsystems that I am missing which would lead to this issue.

Bests,

Are you setting Context.ContextName & Context.ContextClass in the CDO or maybe on begin play?

If on begin play that would explain why it’s not found the first time as it would be set too late in the objects life cycle.

I’m setting it up in the Constructor of cppHUD.

But I have found a solution which is to add a Delay until Next Tick as the very first action in my Begin Play of my bpHUD.

I wonder why it appears that the subsystem needs an extra delay to properly initialize itself, but at least I found a “fix” which is really not costly.

Maybe because the BP code I shared was in the bpHUD that is built around the cppHUD who itself deals with this engine subsystem?

I added the same delay in my first tests, but later on removed it once things started working.

Maybe the larger the level / more strain on the system the slower the load time for the subsystem?

This is my assumption as well, since this is the first time handling an Engine subsystem, unless a staff can actually clarify things. I only dealt with GameInstance subsystems before and never had this problem.

You could call a BlueprintImplementableEvent from c++ when it’s ready instead of begin play, or make a delegate can broadcast once it’s ready and all classes listening in would then call their event to access the subsystem.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.