Top Down Template How to get the pawn to face the mouse direction.

I figured this might do it, but its super buggy does anyone else have a better way of achieving it? Thanks! Here are some gyazos of what it does
The new code is around the bottom the Bis-aiming bool . Its a bit erratic and barely works really

Bigger Circle Pattern for Test
[video]https://gyazo.com/ab95dce98319f682cff3d4c001253e6d[/video]
Smaller Circle Pattern for Test
[video]https://gyazo.com/3280da482ecdac912c70b6e0bbc5ba9c[/video]



void AShatteredDreamsCharacter::Tick(float DeltaSeconds)
{
	if (CursorToWorld != nullptr)
	{
		if (UHeadMountedDisplayFunctionLibrary::IsHeadMountedDisplayEnabled())
		{
			if (UWorld* World = GetWorld())
			{
				FHitResult HitResult;
				FCollisionQueryParams Params;
				FVector StartLocation = TopDownCameraComponent->GetComponentLocation();
				FVector EndLocation = TopDownCameraComponent->GetComponentRotation().Vector() * 2000.0f;
				Params.AddIgnoredActor(this);
				World->LineTraceSingleByChannel(HitResult, StartLocation, EndLocation, ECC_Visibility, Params);
				FQuat SurfaceRotation = HitResult.ImpactNormal.ToOrientationRotator().Quaternion();
				CursorToWorld->SetWorldLocationAndRotation(HitResult.Location, SurfaceRotation);
			}
		}
		else if (APlayerController* PC = Cast<APlayerController>(GetController()))
		{
			FHitResult TraceHitResult;
			PC->GetHitResultUnderCursor(ECC_Visibility, true, TraceHitResult);
			FVector CursorFV = TraceHitResult.ImpactNormal;
			FRotator CursorR = CursorFV.Rotation();
			CursorToWorld->SetWorldLocation(TraceHitResult.Location);
			CursorToWorld->SetWorldRotation(CursorR);

			if (BisAiming)
			{
				FVector Location = TraceHitResult.ImpactPoint;
				Location  + FVector(0, 0, 45);
				FRotator Temp = Location.Rotation();
				Temp.Pitch = 0;
				Temp.Roll = 0;
				ClientSetRotation(Temp);
				SetActorRotation(Temp);
			}
		}
	}
}


I do it this way. Works a charm and even works if your cursor doesn’t hit a bit of the world (as its tracing against a virtual plane)



bool ATanksVsZombiesPlayerController::GetMousePositionOnAimingPlane(FVector& IntersectVector) const
{

	ULocalPlayer* LocalPlayer = Cast<ULocalPlayer>(Player);
	bool bHit = false;
	if (LocalPlayer && LocalPlayer->ViewportClient)
	{
		FVector2D MousePosition;
		if (LocalPlayer->ViewportClient->GetMousePosition(MousePosition))
		{
			bHit = GetPlanePositionAtScreenPosition(MousePosition, IntersectVector);
		}
	}

	if (!bHit)	//If there was no hit we reset the results. This is redundant but helps Blueprint users
	{
		IntersectVector = FVector::ZeroVector;
	}

	return bHit;
}

bool ATanksVsZombiesPlayerController::GetPlanePositionAtScreenPosition(const FVector2D ScreenPosition, FVector& IntersectVector) const
{
	// Early out if we clicked on a HUD hitbox
	if (GetHUD() != NULL && GetHUD()->GetHitBoxAtCoordinates(ScreenPosition, true))
	{
		return false;
	}

	FVector WorldOrigin;
	FVector WorldDirection;
	if (UGameplayStatics::DeprojectScreenToWorld(this, ScreenPosition, WorldOrigin, WorldDirection) == true)
	{
		IntersectVector = FMath::LinePlaneIntersection(WorldOrigin, WorldOrigin + WorldDirection * HitResultTraceDistance, GetPawn()->GetActorLocation(), FVector::UpVector);
		return true;
	}

	return false;
}


Then I use it in my tick like this



static const FName NAME_MouseAimingTrace("MouseAimingTrace");
void ATanksVsZombiesPlayerController::AimUsingMouseCursor() const
{
	if (!bMouseInput)
		return;

	// Check we have a proper pawn
	ATanksVsZombiesCharacter* Pawn = Cast<ATanksVsZombiesCharacter>(GetPawn());
	if (Pawn == nullptr)
		return;

	// Get the pawn location
	FVector PawnLocation = Pawn->GetActorLocation(); 
	
	// Trace to whats beneath the mouse cursor
	FHitResult OutTraceResult;
	//GetHitResultUnderCursor(ECC_Pawn, false, OutTraceResult);
	FVector IntersectVector;
	GetMousePositionOnAimingPlane(IntersectVector);

        // Trace down through the aiming plane to see if we hit an actor that we can aim at
	FCollisionQueryParams CollisionQueryParams(NAME_MouseAimingTrace, true);
	bool bHit = GetWorld()->LineTraceSingleByChannel(OutTraceResult, IntersectVector, IntersectVector - FVector::UpVector * HitResultTraceDistance, ECC_Pawn, CollisionQueryParams);
	
	
	// If we hit something aim set that as our aim direction, otherwise aim at the point on the plane
	FVector Direction = FVector::ZeroVector;
	FVector Location = bHit ? OutTraceResult.ImpactPoint : IntersectVector;

	if (Location != FVector::ZeroVector)
	{
		Direction = Location - PawnLocation;
		DrawDebugLine(GetWorld(), PawnLocation, Location, FColor(255, 0, 0), false, -1, 0, 10.0f);
		if (bHit)
			DrawDebugLine(GetWorld(), IntersectVector, OutTraceResult.ImpactPoint, FColor(255, 255, 0), false, -1, 0, 10.0f);
	}

	// Tell the pawn what its new aim direction is
	Pawn->SetAimInputVector(Direction);
}


SetAimInputVector goes through various functions to decide whether to use a joypad direction or the mouse direction, then consume it (reset it to zero vector) then lerp to it over time so things rotate rather than jump instantly. You may want to go back to your own code once you reach FVector Location =, as this is where the mouse cursor fell.

you are a ******* saint! Where did you learn how to do that? thanks so much it works great!

That’s about my 4th version of a mouse aim system. The first one was like yours, but I wasn’t happy with the way it traced into walls or stopped working if no actor was beneath the cursor.

Then I googled formulas for tracing hits on a plane, then found out that unrealengine has one already. I already knew how to do screen to world traces from something I did in UE3

Finally I put all that knowledge together using the same patten that the UE engine provides for doing actor under the cursor trace.

So basically 16 years in the Unreal engine and some Google queries :slight_smile:

Glad it sorted things for you.