Cannot Cast to Actor Component from HUD Class in C++

Here I am trying to make a cpp conversion of the blueprint tut.

//RadarComponent.h
#pragma once

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "RadarComponent.generated.h"


UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class CPPRADARTUTORIAL_API URadarComponent : public UActorComponent
{
	GENERATED_BODY()

public:	
	// Sets default values for this component's properties
	URadarComponent();

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

public:	
	// Called every frame
	virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
	virtual void EndPlay(const EEndPlayReason::Type EndPlayReason);
};
//RadarComp.cpp
#include "RadarComponent.h"
#include "CppRadarHUD.h"
#include "Kismet/GameplayStatics.h"
// Sets default values for this component's properties
URadarComponent::URadarComponent()
{
	// Set this component to be initialized when the game starts, and to be ticked every frame.  You can turn these features
	// off to improve performance if you don't need them.
	PrimaryComponentTick.bCanEverTick = true;

	// ...
}


// Called when the game starts
void URadarComponent::BeginPlay()
{
	Super::BeginPlay();
	
	if (ACppRadarTutorialHUD* Radar = Cast<ACppRadarTutorialHUD>(UGameplayStatics::GetPlayerController(GetWorld(), 0)->GetHUD()))
	{
		Radar->AddToRadar(GetOwner());
	}
	// ...
	
}


// Called every frame
void URadarComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

	// ...
}

void URadarComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
	if (ACppRadarTutorialHUD* Radar = Cast<ACppRadarTutorialHUD>(UGameplayStatics::GetPlayerController(GetWorld(), 0)->GetHUD()))
	{
		Radar->AddToRadar(GetOwner());
	}
}

#pragma once 

#include "CoreMinimal.h"
#include "GameFramework/HUD.h"
#include "CppRadarHUD.generated.h"

UCLASS()
class ACppRadarHUD : public AHUD
{
	GENERATED_BODY()

public:
	ACppRadarHUD();

	/** Primary draw call for the HUD */
	    virtual void DrawHUD() override;
	UFUNCTION(BlueprintCallable)
		void AddToRadar(AActor* Actor);
	UFUNCTION(BlueprintCallable)
		void ClearFromRadar(AActor* Actors);
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "RadarArray")
	    TArray<AActor*> RadarActors;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "2DVector")
	    FVector2D ScreenSize;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "2DVector")
	    FVector2D RadarStartLocation;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Floats")
		float ObjectDistanceScale = 25.0f;
	UFUNCTION()
	    float GetRadarCenterPosition();

	UFUNCTION()
		float GetDotPosition(AActor* Actor);
	void ReceiveDrawHUD(int32 SizeX, int32 SizeY);

	class AActor* ActorRef;
private:
	/** Crosshair asset pointer */
	class UTexture2D* CrosshairTex;
	
	
};

#include "CppRadarHUD.h"
#include "Engine/Canvas.h"
#include "Engine/Texture2D.h"
#include "TextureResource.h"
#include "CanvasItem.h"
#include "UObject/ConstructorHelpers.h"
#include "Kismet/GameplayStatics.h"
#include "Kismet/KismetMathLibrary.h"
#include "RadarComponent.h"
#include "Components/ActorComponent.h"
ACppRadarHUD::ACppRadarHUD()
{
	// Set the crosshair texture
	static ConstructorHelpers::FObjectFinder<UTexture2D> CrosshairTexObj(TEXT("/Game/FirstPerson/Textures/FirstPersonCrosshair"));
	CrosshairTex = CrosshairTexObj.Object;
}


void ACppRadarHUD::DrawHUD()
{
	Super::DrawHUD();

	// Draw very simple crosshair

	// find center of the Canvas
	const FVector2D Center(Canvas->ClipX * 0.5f, Canvas->ClipY * 0.5f);

	// offset by half the texture's dimensions so that the center of the texture aligns with the center of the Canvas
	const FVector2D CrosshairDrawPosition( (Center.X),
										   (Center.Y + 20.0f));

	// draw the crosshair
	FCanvasTileItem TileItem( CrosshairDrawPosition, CrosshairTex->Resource, FLinearColor::White);
	TileItem.BlendMode = SE_BLEND_Translucent;
	Canvas->DrawItem( TileItem );
}

