Can't make changes to interface method declarations after implementing them in Blueprints

Hello everyone, i have a question regarding interfaces. Couldn’t find any answers online so i became desperate

BaseLevelManager.h

 
	UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="Interface")
	void LoadMainMenu();

	UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="Interface")
	void UnloadMainMenu();

	UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="Interface")
	void LoadTargetLevel(FString TargetLevel);

	UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="Interface")
	void UnloadTargetLevel(FString TargetLevel);

Here i have 4 functions, but the problem lies in the last 2 with their parameters. Initially i passed ULevel reference as a parameter, then decided to switch to UWorld soft object pointer, it didn’t quite work, same as FString

BaseGamemode.h

UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="Interface")
	void LoadMainMenu();
	virtual void LoadMainMenu_Implementation() override;

	UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="Interface")
	void UnloadMainMenu();
	virtual void UnloadMainMenu_Implementation() override;

	UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="Interface")
	void LoadTargetLevel(FString TargetLevel);
	virtual void LoadTargetLevel_Implementation(FString TargetLevel) override;

	UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="Interface")
	void UnloadTargetLevel(FString TargetLevel);
	virtual void UnloadTargetLevel_Implementation(FString TargetLevel) override;

Don’t know if it’s weird to put interfaces into gamemode for this stuff, and i’m not sure if it’s the best practice for overriding interfaces but it worked perfectly so far in other classes

And here’s the live coding report. What’s weird is that it works perfectly if i switch it back to ULevel reference which led me to believe that the problem is in BP implementation, but at the same time i tried to delete the inherited BP and it still didn’t work.
Sorry if it’s a trivial question and thank you in advance

Try removing override; specifier, as these methods do not override anything.
If this does not work, give me whole .h and .cpp to try and replicate it here on my end.

Thanks for the reply, tried to do as you said but the error remains

BaseGamemode.h

#pragma once

#include "CoreMinimal.h"
#include "BaseLevelManager.h"
#include "GameFramework/GameMode.h"
#include "BaseGamemode.generated.h"

/**
 * 
 */
UCLASS()
class PROJECTREBIS_API ABaseGamemode : public AGameMode, public IBaseLevelManager
{
	GENERATED_BODY()

public:
	UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category="Default")
	TMap<FString, TSoftObjectPtr<UWorld>> LevelsArray;

	UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Default")
	UWorld* CurrentLevel;
	
public:
	UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="Interface")
	void LoadMainMenu();
	virtual void LoadMainMenu_Implementation() override;

	UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="Interface")
	void UnloadMainMenu();
	virtual void UnloadMainMenu_Implementation() override;

	UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="Interface")
	void LoadTargetLevel(TSoftObjectPtr<UWorld> TargetLevel);
	virtual void LoadTargetLevel_Implementation(TSoftObjectPtr<UWorld> TargetLevel);

	UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="Interface")
	void UnloadTargetLevel(TSoftObjectPtr<UWorld> TargetLevel);
	virtual void UnloadTargetLevel_Implementation(TSoftObjectPtr<UWorld> TargetLevel);
};

BaseGamemode.cpp

#include "BaseGamemode.h"

void ABaseGamemode::LoadMainMenu_Implementation()
{
	IBaseLevelManager::LoadMainMenu_Implementation();
}

void ABaseGamemode::UnloadMainMenu_Implementation()
{
	IBaseLevelManager::UnloadMainMenu_Implementation();
}

void ABaseGamemode::UnloadTargetLevel_Implementation(TSoftObjectPtr<UWorld> TargetLevel)
{
	IBaseLevelManager::UnloadTargetLevel_Implementation(TargetLevel);
}

void ABaseGamemode::LoadTargetLevel_Implementation(TSoftObjectPtr<UWorld> TargetLevel)
{
	IBaseLevelManager::LoadTargetLevel_Implementation(TargetLevel);
}

BaseLevelManager.h

#pragma once

#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "BaseLevelManager.generated.h"

/**
 * 
 */
UINTERFACE(BlueprintType)
class UBaseLevelManager : public UInterface
{
	GENERATED_BODY()
};

class IBaseLevelManager
{
	GENERATED_BODY()

public:

	UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="Interface")
	void LoadMainMenu();

	UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="Interface")
	void UnloadMainMenu();

	UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="Interface")
	void LoadTargetLevel(TSoftObjectPtr<UWorld> TargetLevel);

	UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="Interface")
	void UnloadTargetLevel(TSoftObjectPtr<UWorld> TargetLevel);
};

I changed parameters to TSoftObjectPtr in these, previously they were ULevel*

Here’s how it’s implemented in BP

1 Like

Try removing override ; specifier, as these methods do not override anything.
If this does not work, give me whole .h and .cpp to try and replicate it here on my end.

OK so the TSoftObjectPtr cannot be used with UFUNCTION. If you want this to compile, then you must do it in C++ (or use solution in my next message bellow). Otherwise, just remove UFUNCTION line above both Load and Unload methods, and it should work for you. Try it out with TSoftObjectPtr<AActor> and you’ll get the same error result.

	void LoadTargetLevel(TSoftObjectPtr<UWorld> TargetLevel);
	
	void UnloadTargetLevel(TSoftObjectPtr<UWorld> TargetLevel);

Also, you can do it like this, but this is no longer soft pointer, so I would avoid it. I still don’t get it why you would pass in the whole world. Why don’t you unlod and load by WorldName (FString)?

UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="Interface")
void LoadMainMenu();

UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="Interface")
void UnloadMainMenu();

UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="Interface")
void LoadTargetLevel(UWorld* TargetLevel);

UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="Interface")
void UnloadTargetLevel(UWorld* TargetLevel);

If you want to stick to TSoftObjectPtr and UFUNCTION, then you should use const TSoftObjectPtr<UWorld>& TargetLevel instead of TSoftObjectPtr<UWorld> TargetLevel

	UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="Interface")
	void LoadTargetLevel(const TSoftObjectPtr<UWorld>& TargetLevel);
	
	UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="Interface")
	void UnloadTargetLevel(const TSoftObjectPtr<UWorld>& TargetLevel);

This should work for you now.

More generally, the BlueprintNativeEvent / BlueprintImplementableEvent specifiers make the generated code automatically convert any structure parameters to const& variants, causing this sort of error.

So this is also true for FString - change arguments to const FString& and it should work.

2 Likes

Wow, that’s a weird behavior. But it works now, so thank you.
In the end i opted to use FString as a parameter but the weird thing is that it still requires to be const address, which leads me to question how UHT determines interfaces, if you have any knowledge of this i would be happy to hear it
As for removing UFUNCTION macro i could only remove it in the derived class that implements the interface, NOT in the interface (that’s for anyone who’s looking for answers)
And yeah as for why i wanted to use UWorld i have a TMap of all levels that can load but didn’t think of simply using FString to find by key which is honestly my fault

The end result:

BaseLevelManager.h

#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "BaseLevelManager.generated.h"

/**
 * 
 */
UINTERFACE(BlueprintType)
class UBaseLevelManager : public UInterface
{
	GENERATED_BODY()
};

class IBaseLevelManager
{
	GENERATED_BODY()

public:

	UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="Interface")
	void LoadMainMenu();

	UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="Interface")
	void UnloadMainMenu();

	UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="Interface")
	void LoadTargetLevel(const FString& TargetLevel);

	UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="Interface")
	void UnloadTargetLevel(const FString& TargetLevel);
};

BaseGamemode.h

UCLASS()
class PROJECTREBIS_API ABaseGamemode : public AGameMode, public IBaseLevelManager
{
	GENERATED_BODY()

public:
	UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category="Default")
	TMap<FString, TSoftObjectPtr<UWorld>> LevelsArray;

	UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Default")
	UWorld* CurrentLevel;
	
public:
	UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="Interface")
	void LoadMainMenu();
	virtual void LoadMainMenu_Implementation() override;

	UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="Interface")
	void UnloadMainMenu();
	virtual void UnloadMainMenu_Implementation() override;

	void LoadTargetLevel(const FString& TargetLevel);
	virtual void LoadTargetLevel_Implementation(const FString& TargetLevel);

	void UnloadTargetLevel(const FString& TargetLevel);
	virtual void UnloadTargetLevel_Implementation(const FString& TargetLevel);
};
1 Like

Thank you, i wrote my reply before noticing your answer.
That actually explains a lot, unreal can be real frustrating sometimes…