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;
}