Referencing destroyed objects crashes game

In the game I am currently working on, every character has an AActor * enemy attribute.

This attribute is used in various locations in code for each AI. When the corresponding enemy is destroyed via


AActor::Destroy()

, code such as: enemy->getHealth() crashes the game.

The result is simply that I have to put obscene amounts of the following example code in my game:



if (!enemy->isPendingKill())
{
// do stuff
}


The above however does not fully solve the problem. At one instance, the enemy->isPendingKill() may be false, but later on in the code it may suddenly become true. Is there any way of getting around this problem without having to wrap every single line of game code in the above if statement?

Check for a null pointer reference?

This does not solve the problem. For example, I just got a crash at the following line of code (which gets called every tick):


	if (currentEnemy != NULL && !currentEnemy->IsPendingKill())
	{
		float distanceToEnemy = FVector::Dist(currentEnemy->GetActorLocation(), GetActorLocation());  // CRASH HERE
                ...
                ...

It crashed because currentEnemy is NULL :confused:

I don’t have UE4 here, but if I’m not mistaken you should check for nullptr, try it and let me know if it works.

If you only want to access the currentEnemy variable once, you can also:


float distanceToEnemy = currentEnemy == nullptr ? 0 : FMath::abs(FVector::Dist(currentEnemy->GetActorLocation(), GetActorLocation())); 

if(distanceToEnemy > 0)
 doW/e();

Using NULL or nullptr shouldn’t result in different behavior. However using nullptr is conceptually better since NULL resolves to the integer value 0, whereas nullptr is its own type.

@walthamMichael: You can bind a function to the enemy actor’s OnEndPlay delegate that sets currentEnemy to NULL when that actor is destroyed. Declare a UFUNCTION that takes an EEndPlayReason::Type argument:

.h:



UFUNCTION()
void OnEnemyDestroyed(EEndPlayReason::Type EndReason);


Then whenever you set currentEnemy to something, also bind your OnEnemyDestroyed function to its OnEndPlay delegate:



currentEnemy->OnEndPlay.AddUniqueDynamic(this, &MyClass::OnEnemyDestroyed);


In the implementation of OnEnemyDestroyed, set currentEnemy to NULL. Now you just keep in mind that if your enemy is NULL it means its destroyed.

Edit: keep in mind that when you change targets from a non-null target, you should unbind the function from your previous target! You can use OnEndPlay.RemoveDynamic for that.

Thanks a lot! I was looking for something similar to this :slight_smile: