ImplementsInterface() fails with nested inheritance. Is there a work around?

I have a pure virtual interface IResettable:

	UINTERFACE(MinimalAPI, Blueprintable)
	class CHRONO_API UResettable : public UInterface
	{
		GENERATED_BODY()
	};

	class CHRONO_API IResettable
	{
		GENERATED_BODY()

	public:
		virtual void setReset() = 0;
	};

and 2 other interfaces that derive from it, IPausable and ISpeedable.

UINTERFACE(MinimalAPI, Blueprintable)
class CHRONO_API UPausable : public UInterface
{
	GENERATED_BODY()
};

class CHRONO_API IPausable : public IResettable
{
	GENERATED_BODY()

public:
	virtual void setPause() = 0;
};

The logic is that an actor that be paused or sped-up (some property of the game i’m developing), must also be able to be reset back to its normal state. By nesting the interfaces, the actor has no need to inherit IResettable, he can just inherit IPausable and/or ISpeedable and implement all the necessary methods (you can’t even compile the code if you forget to implement setReset().

UCLASS()
class CHRONO_API ABoxEntity : public AActor, public IPausable
{
	GENERATED_BODY()

public:
	void setPause() override;
	void setReset() override;
};

Now, the issue comes on another part of the code. Some raycast hits one of these actors, and I want to check if they are IResettable, and from there, call setReset(). The code I use to detect if the Actor is IPausable works fine.

if (hit.GetActor()->GetClass()->ImplementsInterface(UPausable::StaticClass()))
{
	if (IPausable *pausable_actor = Cast<IPausable>(hit.GetActor()))
	{
		pausable_actor->setPause();
	}
}

But the same code for IResettable does not work, and I think it’s because IResettable is a nested inheritance and neither ImplementsInterface nor Cast search for it.

if (hit.GetActor()->GetClass()->ImplementsInterface(UResettable::StaticClass()))
{
	if (IResettable *resettable_actor = Cast<IResettable>(hit.GetActor()))
	{
		resettable_actor->setReset();
	}
}

So, is this a design flaw from my side? Am I missing something on the interface declarations? Or is it a bug in ImplementsInterface? I tried to provide an MVCE, but let me know if you need additional info.

Maybe you forgot to inherit from UResettable?

Try

Pausable.h

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

#pragma once

#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "Resettable.h"
#include "Pausable.generated.h"

// This class does not need to be modified.
UINTERFACE(MinimalAPI)
class UPausable : public UResettable
{
	GENERATED_BODY()
};

/**
 * 
 */
class INTER_INHERITANCE_API IPausable : public IResettable
{
	GENERATED_BODY()
		
public:
	virtual void setPause(){}		
};


Resetable.h

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

#pragma once

#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "Resettable.generated.h"

// This class does not need to be modified.
UINTERFACE(MinimalAPI)
class UResettable : public UInterface
{
	GENERATED_BODY()
};

/**
 * 
 */
class INTER_INHERITANCE_API IResettable
{
	GENERATED_BODY()

public:

	virtual void setReset() {}
};

boxEntity.h

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

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Pausable.h"
#include "BoxEntity.generated.h"

UCLASS(Blueprintable)
class INTER_INHERITANCE_API ABoxEntity : public AActor, public IPausable
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	ABoxEntity();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	void setPause() override;

	void setReset() override;

	UFUNCTION(BlueprintCallable)
	void hitTest();

	class USphereComponent* collision;

	FVector Start;
	FVector End;

	UPROPERTY(EditAnywhere,BlueprintReadWrite)
	float radius;

};

BoxEntity.cpp

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


#include "BoxEntity.h"
#include "DrawDebugHelpers.h" 
#include "Components/SphereComponent.h"
// Sets default values
ABoxEntity::ABoxEntity()
{
 	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

	collision = CreateDefaultSubobject<USphereComponent>(TEXT("Sphere collision"));
	collision->SetupAttachment(GetRootComponent());
	collision->SetSphereRadius(radius);
	collision->SetCollisionResponseToChannel(ECollisionChannel::ECC_WorldDynamic, ECollisionResponse::ECR_Block);	
	hitTest();
}

// Called when the game starts or when spawned
void ABoxEntity::BeginPlay()
{
	Super::BeginPlay();
	hitTest();
}

// Called every frame
void ABoxEntity::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
	hitTest();	
}

void ABoxEntity::setPause()
{
}

void ABoxEntity::setReset()
{
}

void ABoxEntity::hitTest()
{
	Start = GetActorLocation();
	End = GetActorLocation() + (GetActorLocation() + GetActorForwardVector() * 1400);


	FHitResult hit;
	
	FCollisionQueryParams qp;
	qp.bDebugQuery = true;
	qp.bTraceComplex = true;
	
	FCollisionResponseParams colResponse;	

	DrawDebugSphere(GetWorld(), End, 20, 12, FColor::Orange, false, 22222222, 0, 2);

	GetWorld()->LineTraceSingleByChannel(hit, Start, End, ECollisionChannel::ECC_WorldDynamic, qp, colResponse);
	if (IsValid(hit.GetActor())) 
	{
		
		DrawDebugSphere(GetWorld(), hit.Location, 12, 12, FColor::Red, false, 22222222, 0, 2);				
		if (hit.GetActor()->GetClass()->ImplementsInterface(UPausable::StaticClass()))
		{
			if (IPausable* pausable_actor = Cast<IPausable>(hit.GetActor()))
			{
				GEngine->AddOnScreenDebugMessage(-1, 1, FColor::Cyan, hit.GetActor()->GetName() + " is IPausable");
				pausable_actor->setPause();
			}
		}

		if (hit.GetActor()->GetClass()->ImplementsInterface(UResettable::StaticClass()))
		{
			if (IResettable* resettable_actor = Cast<IResettable>(hit.GetActor()))
			{
				GEngine->AddOnScreenDebugMessage(-1, 1, FColor::Cyan, hit.GetActor()->GetName() + " is IReset");
				resettable_actor->setReset();
			}
		}
	}
}

Here you can see both are found no problem

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