Download

Spectate Pawns in ShooterGame

I’m trying to implement spectating of Pawns in ShooterGame such that you can seek forward and backwards through the different players and be able to detach and go back to free-roam.

Right now:

AShooterPlayerController::FireInput triggers when LeftMouse is clicked and finds the next alive pawn to spectate

AShooterPlayerController::TargetingInput triggers when RightMouse is clicked and finds the previous alive pawn to spectate

AShooterPlayerController::JumpInput triggers when Spacebar is clicked and detaches from the current spectating player

My approach is hacky but I honestly struggled to figure out how to seek forward/backward in the list given just the previous spectating position (int32 LastSpectatePosition)

Still need to modify this so that the ShooterCharacter that is the target of spectating will hide the third person mesh and only show a first person mesh which come to think of it may not be a trivial task.

I need to refactor how I iterate through the Pawn list in reverse so any help there would be great and suggestions in general are appreciated.



void AShooterPlayerController::SetupInputComponent()
{
	Super::SetupInputComponent();

	// UI input
	InputComponent->BindAction("InGameMenu", IE_Pressed, this, &AShooterPlayerController::OnToggleInGameMenu);
	InputComponent->BindAction("Scoreboard", IE_Pressed, this, &AShooterPlayerController::OnShowScoreboard);
	InputComponent->BindAction("Scoreboard", IE_Released, this, &AShooterPlayerController::OnHideScoreboard);
	InputComponent->BindAction("ChooseTeam", IE_Pressed, this, &AShooterPlayerController::OnToggleChooseTeamMenu);
	InputComponent->BindAction("ChooseClass", IE_Pressed, this, &AShooterPlayerController::OnToggleChooseClassMenu);

	// voice chat
	InputComponent->BindAction("PushToTalk", IE_Pressed, this, &APlayerController::StartTalking);
	InputComponent->BindAction("PushToTalk", IE_Released, this, &APlayerController::StopTalking);

	InputComponent->BindAction("ToggleChat", IE_Pressed, this, &AShooterPlayerController::ToggleChatWindow);
	InputComponent->BindAction("ToggleChatTeam", IE_Pressed, this, &AShooterPlayerController::ToggleChatWindowTeam);

	InputComponent->BindAction("ToggleChatTeam", IE_Pressed, this, &AShooterPlayerController::ToggleChatWindowTeam);

        // spectate
	InputComponent->BindAction("Fire", IE_Pressed, this, &AShooterPlayerController::FireInput).bConsumeInput = false;
	InputComponent->BindAction("Targeting", IE_Pressed, this, &AShooterPlayerController::TargetingInput).bConsumeInput = false;
	InputComponent->BindAction("Jump", IE_Pressed, this, &AShooterPlayerController::JumpInput).bConsumeInput = false;
}




void AShooterPlayerController::FireInput()
{
	if (IsInState(NAME_Spectating))
	{
		AShooterCharacter * ShooterCharacter = GetNextCharacterToSpectate();

		if (ShooterCharacter)
		{
			bIsSpectatingPlayer = true;//we are now spectating a player
			SetViewTarget(ShooterCharacter);
		}
	}
}

AShooterCharacter * AShooterPlayerController::GetNextCharacterToSpectate()
{
        //try to find next character from current point in list to the end
	AShooterCharacter * ShooterCharacter = GetNextAliveCharacterAfterPosition(LastSpectatePosition);

        //if we do not find a character from current position to end of list
	if (ShooterCharacter == nullptr)
	{
                //try searching from the beginning of the list
		ShooterCharacter = GetNextAliveCharacterAfterPosition(-1);
	}

	return ShooterCharacter;
}

AShooterCharacter * AShooterPlayerController::GetNextAliveCharacterAfterPosition(int32 Position)
{
	int32 IteratorPosition = 0;
	for (FConstPawnIterator Iterator = GetWorld()->GetPawnIterator(); Iterator; ++Iterator)
	{
		AShooterCharacter * ShooterCharacter = Cast<AShooterCharacter>(*Iterator);
		if (ShooterCharacter)
		{
			if (ShooterCharacter->IsAlive() && IteratorPosition > Position)
			{
				LastSpectatePosition = IteratorPosition;
				
				return ShooterCharacter;
			}
		}
		IteratorPosition++;
	}

	return nullptr;
}




void AShooterPlayerController::TargetingInput()
{
	if (IsInState(NAME_Spectating))
	{
		AShooterCharacter * ShooterCharacter = GetPreviousCharacterToSpectate();

		if (ShooterCharacter)
		{
			bIsSpectatingPlayer = true;//we are now spectating a player
			SetViewTarget(ShooterCharacter);
		}
	}
}




AShooterCharacter * AShooterPlayerController::GetPreviousCharacterToSpectate()
{
        //try to find previous character from current point in list to the beginning
	AShooterCharacter * ShooterCharacter = GetPreviousAliveCharacterBeforePosition(LastSpectatePosition);

        //if we do not find a character from current position to beginning of list
	if (ShooterCharacter == nullptr)
	{
                //start over from the very end of the list
		ShooterCharacter = GetPreviousAliveCharacterBeforePosition(100000); //huge hack because I'm not sure how to get the size of the Pawn array from just the iterator
	}

	return ShooterCharacter;
}




AShooterCharacter * AShooterPlayerController::GetPreviousAliveCharacterBeforePosition(int Position)
{
	TArray<AShooterCharacter*> ReversePawnArray;

        //how can I modify the iterator to step through the list in reverse order?
	for (FConstPawnIterator Iterator = GetWorld()->GetPawnIterator(); Iterator; ++Iterator)
	{
		AShooterCharacter * ShooterCharacter = Cast<AShooterCharacter>(*Iterator);
		if (ShooterCharacter)
		{
			ReversePawnArray.Add(ShooterCharacter);
		}
	}

        //my hack is to just create an array myself of ShooterCharacter's and step through it in reverse
	for (int i = ReversePawnArray.Num()-1; i >= 0; i--)
	{
		AShooterCharacter * ShooterCharacter = ReversePawnArray*;
		if (ShooterCharacter->IsAlive())
		{
			if (i < Position)
			{
				LastSpectatePosition = i;
				return ShooterCharacter;
			}
		}
	}

	return nullptr;
}




void AShooterPlayerController::JumpInput()
{
	if (IsInState(NAME_Spectating) && bIsSpectatingPlayer)
	{
		bIsSpectatingPlayer = false;

		AActor * ViewTarget = GetViewTarget();
		SetViewTarget(NULL);
		//sets our free roam near the character we were just spectating
		FVector CameraLocation = ViewTarget->GetActorLocation();// +FVector(0, 0, 30.0f);
		//FRotator CameraRotation(-90.0f, 0.0f, 0.0f);
		ClientSetSpectatorCamera(CameraLocation, ViewTarget->GetActorRotation());
	}
}

/* Bit of a hack to ensure bIsSpectatingPlayer state updates to false if we ever leave the spectating state */
void AShooterPlayerController::BeginPlayingState()
{
	Super::BeginPlayingState();
	bIsSpectatingPlayer = false;
}