Section 8: Finishing Touches
In this section, we'll add finishing touches to your game by playing death animations, disabling input, adding blocking and rolling, and polishing our game with a cool map!
Section Intro - Finishing Touches
There are no notes for this lecture.
Disabling Player Input
In this lecture, we created an animation for when the player is defeated and played it when the the OnZeroHealth event is broadcasted. During this event, we also disabled the player's input by calling the DisableInput() function. This function accepts the controller of the player, which we used the GetController() function to do so. When calling this function, it only returns a generic controller, but we must specify the APlayerController class by passing it in as a generic to cast it.
DisableInput(GetController<APlayerController>());Subscribing to Events With C++
In this lecture, we learned how to subscribe to an event with C++. To do so, we can call the AddDynamic() function, which is available on all events created with the DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE macro. When calling this function, you must provide the class with the function and then a reference to that function.
GetWorld()->GetFirstPlayerController()
->GetPawn<AMainCharacter>()
->StatsComp
->OnZeroHealthDelegate.AddDynamic(
this, &ABossCharacter::HandlePlayerZeroHealth
);Stopping AI Brain Logic
In this lecture, we learned how to play the death animation for the boss when their health reaches zero. During this process, we disabled the brain of our AI component so that the enemy doesn't follow or attack us anymore. We can do so by accessing the UBrainComponent added to each AI controller. On this component, we have access to a function called StopLogic(). This function accepts the reason for stopping the brain, which can be anything we want.
ControllerRef->GetBrainComponent()
->StopLogic("defeated");
FindComponentByClass<UCapsuleComponent>()
->SetCollisionEnabled(
ECollisionEnabled::NoCollision
);In addition, we also disabled collision on the UCapsuleComponent by calling the SetCollisionEnabled() function and passing in the ECollisionEnabled::NoCollision enum value.
Destroying Widgets
There are no notes for this lecture.
Ending the Lock on Behavior
In this lecture, we learned how to end the lock-on behavior. We created a function on our IMainPlayer interface that was implemented in the AMainCharacter class.
void AMainCharacter::EndLockonWithActor(AActor* ActorRef)
{
if (LockonComp->CurrentTargetActor != ActorRef) { return; }
LockonComp->EndLockon();
}
In this function, we're comparing the actor that's currently locked on by our component with the actor that wants to break the lock on.
From our boss character, we cast the player into a variable with the interface. Afterward, we check if the casting was successful. If it was, we called the EndLockonWithActor() interface function.
IMainPlayer* PlayerRef{
GetWorld()->GetFirstPlayerController()
->GetPawn<IMainPlayer>()
};
if (!PlayerRef) { return; }
PlayerRef->EndLockonWithActor(this);Cached Poses and Blending Bones
There are no notes for this lecture.
Playing the Block Animation
There are no notes for this lecture.
Using Dot Products
In this lecture, we learned how to use dot products to check if two actors are facing each other. A dot product is a mathematical calculation for multiplying two vectors. This is helpful for checking if two actors are facing each other because a positive result means the actors are facing away and a negative value means facing each other.
To calculate a dot product, we can use the FVector::DotProduct() function.
FVector OpponentForward{ Opponent->GetActorForwardVector() };
FVector PlayerForward{ CharacterRef->GetActorForwardVector() };
// Positive if looking away. Negative if looking at each other.
double result{
FVector::DotProduct(OpponentForward, PlayerForward)
};We must provide the locations of the actors we want to check. For the vectors, we used the GetActorForwardVector() function to get the direction the actor is facing.
Hit Animations
There are no notes for this lecture.
Rotating on an Axis
In this lecture, we learned how to set up a rolling animation. For this action, we made sure to rotate the actor in the direction they're trying to roll whether they're locked on or not. First, we had to determine their direction by using the Velocity.Length variable, which will tell us if the character is moving.
FVector Direction{
CharacterRef->GetCharacterMovement()->Velocity.Length() < 1 ?
CharacterRef->GetActorForwardVector() :
CharacterRef->GetLastMovementInputVector()
};If the value is less than 1, it means they're not moving, so we set the direction to their forward vector, which is the direction they're facing. Otherwise, we're gonna use their movement vector. Since our characters can be locked onto an enemy, but may also be able to move left or right, we should grab the direction they're moving in to determine their roll direction.
After grabbing that info, we used the UKismetMathLibrary::MakeRotFromX() function to grab a rotation based on a vector. We used this rotation value with the SetActorRotation() function like so:
FRotator NewRotation{
UKismetMathLibrary::MakeRotFromX(Direction)
};
CharacterRef->SetActorRotation(NewRotation);Ignoring Damage
In this lecture, we updated our CanTakeDamage() function to check the bIsRollActive variable. If it is truthy, we're going to tell our character to ignore any incoming damage.
bool AMainCharacter::CanTakeDamage(AActor* Causer)
{
if (PlayerActionsComp->bIsRollActive)
{
return false;
}
if (PlayerAnim->bIsBlocking)
{
return BlockComp->Check(Causer);
}
return true;
}As for the bIsRollActive variable, we had to move it into the public section of our class definition to make it available to read from other classes.
public:
bool bIsRollActive{ false };Spawning Emitters
In this lecture, we learned how to spawn an emitter, which allows us to play a particle system. We can use the UGamePlayStatics class to help us with spawning particles. Specifically, the SpawnEmitterAtLocation() function. This function has three arguments, which are the world to load the particles in, the template, and the location.
UGameplayStatics::SpawnEmitterAtLocation(
GetWorld(),
HitParticlesTemplate,
Hit.ImpactPoint
);For the location, we're using the ImpactPoint variable, which can be found on FHitResult types. This variable contains the location where the trace system has hit an actor.
Fixing the Player Attack Reset
There are no notes for this lecture.
Camera Shake
In this lecture, we learned how to apply a camera shake. First, we updated our AMainCharacter::PlayHurtAnim function for storing the blueprint containing the camera shake info. This parameter uses the TSubclassOf type to allow for any blueprint to be used as long as it derives from a specific class. The parent class can be passed in as a generic.
void PlayHurtAnim(TSubclassOf<class UCameraShakeBase> CameraShakeTemplate);In our function, we played the camera shake by using the ClientStartCameraTake function from the player controller. This function accepts the template as an argument.
void AMainCharacter::PlayHurtAnim(TSubclassOf<class UCameraShakeBase> CameraShakeTemplate)
{
PlayAnimMontage(HurtAnimMontage);
if (CameraShakeTemplate)
{
GetController<APlayerController>()->ClientStartCameraShake(
CameraShakeTemplate
);
}
}
Adding a New Map
There are no notes for this lecture.
Last updated