Hey guys,
We are back after a short summer break with more tutorials.
Today we go back to our C++ Fundamentals lessons by looking at possession and how we can control different charactesr in our world. In addition to that we are going to setup a few materials so we can change our characters appearance to showcase the possession a bit better.
As usual you can find the start project on our GitHub page.
Possession ? That sounds scary
Don’t worry it’s not halloween yet, and possession is just a fancy term for taking complete control over another character or actor within your game world.
This allows our player to be able to experience different character behaviors, it allows for playing different stories like in GTA 5 or you can use it for simple mount mechanics. Tons of possibilities.
So let’s take a look at how we possess our characters
// save a copy of our controller AController* SavedController = GetController(); // unpossess first ( helps with multiplayer ) SavedController->UnPossess(); // disable movement mode GetCharacterMovement()->SetMovementMode(EMovementMode::MOVE_None); // possess our new actor SavedController->Possess(Cast<APawn>(ActorToPossess));
Seems pretty straight forward but let’s see what is going on here.
First we end up saving a copy of our current player controller so we can reference it later. This can also be used to save the “AI” brain of a character in the world and put it back when we unpossess said character.
We then go ahead and unpossess the current controller to ensure it’s cleaned up.
We then go ahead and set the movement mode on our player to none so he doesn’t move when we leave his body.
And lastly we possess the actor of our choosing.
So what about these materials ? Are they also haunted ?
As far as we know, they are not, but we only know so much.
Materials are pretty easy to setup and change, you just need to be aware where on the character in question they reside. Let’s take a look at this example
header
UPROPERTY(EditDefaultsOnly, Category = Possession) class UMaterialInterface* DefaultMaterialBody; UPROPERTY(EditDefaultsOnly, Category = Possession) class UMaterialInterface* DefaultMaterialChest;
cpp
if(DefaultMaterialBody) { GetMesh()->SetMaterial(0, DefaultMaterialBody); } if(DefaultMaterialChest) { GetMesh()->SetMaterial(1, DefaultMaterialChest); }
As you can see we do really just a few things to switch over the materials.
We first define the properties on our blueprint so we can assign material instances to our player.
Then based on our logic we can set the material to the newly defined variables.
The only little gotcha is to ensure you are referencing the right material index on your mesh. In the case of our Unreal mannequin he has two materials, one for the body and the other for the chest piece. They are respectively assigned to index 0 and 1 on the mesh. So just ensure you are setting your material on the correct section of your model.
It still feels like a need an old priest and a young priest
Nope that’s really it friends, unpossess current controller and possess new target. Rinse and repeat.
Here is a much more involved example lifted from our sample project combining both material switching as well as possession
void AUE4Fundamentals11Character::Interact() { FVector Start; FVector End; FVector PlayerEyesLoc; FRotator PlayerEyesRot; GetActorEyesViewPoint(PlayerEyesLoc, PlayerEyesRot); Start = PlayerEyesLoc; End = PlayerEyesLoc + (PlayerEyesRot.Vector() * LineTraceDistance); FCollisionQueryParams TraceParams(FName(TEXT("InteractTrace")), true, this); FHitResult InteractHit = FHitResult(ForceInit); bool bIsHit = GetWorld()->LineTraceSingleByChannel(InteractHit, Start, End, ECC_GameTraceChannel3, TraceParams); if(bIsHit && InteractHit.GetActor() != this) { // Log(ELogLevel::WARNING, InteractHit.Actor->GetName()); // start to end, green, will lines always stay on, depth priority, thickness of line DrawDebugLine(GetWorld(), Start, End, FColor::Green, false, 5.f, ECC_WorldStatic, 1.f); // implements interface if(InteractHit.GetActor()->GetClass()->ImplementsInterface(UInteractiveActor::StaticClass())) { IInteractiveActor::Execute_Interact(InteractHit.GetActor()); } else if(InteractHit.GetActor()->IsA(ACharacter::StaticClass())) { // check to see if we are a possessed entity if(bIsCurrentlyPossessed) { bIsCurrentlyPossessed = false; if(DefaultMaterialBody) { GetMesh()->SetMaterial(0, DefaultMaterialBody); } if(DefaultMaterialChest) { GetMesh()->SetMaterial(1, DefaultMaterialChest); } } AUE4Fundamentals11Character* PossessableCharacter = Cast<AUE4Fundamentals11Character>(InteractHit.GetActor()); if(PossessableCharacter) { if(!PossessableCharacter->bIsCurrentlyPossessed) { // handle possession if(!SavedController) { // save the controller SavedController = GetController(); } // unpossess first ( helps with multiplayer ) SavedController->UnPossess(); // disable current player state management bIsKeyboardEnabled = false; bIsRunning = false; bIsArmed = false; // disable movement mode GetCharacterMovement()->SetMovementMode(EMovementMode::MOVE_None); // possess our new actor SavedController->Possess(Cast<APawn>(InteractHit.GetActor())); // enable movement back on the possessed actor PossessableCharacter->GetCharacterMovement()->SetMovementMode(EMovementMode::MOVE_Walking); // set the values for our possession materials if(PossessableCharacter->PossessMaterialBody) { PossessableCharacter->GetMesh()->SetMaterial(0, PossessableCharacter->PossessMaterialBody); } if(PossessableCharacter->PossessMaterialChest) { PossessableCharacter->GetMesh()->SetMaterial(1, PossessableCharacter->PossessMaterialChest); } // ensure the new player is correctly marked as possesed and can be interacted with PossessableCharacter->bIsKeyboardEnabled = true; PossessableCharacter->bIsCurrentlyPossessed = true; } } } } else { DrawDebugLine(GetWorld(), Start, End, FColor::Purple, false, 5.f, ECC_WorldStatic, 1.f); } }
As you can see I am doing a few moves to update the material on the target mesh we want to possess and reset the materials on the mesh we are currently occupying.
Additionally there is a bit of logic to update the state of a few variables that control our animation presentation. For example I am turning off the armed animation logic.
But otherwise it’s just a call to Possess / Unpossess as well as SetMaterial
That’s it folks, hope you found this helpful and here are a few links for some light reading: