[RESOLVED] I am making a mini soccer game using a power up, soccer ball and blockers

I have three actors. A power up that activates as time that keeps a boolean to true. I have a ball with a sphere component and a blocker that is supposed to destroy the ball unless I a have collected the power up. The game crashes once the ball collides with the blocker. I am casting to the bool within the timer function that is within the character within the blocker. The blocker checks to see if the the ball’s sphere component collides and checks to see if the bool within the character is true. For some reason the power up is active bool returns a nullptr and the game crashes.

void ASoccerGameCharacter::PowerUpTimer()
{
	PowerUpIsCollected = bIsActive;
	GetWorldTimerManager().SetTimer(InvincibleTimer, this,
		&ASoccerGameCharacter::PowerUpTimer, PowerUpDelay, true, 20.0f);
}

bool ASoccerGameCharacter::PowerUpTimed()
{
	return bIsActive;
}
I am using this boolean to create a variable that can be referenced and not be consider a nullptr.

void ASoccerGameCharacter::PowerUpTimerNotActive()
{
	PowerUpIsCollected = false;
}

Here is the timer and the bools.

void ABallPowerUp::OnPowerUpBeginOverlap(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	ASoccerGameCharacter* Player = Cast<ASoccerGameCharacter>(OtherActor);
	if (Player->CharacterSphere)
	{
		Player->PowerUpTimer();
		Destroy();
	}

}

Once I collide with the powerup the timer activates before destruction.

void ABlocker::OnComponentBeginOverlap(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	ABall* Ball = Cast<ABall>(OtherActor);
	ASoccerGameCharacter* Player = Cast<ASoccerGameCharacter>(OtherActor);
	if (Ball->BallIsInv && Ball->BallSphere)
	{
		Destroy();
	}
	else
	{
		Ball->Destroy();
	}
	//if (Ball && Ball->BallSphere)
	//{
	//	if (Player->PowerUpIsCollected)
	//	{
	//		Destroy();
	//	}
	//}
}
Here I am checking to see if the ball's sphere component has collided with the blocker itself and the blocker also checks to see if the bool within the character is true or false if the bool is true the blocker destroys itself if the bool is false the ball becomes destroyed. At this point this is where the game crashes any help would be appreciated. I am using overlaps to connect one function to another function the last function in line returns a nullptr for some reason I am casting correctly I believe. I am checking to see if my character has picked up the powerup. 

bool ABall::BallCollectedPowerUp()
{
	ASoccerGameCharacter* Player = Cast<ASoccerGameCharacter>(GetWorld());
	if (Player->CharacterSphere && Player->PowerUpIsCollected == true)
	{
		return BallIsInv = true;
	}
	else
	{
		return BallIsInv = false;
	}
}
I am now checking the a boolean within the ball knowing that a void cannot return a boolean value.

I see a lot of casts but no checks to verify if the casts were successful or not.

I know which variable is not allowing me to pass verification I don’t need to check. Every time the engine crashes on me I can look at the specific cpp line and figure which variable is giving me trouble. For me checking the casts is the least of my concern. You can’t pull a variable from a void using a cast cause of nullptr depending on the situation.

Of course certain variable casts are unsuccessful due to the type of functions you pull those variables from.

More importantly it is more so valid to see if the variable is returning anything at all.

ABall* Ball = Cast(OtherActor);
ASoccerGameCharacter* Player = Cast(OtherActor);

So, the OtherActor is what? The ball or the player? Without checking the cast or tag or anything else, how can you know?

ASoccerGameCharacter* Player = Cast<ASoccerGameCharacter>(GetWorld());

This is not how you get the player character. The cast fails. Player is null. Trying to access CharacterSphere or PowerUpIsCollected will cause a crash.

Could you tell me how to do so properly please? The otheactor is a parameter you pass in to the cast in order to allow the cast to be possible.

