Carnage At Castle Moon is a high octane base defense FPS. The goal is to defend 3 points on the map from waves of enemies, with the difficulty increasing each wave.
On Carnage At Moon my main task was working on the first person controller which features several smaller tasks. Movement, dashing, floating and input handling. I was supposed to work on Gun abilities but those features did not make it to the final version of the game. Sense the engine I'm using is Unreal. I could effectively make use of the built-in methods found under the ACharacter class to create my character.
Binding input in is relatively easy. The way I set it up is by binding a key or an axis to a method. This is the way I've done it for any code that needs to be called based on an Input from the player.
I make the character move forward/back or right/left based on the direction. This is to move the player relative to their current direction. I then add movement based on moveValue (which is (1 to -1). The player effectively moves forward if the value is positive and backwards if the value is negative. To implement running I just change the walking speed to a bigger value.
void AGPPlayerController::MoveForward(float moveValue) { const FVector direction = GetActorForwardVector(); AddMovementInput(direction, moveValue, true); } void AGPPlayerController::MoveRight(float moveValue) { const FVector direction = GetActorRightVector(); AddMovementInput(direction, moveValue, true); }
Jumping is easy but can become a bit tricky because of ground checking. This is done by casting a ray downwards from the player to know when the player is touching the ground or not. This is what's been done for this project too.
Looking around in horizontal direction should rotate the entire player model too. I implement this by just adding yaw rotation when the mouse axis moves right or left.
looking around in vertical direction should not pitch the player model up and down. Instead it should rotate the camera up and down. This means I could not just pitch the character up and down the same way I did for looking in horizontal direction. Instead I add rotation to the camera to allow the player to look up and down without ever moving the model. I then clamp the rotation to a max and min value to limit how far up and down the player can look.
void AGPPlayerController::PitchCamera(float val) { float currentRot = CameraComponent->GetComponentRotation().Pitch; const FRotator rot = FRotator(val, 0 ,0); if(currentRot + rot.Pitch < PitchMinMax.X || currentRot + rot.Pitch > PitchMinMax.Y) return; CameraComponent->AddLocalRotation(rot); } void AGPPlayerController::MoveRight(float moveValue) { const FVector direction = GetActorRightVector(); AddMovementInput(direction, moveValue, true); }
Instead I add rotation to the camera to allow the player to look up and down without ever moving the model. I then clamp the rotation to a max and min value to limit how far up and down the player can look.
Adding both systems together effectively creates a look around that feels realistic, responsive and fun to use.
The player controller features a floating feature that is applied when the character is holding down space after a jump. It essentially causes the player to drop to the ground slowly creating a floating feeling. The player is not supposed to be able to float before they have reached max altitude.
This effect was achieved by manipulating the gravity scale of the player. In order to make sure the floating effect is only applied when the character is falling i check the velocity and apply the effect when the value is negative.
//Set gravity scale to default when the player is on the Ground if(GetCharacterMovement()->IsMovingOnGround()) { GetCharacterMovement()->GravityScale = defaultGravityScale; OnFallingEvent(); } //if floating set gravity to FLOATING if(isFloating && GetCharacterMovement()->Velocity.Z < 0) { GetCharacterMovement()->GravityScale = FloatingGravityScale; OnFloatingEvent(); } //If no longer floating set gravity to FALLING. if(!isFloating && !GetCharacterMovement()->IsMovingOnGround()) { GetCharacterMovement()->GravityScale = FallGravityScale; OnFallingEvent(); }
The final feature the character has is the ability to dash. The character can dash in most vertical directions. This effectively allows the character to dash in the air and combined with the floating feature creates a fun flying effect. The dashing ability has a cooldown which is a timer that allows it to be triggered every few seconds.
void AGPPlayerController::OnDash() { FVector direction; float dashForceTemp = DashForce; if(GetCharacterMovement()->IsMovingOnGround() && CameraComponent->GetComponentRotation().Pitch < 0) { direction = GetActorForwardVector(); } else if(CameraComponent->GetComponentRotation().Pitch > AngleToApplyVerticalDash) { direction = GetActorUpVector(); DashForce = VerticalDashForce; } else { direction = CameraComponent->GetForwardVector(); } //GetCharacterMovement()->GroundFriction = 0; LaunchCharacter(direction * DashForce * 10, false, false); CanDash = false; DashForce = dashForceTemp; GetWorldTimerManager().SetTimer(dashCooldownTimer, this, &AGPPlayerController::SetCanDashTrue, dashCoolDownTime, false); }