Need help converting this blueprint to C++

So, I decided that a good way to learn C++ was to convert a blueprint I made into C++.

This is the blueprint:

Here is my attempt at the C++ function:



ASBaseCharacter* ASZombieCharacter::GetClosestPlayer()
{
	//Set up local Arrays
	TArray<ASBaseCharacter*> AlivePlayerArray;
	TArray<float> DistanceArray;
	TArray<ASBaseCharacter*> PlayerArray;

	//Get all PlayerControllers in the multiplayer gameworld
	for (TActorIterator<APlayerController> ActorItr(GetWorld()); ActorItr; ++ActorItr)
	{
		if (!ActorItr) return;
		
		//For each of the found player characters get the ASBaseCharacter
		ASBaseCharacter* Player = Cast<ASBaseCharacter>(ActorItr->GetPawn());
		
		//add the ASBaseCharacter to total PlayerArray
		PlayerArray.Add(Player);

		
		
	}
	//PlayerArray count > 0
	if (PlayerArray.Num() > 0)
	{
		//for all Players in PlayerArray
		for (ASBaseCharacter* Player : PlayerArray)
		{
			//make sure the Player is not dead (is Alive is set in SBaseCharacter
			if (Player->IsAlive())
			{
				//get the distance (float) from the AI to the Player Pawn position
				FVector difference = Player->GetActorLocation() - GetActorLocation();
				float distance = difference.Size();
				
				//Add the distance to the distance array
				DistanceArray.Add(distance);

				//Add the player to the Alive player array
				AlivePlayerArray.Add(Player);
			}
			
		}
	}
	
	
	//distanceArray count > 0
	if (DistanceArray.Num() > 0)
	{
		//find the lowest float number in the distance array and get its integer value
		int32 MinIndex;
		const float MinDistance = FMath::Min<float>(DistanceArray, &MinIndex);
		
		//use integer value to set the Closest player in the AlivePlayerArray
		ASBaseCharacter* ClosestPlayer = AlivePlayerArray[MinIndex];
		
		return ClosestPlayer;
	}
	
}


This will compile but it crashes the engine on play.

Now the issues I have with above, other than crashing.

1)I would rather work with PlayerPawn than ASBaseCharacter but I have yet to figure out how to search for PlayerPawn in C++.

2)TActorIterator doesn’t work like Get all actors of class, which is a bummer (or maybe it does and I am not understanding Iteration in C++)

3)I am not sure if I am using 's FMath::Min array stuff correctly.

Any help would be greatly appreciated.

I suggest that you don’t use 's functions. They may be great, but you stated you want to learn C++ in UE4, so just think the boilerplate out yourself.

Second suggestion. You don’t need to fill arrays and do this in separate loops. In your initial actor loop test the distance and just keep the reference to the closest (get distance, compare if less than current distance, if so set reference. On the first one you need to do something different, either have a flag or specially initial value such as -1 for the distance that you compare against so the first actor is always set as the shortest to start), no need to fill an array then reloop that.

Reason for crash. Most likely is an the index is overflowing your Array, that’s not a safe way to access an array. You should initialize the int32 to 0, and then make sure it is less than the length of the array minus 1 before you try to get something out of it, otherwise, that will crash. But, there’s no way to tell for sure without debugging it. Set a breakpoint in your code and run it in debug then step through each line and see where it crashes.

So like this?



ASBaseCharacter* ASZombieCharacter::GetClosestPlayer()
{
	float ClosestDistance = 10000.0f;
	ASBaseCharacter* ClosestPlayer;
	//Get all PlayerControllers in the multiplayer gameworld
	for (TActorIterator<APlayerController> ActorItr(GetWorld()); ActorItr; ++ActorItr)
	{
		if (!ActorItr) return;  //will this line break out of function if no PlayerController found?
		
                //For each of the found player characters get the ASBaseCharacter
		ASBaseCharacter* Player = Cast<ASBaseCharacter>(ActorItr->GetPawn());

		//get the distance (float) from the AI to the Player Pawn position
		FVector difference = Player->GetActorLocation() - GetActorLocation();
		float distance = difference.Size();
		if (distance < ClosestDistance)
		{
			ClosestDistance = distance;
			ClosestPlayer = Player;
		}
	}

	return ClosestPlayer;
	
}


That’s not going to work. You are Iterating on APlayerController and then trying to cast it to a ASBaseCharacter. Unless you have extending PLayerController with your character class name which would be really confusing. Iterate on the Characters, or Get the ControlledPawn from the Controller and then cast that result.

Yes on the loop simplification, that’s what I meant. You can do it all in one shot. Sugestion though is initialize the pointer to 0 at the start and make sure you test the result because it can be NULL.

Yes, MUCH better :slight_smile:

Although there are still more things to consider.

First of all in a scenario when there are no PlayerControllers (yet), the loop body will never run. This could be an issue since you return ClosestPlayer which never had any value assigned explicitly. So it could contain garbage and upon trying to access anything through the pointer lead to a crash. Simply initialize it to NULL or nullptr and you should be safe (as long as you check whether your pointers are valid).

Something that shouldn’t really harm but is also not really useful in any way is the additional “if (!ActorItr) return;”. You’re already checking whether your iterator is valid in the loop condition (ActorItr;) and if it isn’t then you didn’t enter the loop body in the first place. And to answer your question in the comment in that line: Yes return will break out of the function and return the result (which you haven’t specified at this point) to the point where GetClosestPlayer has been called from, but you will not get to this point at all if there is no PlayerController found (see above).

EDIT (in response to mikepurvis):
The PlayerController isn’t what’s being casted here. It’s the PlayerController’s Pawn: ActorItr->GetPawn().

And something I forgot to mention: You should be able to simply replace TActorIterator<APlayerController> with TActorIterator<APawn> and this should allow you to iterate over all Pawns instead of all PlayerControllers.

Ok so I see what you are saying.

If I use APawn it will also find the AI but if I use a Tag for player I should be able to make it work that way.

So it will

Find all Pawns -> Exclude Pawns without the Tag Player (using if statement) and then do everything needed.

My question revolves around performance. Because I need to constantly update a zombies target I am running this on tick. If I am searching for all pawns in the world every frame (which could be up to 100 or more) wouldn’t that be pretty slow or at the very least destroy performance?

I’ve been thinking about this issues and wondering if I should limit the area the Zombie can search (on tick) or use sensing component to only check distance while multiple characters are sensed (use onHearNoise, OnSeePawn).

edit: Just to let you know I made the changes yall pointed out and it works awesome. Thanks for the help!

Instead of APawn, search on the base class for your PlayerCharacter whatever that is. That way you only look at those. It looked like it was ASBaseCharacter, but if the AI use that as well get the sub class that only the Player Characters extend from and not the AI’s.

I mentioned to initialize your return value pointer to 0, but UnrealEverything said it more modern, set it to nullptr. 0 is the old C way. That way, if you don’t find it anything to set it to in the loop you can safely return it as a nullptr, that you can test against null. If you don’t initialize it and use it, you will crash.