I know you are giving hints and eluding to the correct answer, I would appreciate it if possible could you be more direct kind sir.

There are several ways to get the player.

Assuming single player, you can use for example:

#include "Kismet/GameplayStatics.h"
...

ASoccerGameCharacter* Player= Cast<ASoccerGameCharacter>(UGameplayStatics::GetPlayerCharacter(this, 0));

or

ASoccerGameCharacter* Player = Cast<ASoccerGameCharacter>(GetWorld()->GetFirstPlayerController()->GetPawn());

Thank you for the advice Cleric.

They will just get the player character in any place. And even that will not prevent a nullptr situation. You still have to have guards:

ASoccerGameCharacter* Player = Cast<ASoccerGameCharacter>(UGameplayStatics::GetPlayerCharacter(this, 0));

if (Player)
{
    ...
}
else
{
    UE_LOG(LogTemp, Error, TEXT("Invalid Player"));
}

For the second case:

if (GetWorld())
{
    auto controller = GetWorld()->GetFirstPlayerController();
    if (controller)
    {
       auto Player = Cast<ASoccerGameCharacter>(controller ->GetPawn());
       ....
    }
    else
    {
        UE_LOG(LogTemp, Error, TEXT("Invalid Player controller"));
    }
}
else
{
    UE_LOG(LogTemp, Error, TEXT("Invalid GetWorld"));
}

During development, you should use and abuse safeguards (asserts, pointers checks) and logs (specially for negative outcomes).
The time spent in these are way lesser than having to restart the editor over and over again and in trying to find out the cause of the crash.

The way you do is ok:

ABall* Ball = Cast<ABall>(OtherActor);

You just need to check Ball before using it:

if (Ball)
{
    Ball->Destroy();
}

For the player also works:

ASoccerGameCharacter* Player= Cast<ASoccerGameCharacter>(OtherActor);

But, without checking the result of the cast, you won’t know if the OtherActor is a Ball or the Player.

This is C++. Assumptions = crash. I’m sure about X = crash.

So basically these parameters will prevent a nullptr if you don’t mind me asking a redundant question Cleric?

How would I cast to an actor rather then a player character?

void ASoccerGameCharacter::PowerUpTimer()
{
PowerUpIsCollected = true;
GetWorldTimerManager().SetTimer(InvincibleTimer, this,
&ASoccerGameCharacter::PowerUpTimerNotActive, PowerUpDelay, true, 10.0f);
}

//bool ASoccerGameCharacter::PowerUpTimed()
//{
//	
//}

void ASoccerGameCharacter::PowerUpTimerNotActive()
{
	PowerUpIsCollected = false;
}
void ABallPowerUp::OnPowerUpBeginOverlap(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	
	ASoccerGameCharacter* Player = Cast<ASoccerGameCharacter>(UGameplayStatics::GetPlayerCharacter(this, 0));
	if (Player->CharacterSphere)
	{
		Player->PowerUpTimer();
		Destroy();
	}

}

void ABlocker::OnComponentBeginOverlap(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	ASoccerGameCharacter* Player = Cast<ASoccerGameCharacter>(UGameplayStatics::GetPlayerCharacter(this, 0));
	ABall* Ball = Cast<ABall>(OtherActor);
	if (Player->PowerUpIsCollected)
	{
		Destroy();
	}
	else
	{
		Ball->Destroy();
	}
	
	//if (Ball && Ball->BallSphere)
	//{
	//	if (Player->PowerUpIsCollected)
	//	{
	//		Destroy();
	//	}
	//}
}
I casted correctly now and the ball can destroy the blocker with the timer active and the ball itself can be destroyed. When the timer is inactive. Thank you Cleric.

Thank you for your help your advice got the job done I will keep these post for references in the future. :smiley:

The timer worked with the gameplay statics parameters that you had suggested I could cast from the character and place the vars in the blocker no problem. I can cast the variables from within the void and they were not nullptr.