void ACppRadarHUD::AddToRadar(AActor* Actor)
{
	RadarActors.Add(Actor);
}

void ACppRadarHUD::ClearFromRadar(AActor* Actors)
{
	RadarActors.Remove(Actors);
}

float ACppRadarHUD::GetRadarCenterPosition()
{
	float resultScreenSizeAndRadarX;
	float resultScreenSizeAndRadarY;
	resultScreenSizeAndRadarX = ScreenSize.X * RadarStartLocation.X;
	resultScreenSizeAndRadarY = ScreenSize.Y * RadarStartLocation.Y;
	return resultScreenSizeAndRadarX;
	return resultScreenSizeAndRadarY;
}

float ACppRadarHUD::GetDotPosition(AActor* Actor)
{
	FVector2D ResultX;
	FVector2D ResultY;
	ResultX.X = UKismetMathLibrary::InverseTransformLocation(UGameplayStatics::GetPlayerPawn(GetWorld(), 0)->GetActorTransform(), Actor->GetActorLocation()).X / ObjectDistanceScale;
	ResultY.Y = UKismetMathLibrary::InverseTransformLocation(UGameplayStatics::GetPlayerPawn(GetWorld(), 0)->GetActorTransform(), Actor->GetActorLocation()).Y / ObjectDistanceScale;
	return ResultX.X;
	return ResultY.Y;
}

void ACppRadarHUD::ReceiveDrawHUD(int32 SizeX, int32 SizeY)
{
	ScreenSize.X = SizeX;
	ScreenSize.Y = SizeY;
	GetOwningPlayerController()->GetPawn()->FindComponentByClass<ARadarComponent>();
	for (  )
	{

	}
}

OK there are a few errors.

  1. on URadarComponent::EndPlay you are adding the actor instead of removing it from the array.

  2. on ReceiveDrawHUD You are casting to ARadarComponent instead of URadarComponent.

  3. The function GetDotPosition has unreachable code. return halts a function immediately to return 1 value. the second return will not be reached. In math we can store multiple numbers inside a vector, in this case the return type could be an FVector2D. In another situation returning multiple completely different values could be done by modifying method arguments passed in by reference.

Besides that there is no reason to retrieve the radar component during ReceiveDrawHud. the component registers its owning actor to the HUD so all the actors within the HUDs RadarActors can be assumed to have this component. At this point you don’t need it in the method.

You could just

void ACppRadarHUD::ReceiveDrawHUD(int32 SizeX, int32 SizeY)
{
  for (AActor* ActorX : RadarActors) {
    FVector2D DotPositionX = GetDotPosition(ActorX);

    // Do Stuff
  }
}
  • It’s not an insult to your intelligense but you would indeed benefit from following general c++ tutorials which handle these topics, there are basics.
1 Like

I’ve been through the basics I just haven’t worked very much with HUD related topics that is all. Thank you for your help so far. The basics do not help in this case I have seen many tutorials on basic C++ but that does not help in the grand scheme of things when dealing with UE4 C++ because the framework is completely different then Vanilla C++. UE4 C++ is a different monster almost entirely.

Forgot to mention you should add guards wherever possible to avoid operating on null pointers. Don’t worry about performance at this point. Basically you should check if all the pointer variables used as input (function arguments, getters in code) are valid before dereferencing them.

Example:

//GetOwningPlayerController()->GetPawn()->FindComponentByClass<URadarComponent>();

APlayerController* PCX = GetOwningPlayerController();

APawn* PawnX = IsValid(PCX ) ? PCX ->GetPawn() : nullptr;

URadarComponent* RadarComponent = IsValid(PawnX) ? 
PawnX->FindComponentByClass<URadarComponent>() : nullptr;

UObjects have the IsValid() method which checks both if a pointer is nullptr or not and if the object is in the process of being destroyed. The garbage collector will automatically nullptr variables if the object has not been specifically marked to be kept alive (UPROPERTY references, actors on levels, actor components on actors do this automatically).

In general , if you feel like a nullptr / IsValid() check is enought to keep your code running (or abort a method) without consequences then this is OK. else, you can also use UE’s asserts to crash the program which avoid corrupting someone’s program by performing logic that makes no sense.

1 Like

Nah it just uses tons of macros and unfamiliar libraries / classes.

You definitely have seen somethings I have missed.

I just honestly don’t think the basics will help in this instance tbh.

Thank you for the tips in order to check for nullptrs.

How would I store both values in the FVector2D in this instance.

FVector2D ACppRadarHUD::GetDotPosition(AActor* Actor)
{
	FVector2D ResultX;
	ResultX = UKismetMathLibrary::InverseTransformLocation(UGameplayStatics::GetPlayerPawn(GetWorld(), 0)->GetActorTransform(), Actor->GetActorLocation()).X / ObjectDistanceScale;
   + UKismetMathLibrary::InverseTransformLocation(UGameplayStatics::GetPlayerPawn(GetWorld(), 0)->GetActorTransform(), Actor->GetActorLocation()).Y / ObjectDistanceScale;
	return ResultX;
}

I think I figured out how to pass both variables into one value.

FVector2D ACppRadarHUD::GetDotPosition(AActor* Actor)
{
	
	float FvectorX;
	float FvectorY;
	FVector2D Result = FvectorX + FvectorY;
    FvectorX = UKismetMathLibrary::InverseTransformLocation(UGameplayStatics::GetPlayerPawn(GetWorld(), 0)->GetActorTransform(), Actor->GetActorLocation()).X / ObjectDistanceScale;
    FvectorY = UKismetMathLibrary::InverseTransformLocation(UGameplayStatics::GetPlayerPawn(GetWorld(), 0)->GetActorTransform(), Actor->GetActorLocation()).Y / ObjectDistanceScale;
	return Result;
}

Your function has UB.

Well, you’ll probably call it “intelligence insult” again, but you won’t learn anything without understanding the rules of the language.

2 Likes
float X = 200.f;
float Y = 100.f;
FVector2D XY = FVector2D(X, Y);
1 Like

Are not initialized and not vectors.

1 Like

Does not modify the Result property.

After copying over a float value into a struct you have a copy there, not a reference. To edit the floats inside the struct you could:

float X = 200.f;
float Y = 100.f;
FVector2D XY = FVector2D(X, Y);

XY.X = 300.f;
XY.Y = 500.f;

1 Like

No I won’t call it an intelligence insult. But for sure going back to the basics does not help at all what so ever. That is a waste of precious time. So basically if you don’t know something regarding HUD manipulation and drawing HUD in Unreal Engine 4 C++. That means by default I have to go back to the basics regarding if statements, for loops, polymorphism, inheritance, linked lists, range based for loops, functions etc in Vanilla C++ to understand how to draw and manipulate HUD in UE4 C++? Which Vanilla C++ basics do not cover anything regarding the manipulation of HUD in a foreign framework where things operate differently. I can’t tell if you are just trolling me or what and that is just a default answer to where someone does not know something so that just makes things easier.

Is this better?

FVector2D ACppRadarHUD::GetDotPosition(AActor* Actor)
{
	float FvectorX  = UKismetMathLibrary::InverseTransformLocation(UGameplayStatics::GetPlayerPawn(GetWorld(), 0)->GetActorTransform(), Actor->GetActorLocation()).X / ObjectDistanceScale;
	float FvectorY  = UKismetMathLibrary::InverseTransformLocation(UGameplayStatics::GetPlayerPawn(GetWorld(), 0)->GetActorTransform(), Actor->GetActorLocation()).Y / ObjectDistanceScale;
	FVector2D Result = FVector2D(FvectorX, FvectorY);
	return Result;
}

Here you call the constructor of FVector2D with 2 arguments which form the X and Y of Result. why do you set X Y to 100 and 200 afterwards?

Following links are tutorials relevant to questions you asked / errors we found.

Undefined behavior - cppreference.com

C++ Constructors

https://cplusplus.com/doc/tutorial/structures/

C++ Functions - Return

C++ Programming Language | 9780321958327 | Bjarne Stroustrup | Boeken | bol.

Yes that was completely unnecessary. I edited the reply.

Hi are your questions answered?

1 Like

Yes I got some useful tips thank you for your time gentlemen. This post can be marked as resolved.