GetActorsInSelectionRectangle on MouseRelease?

How do I use GetActorsInSelectionRectangle when the user releases the mouse button?

GetActorsInSelectionRectangle is only allowed to be called inside the DrawHUD event of the HUD, so that prevents me from calling it from within my LeftMouseButtonRelease event inside the PlayerController. So how should I go on to get the selected units the moment the player releases the mouse button (finishing his selection)?

If I try to call GetActorsInSelectionRectangle from within my LeftMouseButtonRelease event I get the following message in VS:

Warning GameHUD_0 Canvas Draw functions may only be called during the handling of the DrawHUD event

I just ran into the same issue… I’m gonna try doing what I did for updating building placement using some variant of DeprojectMousePositionToWorld

	APlayerController* MyController = Cast<APlayerController>(Controller);
	if (MyController)
	{
		FVector TargetLocation, WorldDirection;
		MyController->DeprojectMousePositionToWorld(TargetLocation, WorldDirection);
		FHitResult Result;
		if (GetWorld()->LineTraceSingle(Result, TargetLocation, TargetLocation + (WorldDirection * 100000.0f), COLLISION_COMMANDER_PLANE, FCollisionQueryParams(FName("PlaceBuildingTrace"), false), FCollisionResponseParams::DefaultResponseParam))
		{
			TargetLocation = Result.Location;
		}
		PotentialBuilding->TeleportTo(TargetLocation, GetTransform().GetRotation().Rotator());
	}

I ended up copying their implementation into my custom hud using player controller instead of canvas…

PlayerOwner->ProjectWorldLocationToScreen

void ACustomHUD::GetActorsInScreenRect(TSubclassOf<class AActor> ClassFilter, const FVector2D& FirstPoint, const FVector2D& SecondPoint, TArray<AActor*>& OutActors, bool bIncludeNonCollidingComponents, bool bActorMustBeFullyEnclosed)
{
	OutActors.Empty();

	if (PlayerOwner == nullptr)
		return;

	//Create Selection Rectangle from Points
	FBox2D SelectionRectangle(0);

	//This method ensures that an appropriate rectangle is generated, 
	//		no matter what the coordinates of first and second point actually are.
	SelectionRectangle += FirstPoint;
	SelectionRectangle += SecondPoint;


	//The Actor Bounds Point Mapping
	const FVector BoundsPointMapping[8] =
	{
		FVector(1, 1, 1),
		FVector(1, 1, -1),
		FVector(1, -1, 1),
		FVector(1, -1, -1),
		FVector(-1, 1, 1),
		FVector(-1, 1, -1),
		FVector(-1, -1, 1),
		FVector(-1, -1, -1)
	};

	//~~~

	//For Each Actor of the Class Filter Type
	for (TActorIterator<AActor> Itr(GetWorld(), ClassFilter); Itr; ++Itr)
	{
		AActor* EachActor = *Itr;

		//Get Actor Bounds				//casting to base class, checked by template in the .h
		const FBox EachActorBounds = Cast<AActor>(EachActor)->GetComponentsBoundingBox(bIncludeNonCollidingComponents); /* All Components? */

		//Center
		const FVector BoxCenter = EachActorBounds.GetCenter();

		//Extents
		const FVector BoxExtents = EachActorBounds.GetExtent();

		// Build 2D bounding box of actor in screen space
		FBox2D ActorBox2D(0);
		for (uint8 BoundsPointItr = 0; BoundsPointItr < 8; BoundsPointItr++)
		{
			FVector2D ScreenPos;
			PlayerOwner->ProjectWorldLocationToScreen(BoxCenter + (BoundsPointMapping[BoundsPointItr] * BoxExtents), ScreenPos);

			// Add to 2D bounding box
			ActorBox2D += ScreenPos;
		}

		//Selection Box must fully enclose the Projected Actor Bounds
		if (bActorMustBeFullyEnclosed)
		{
			if (SelectionRectangle.IsInside(ActorBox2D))
			{
				OutActors.Add(Cast<AActor>(EachActor));
			}
		}
		//Partial Intersection with Projected Actor Bounds
		else
		{
			if (SelectionRectangle.Intersect(ActorBox2D))
			{
				OutActors.Add(Cast<AActor>(EachActor));
			}
		}
	}
}

What I ended up doing is to create a few methods in the HUD to enable an asynchronous way of handling the selection rectangle. The following is not the exact way it was implemented, but the general concept of it.

HUD->StartSelectionRectangle(mouseX, mouseY)

HUD->UpdateSelectionRectangle(mouseX, mouseY)

HUD->FinishSelectionRectangle(mouseX, mouseY)

HUD->GetSelectionResult()

When the player presses the mouse button, I would call HUD->StartSelection(mouseX, mouseY). This would set a flag inside the HUD telling it to start drawing a rectangle at the given mouse coordinates. The rectangle would be drawn once DrawHUD is triggered.

While the player is still holding the mouse button down (dragging it), every time
Tick() is triggered at the player controller, I would update the mouse position with a call to HUD->UpdateSelectionRectangle(mouseX, mouseY). This way, every time DrawHUD is triggered it would always redraw the rectangle considering the latest mouse position as it moves.

When the player finally releases the mouse button, I would call HUD->FinishSelectionRectangle(mouseX, mouseY). This would set a flag inside the HUD telling it to finish the selection rectangle. The next time DrawHUD is triggered, GetActorsInSelectionRectangle() would be called and the selection result would be stored inside the HUD itself.

The player controller would then be able to access that result by calling HUD->GetSelectionResult().