How to create a "Looping" Cast. C++

If the Actor you are trying to cast has not yet been constructed you get an error.
In Blueprint you can fix this by adding a delay “Loop”.
simpleloop
This is Bad practice since the cast can dramatically fail and the loop will stay active forever. So we need to create a fail point. We can do this by limiting the number of loops that the cast is allowed to do.
Here is a macro that I saw on youtube and have been using.


This macro was created in an Actor Macro Library so that it can be called by any Actor/Child Blueprint.
It takes a number of loops, the time for each loop and then outputs the cast actor if it works.

I have been trying to do the same thing in Cpp using Timer Handles and trying to place the function itself in an outside file so that any class can call it but I can’t get it to work.

If you know how I can accomplish this in Cpp please tell me.

Ok I got something that works like that, but I can’t really make a “library” that can be called by any class. Anyway, I’ll leave it here for anyone that wants it.

|YourController.h|

// HANDLE CASTS //
const int16 CastTo_MaxAttempts = 50;
const float CastTo_DelayBetweenAttempts = 0.2f;

UPROPERTY()
AYourCharacter* YourCharacter;
int16 CastTo_YourCharacter_NumAttempts = 0;
void CastTo_YourCharacter();

|YourController.cpp|

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

	// HANDLE CASTS //
	CastTo_YourCharacter();
}
// HANDLE CASTS //
void AYourController::CastTo_YourCharacter()
{
	// TRY TO CAST //
	YourCharacter = Cast<AYourCharacter>(GetCharacter());

	// IF SUCCESS //
    if (YourCharacter)
    {
		/*
		* YOUR CODE GOES HERE
		*/
    	return;
    }

	// IF FAIL //
    CastTo_YourCharacter_NumAttempts++;
    if (CastTo_YourCharacter_NumAttempts >= CastTo_MaxAttempts)
    {
        if (GEngine) GEngine->AddOnScreenDebugMessage
			(1, 20.f, FColor::Red, FString::Printf(TEXT
			("%s | /*Cast Failed*/ YourCharacter"), *this->GetName()));
        return;
    }
	// USE TIMER TO RECURSIVELY CALL THE CAST AGAIN //
	FTimerHandle CastTo_YourCharacter_TimerHandle;
    GetWorld()->GetTimerManager().SetTimer(CastTo_YourCharacter_TimerHandle, this, &AYourController::CastTo_YourCharacter, CastTo_DelayBetweenAttempts, false);
}

If anyone has a better way to do this or just some improvements please share them, or if you know how to make this it’s own public function that can be used by any class.

blueprint function library

but I don’t think what you’re doing makes a lot of sense in general, your logic should be event-based if possible rather than polling, try to run the logic when the controller possesses a pawn or something

1 Like

I want to have this as Cpp library not blueprint. If you know how to accomplish that please tell me.
Also, this is not something controller to character specific. I want to use it in other classes that get spawned at different rates by UE but still need eachother. I’ve had this problem before with games that weren’t even multiplayer.

Static functions, just look at gameplaystatics class

You shouldn’t still try to poll on each actor, they could try to cast and then if that fails, add the actors to some array which you can then loop through when the need to update is relevant

why int16? if you want this to happen across the wire then your fastest options are uint8 and int32 (these are also understood by blueprints for pins)
does the value need to be greater then 255, or is it meaningful for the value to be negative?

if the object is “needed” for the game logic to work, then have them created together to begin with (could one be a component on the other?)
what if the object never shows up, will your game crash, will you try to create it again?

if this is critical for progress, could this create a situation where the player or game state is soft-locked until it resolves, or thinks its soft-locked? this could also create strain on the system where it is having to run these timers to then do a poll/broadcast all of it just seems slow…

I used int16 because I didn’t need a value above 100 tries. I wasn’t aware of its limitations, thank you for telling me.

An example of a situation that needed this on my last project was a “house” and “floors” system.

  • They were two separate actors, the house would have all the logic while the floors would be just a volume that would encompass the whole floor that is assigned to it.
  • The main goal would be to hide and show the floors as needed.
    The “floors” would tend to load faster than the “house” and because of that they would simply not work (No crashes). So to avoid this I made a BP library that handles cast retries. It tries 50 times with 0.2 second intervals.

The C++ version uses Unreal’s timer handles. They are not C++ sleep functions they won’t make the code lock, they just get triggered, try to cast 50 times and if it fails it just prints out a message saying that “X” cast failed, and the game just keeps going.

if a “Floor” needs a “House” to exist, and the “Floor” will exist inside the “House” then even though the Floor is probably an actor it can still be nested inside the “House” actor, and at the worst you can work with the

PostInitializeComponents()

(this is called on every Actor just after well the components are initialized, but right before begin play)
to get the nested actors, or create them in the worst case. they can still get their own callbacks and functionality, but if can prevent what is sounding like a race condition on level load. then you don’t need to deal with timers, and holding execution up.
if these need to be created at run time to create blueprint actors then you could have the “House” hold a TArray of “Floors” and then have the PostInitializeComponents() calls a BlueprintNativeEvent() that you can load your Blueprint Floors into.
with it being like a blueprintEvent you can also call it whenever you want either through blueprints or C++
Shouldn’t have any need for looping attempts to find something that might not exist.

I’d take a deep dive into polling and dependencies.

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