AI Sensing Optimization

I’m looking for a way to optimize my AI. As it stands, I can get large groups of AI standing close together and when that happens, AI sensing kind of looses it’s mind and the game grinds to a halt. By large number, I mean a maximum of 250 AIs on the field at a time but generally closer to about 100.

The AI are super simple in terms of their actions. The run based on a finite state machine that determines if they should get closer, move to a waypoint or attack. Not a lot else. All the AI is event driven so It does not need to be on a loop with the exception of a single service in the AI behavior tree that runs a service to update the state of the AI on a 0.5 second loop (with a .1 second variance)

Everything works fairly smoothly until enough of the AI gather together. Since my AI is simple and is only really concerned with the closest enemy, I reduce my vision range to the distance between the AI and the enemy so they don’t see as many targets (to eliminate traces)

//target the closer of the two enemies
if (controlledPawn->GetDistanceTo(nearbyEnemy) < controlledPawn->GetDistanceTo(this->currentTarget))
{
     this->currentTarget = nearbyEnemy;
     controlledPawn->PawnSensor->SightRadius = controlledPawn->GetDistanceTo(nearbyEnemy);
}

Doing this didn’t seem to really help though. What exactly is done in RecastAny and Sensing and what can I do to minimize their impact? Are there alternatives?

Hey Justin,

Could you show a bit more what your AI should be able to do? Or what you are trying to accomplish? Maybe they could use something else than the sensing system?

The first thing I would say that could speed up your code is to change GetDistanceTo() to GetSquaredDistanceTo() because the squareroot can be expensive, and you don’t really need the exact value.

Hope it already helps a bit :slight_smile:
Elias

I’ll give it a shot and see what we can do.

As it stands, The AI is constantly looking out for near by enemies. It has 3 states.
Move to Waypoint, Move Into Range and Attack.

All the sensing component does is detect enemies and if they are of the correct type/team, add them to a list of NearByTargets.

void AShadowHeroes_Unit::OnSeePawn(APawn *OtherPawn)
{
	if (IsValid(this->GetController()))
	{
		AShadowHeroesUnitAIController *unitController = Cast<AShadowHeroesUnitAIController>(this->GetController());
		if (IsValid(unitController))
		{
			if (OtherPawn->IsA(AShadowHeroes_Unit::StaticClass()))
			{
				unitController->NearByEnemies.AddUnique(Cast<AShadowHeroes_Unit>(OtherPawn));
			}
		}
	}
}

I have tried using a sphere collision with overlap events to achieve this but it ended up being way slower.

Is there a reason why the AI has to be able to ‘see’ the enemies?

If they only have to check for distance you can make your own actor iterator and check if they are in the right range. And you could do this every tick or every few ticks and balance the checks a bit. But it should not slow down anything too much with only distance checks.

There is no real reason for them to see per say. But when you have large numbers of actors (our unit cap is 150 per team) then you get some problems with get actors of class. That’s why I wanted an event based solution. I wouldn’t want 22,000 function calls a frame, each one getting a distance to.

I did switch over to getSquaredDistanceTo and it helped a bit. I’m just looking for ways to optimize the perception code or a alternative.

EDIT: I tried removing sensing all together and used a collision sphere with overlaps and I gained something like 50ms. Mostly because of the the object being attached and having to move. This leads me to think that possibly having these checks run on their own thread might be a better solution.

Just to follow up on this, I took your advice and used an object iterator then passed the data to a separate thread to perform the calculations. After that I returned the data and set the values.