Unreal Dev: Smoothly Changing Character Controllers
Game Scenario
You want to implement a squad of multiple characters the player will control. The player issues orders to the character they are currently controlling, and can switch characters whenever they choose. Movement is done via clicking a destination, so you can control character A, set its destination, then switch to character B, and character A should continue on its way. Switching back to character A should also not interrupt its movement unless the player specifically does so.
A good starting example is XCOM. Now just imagine it is real time instead of turn-based, so you can send your characters all over the map within seconds of each other.
Character Solution
It took me much longer than I would have liked to arrive at this solution. It is fairly simple, but there wasn’t really any helpful documentation that I could find. Hopefully this helps you (or future me) in this or a similar scenario.
Your game’s Character class should implement a specific function for taking control over a new character.
void AMyCharacter::TakeControl(AController* NewController)
{
AController* CurrentController = GetController();
if (CurrentController == NewController) return;
AMyCharacter* OtherCharacter = Cast<AMyCharacter>(NewController->GetCharacter());
if (OtherCharacter)
{
OtherCharacter->bTakingControl = true;
NewController->UnPossess();
}
bTakingControl = true;
if (CurrentController)
{
CurrentController->UnPossess();
if (OtherCharacter)
{
CurrentController->Possess(OtherCharacter);
OtherCharacter->bTakingControl = false;
}
}
NewController->Possess(this);
bTakingControl = false;
}
In this case, bTakingControl is just a private bool on the class used specifically for this function. This function is pretty clean and straightforward, but we’re not done yet.
There is engine level code that stops character movement when control changes, and to meet the criteria for our game scenario, we need to make it not do that. This is actually fairly simple, but took me some experimentation to get right.
You’ll need to override Restart() and PawnClientRestart() in your Character class like so:
void AMyCharacter::Restart()
{
// replace the normal Restart that forces pathing to stop
if (bTakingControl && MoveComp)
{
// do nothing
}
else
{
Super::Restart();
}
}
void AMyCharacter::PawnClientRestart()
{
if (bTakingControl && MoveComp)
{
// bypass the ACharacter::PawnClientRestart as it will stop movement
APawn::PawnClientRestart();
}
else
{
Super::PawnClientRestart();
}
}
MoveComp is a member variable that’s a cached reference to the UCharacterMovementComponent on the Character.
It was a lot of sweat for such little code! I’d love to hear any feedback or alternative solutions to this problem.