The first-person character

The character we aim to create is going to require the ability to move, jump, turn, and shoot a gun. The visual presence of a first-person character (FPC) is very different to that of a third. For one, instead of an entire character mesh being used, we only need the hands and gun model. Instead of having the camera set back and detached from this character model, we need the arms and gun to rotate with the camera as the FPC turns and moves. The character we are going to create requires some imported assets from another UE4 template. We do this, instead of using the template objects directly so we can emphasize the workings of the objects provided in these templates, by creating them ourselves.

Importing what we need

Again, this is going to require the migration of a few assets. Open UE 4.10 again and navigate to the Unreal project browser. This time create a new Blueprint template from the First Person Template as follows:

Importing what we need

Call this project Temp. We are going to be migrating all of the content from the First Person folder within the Content Browser.

Importing what we need

When the migration requests a destination folder, navigate to the content folder of the recently created BossMode project. This will migrate all of the media assets required for the first-person character. Now let's get to writing some code! During the course of this chapter we will be bouncing from C++ code to blueprint implementations. This development process is a cornerstone of UE 4's experience. If you wish you may now delete the Temp project from your hard drive as it will no longer be needed.

Beginning the FPC

Let's add our first code class to the BossMode project. Open the C++ wizard and create a new class that inherits from a character called BMCharacter (BM standing for BossMode). This object is going to represent our player character in the game. We will be creating input controls for this character so that the player may aim via the mouse or right analogue stick, and move via the keyboard or left analogue stick. We will later be adding a gun and projectiles that the character can shoot.

Establishing a FPC aim/movement

We can begin by writing the class definition for BMCharacter. Let's start with the private members, navigate to BMCharacter.h and add the following code to the class definition underneath GENERATED_BODY():

// Pawn Mesh, will only be seen by self
UPROPERTY(VisibleDefaultsOnly, Category = Mesh)
class USkeletalMeshComponent* FPMesh;

// Gun Mesh, will only be seen by self
UPROPERTY(VisibleDefaultsOnly, Category = Mesh)
class USkeletalMeshComponent* FPGunMesh;

// First person camera
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera,
meta = (AllowPrivateAccess = "true"))
class UCameraComponent* FPCameraComponent;

Here we have created two skeletal mesh components, one for the arms of the pawn and one for the gun the arms will be holding. Each of these meshes will only be visible to the owning pawn. This is because, if we were to ever have other players, cameras, or reflective objects view our character, we would want them to see something similar to the mesh we have been using in our third-person projects thus far. The final component is a camera component that will be used for the first person camera. We have specified that this component is VisibleAnywhere and can be read from blueprint.

We have also used the meta specifier again. The meta specifier allows you to specify more nuanced settings for the property. Last time we used this specifier it was to establish slider settings for an exposed property. In this case we have specified that this camera component can be accessed privately with meta = (AllowPrivateAccess = "true"). This is so that we may expose this object to blueprint for reading, however the object itself remains privately protected within the codebase.

Now, let's move onto our public members. We are going to require some members that will control how the character feels when adjusting the aim with a controller. Add the following code to the class definition under the public encapsulation section and the class constructor declaration:

public:
// Sets default values for this character's properties
ABMCharacter();

// Base Turn Rate in deg/sec
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera)
float BaseTurnRate;

// Base Lookup/Down rate in deg/sec
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera)
float BaseLookUpRate;

// Called when the game starts or when spawned
virtual void BeginPlay() override;

// APawn interface
virtual void SetupPlayerInputComponent(class UInputComponent* InputComponent) override;
// End of APawn interface

virtual void Tick(float DeltaSeconds) override;

The two float values will be utilized when we receive an input command from the engine to gauge how much the character will turn when the aim analogue stick is moved. We then override the SetupPlayerInputComponent() method so that we may provide custom bindings to the UInputComponent, the Tick() method so we can provide custom per-frame functionality, and the BeginPlay() method so we design some start-up functionality for the BMCharacter.

Next we need to add some protected methods that we will use to drive the character's movement and aim. Add the following code to the class definition:

protected:
// Handles forwards and backwards movement
void MoveForward(float Val);

// Handles left and right strafing movement
void MoveRight(float Val);

/**
* Will be called via input axis mapping
* @param Rate this is the normalized rate, 1.0 means full turn rate
*/
void TurnAtRate(float Rate);

/**
* Called via input to turn look up/down at a given rate.
* @param Rate this is the normalized rate, 1.0 means full turn rate
*/
void LookUpAtRate(float Rate);

Each of these functions will be bound to an axis mapping via the SetupPlayerInputComponent()method that we override from the APawn interface.

Awesome, that will do for now. Let's define the functions we just declared.

Defining the FPC constructor

As always, we will start with the default constructor ABMCharacter::ABMCharacter(), we are going to start by initializing the camera and capsule components:

ABMCharacter::ABMCharacter()
{
    // Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = true;

    // Set size for collision capsule
    GetCapsuleComponent()->InitCapsuleSize(42.0f, 96.0f);

    // Set the character turn rates
    BaseTurnRate = 45.0f;
    BaseLookUpRate = 45.0f;

    // Create a CameraComponent	
    FPCameraComponent = 
    CreateDefaultSubobject<UCameraComponent> (TEXT("FirstPersonCamera"));
    FPCameraComponent->AttachParent = GetCapsuleComponent();

    // Position the camera
    FPCameraComponent->RelativeLocation = FVector(0, 0, 64.f);
    FPCameraComponent->bUsePawnControlRotation = true;

Here we have defined the capsule size and set the base turn/lookup rates to be 45 degrees per second. We then use CreateDefaultSubobject() to create the camera component. We attach the camera to the capsule as we wish the camera to update with the capsule. You will notice that we position the camera so that it sits 64 units up the Z axis. This will position the camera two-thirds up the capsule (as we set the capsule height to 96), resulting in a chest-high perspective for the character. This works perfectly for a first person character as usually you do not assume the view from the eyes of the character but the position of the chest or gun when it is at rest.

Lastly we set the bUsePawnControlRotation to true. This is very important as it allows us to apply input rotations to the pawn and have it affect the first person camera. This will result in the type of aim behavior we expect from a first person character. If you wish to experiment, once we have established the first person character properly, change this variable to false and see how the character behaves.

Next we must initialize the skeletal mesh components we created earlier. Add the following code to the default constructor:

// Create a mesh component that will only be viewed by the controlling pawn
FPMesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("CharacterMesh1P"));

FPMesh->SetOnlyOwnerSee(true);
FPMesh->AttachParent = FPCameraComponent;
FPMesh->bCastDynamicShadow = false;
FPMesh->CastShadow = false;

Here we are initializing the first person mesh. This will appear as a pair of arms in the game. We have set the mesh component so that it will only be visible to the owning object via FPMesh->SetOnlyOwnerSee(true). This is important as we only wish the FPCameraComponent to see this mesh. Otherwise other players or game cameras will see a pair of floating arms! We also don't want this mesh to cast a shadow. If the player saw the shadow of just two arms on a wall it would completely break the illusion they are controlling a fully fleshed character. We do this by setting bCastShadow and bCastDynamicShadow to false. We then attach the mesh to the camera so the mesh position will update relative to the camera. Next we add similar code for the gun mesh:

FPGunMesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("FP_Gun"));
FPGunMesh->SetOnlyOwnerSee(true);// only the owning player will see mesh

FPGunMesh->bCastDynamicShadow = false;
FPGunMesh->CastShadow = false;

FPGunMesh->AttachTo(FPMesh, TEXT("GripPoint"), EAttachLocation::SnapToTargetIncludingScale, true);

Again we are initializing the gun mesh so that only the owning player can see the mesh, as well as preventing it from casting any kind of shadow. Next we are attaching the mesh in a new way. We are attaching the mesh to a socket that is present in the FPMesh. This is so that the gun mesh will be positioned properly in the FPMesh hands. This will also ensure that when FPMesh updates in position and rotation, so does the FPGunMesh.

We are doing this by using an overloaded version of the AttachTo()method. This one takes in a pointer to the skeletal mesh that will own the socket we are attaching the mesh to, the name of the target socket, an attach type (we have used SnapToTargetIncludingScale as this will snap the FPGunMesh to the socket location even if we scale the FPMesh), and a boolean flag determining whether we wish these bodies to be wielded under a physics simulation. Note that if the provided socket name does not exist within the skeletal mesh assigned to FPMesh, the function will assume a default relative position of 0,0,0.

Tip

Note that we have not associated these skeletal mesh components with any assets. That is because we are going to be making these asset associations in the Blueprint abstraction of this object. Going forward, this is the preferred method when using UE4. This means it is much easier for us to swap out assets on a code object. Your game designers will thank you!

Defining the FPC move and aim functions

Next we can add definitions for the move and aim functions we declared earlier. Add the following code to the BMCharacter.cpp:

void ABMCharacter::MoveForward(floatValue)
{
    if (Value != 0.0f)
    {
        // add movement in that direction
        AddMovementInput(GetActorForwardVector(), Value);
    }
}

This function simply takes in a value from the axis mapping, we then call AddMovementInput() and pass in the character's forward vector as well as the movement value. This method will utilize the settings stored within the UCharacterMovementComponent such as move speed and walking capabilities to drive the movement of the character. We only need to specify one function for forwards and backwards movement as Value will range from -1 to 1. If the value is negative, we will move backwards along the character's forward vector:

void ABMCharacter::MoveRight(float Val)
{
    if (Value != 0.0f)
    {
        // add movement in that direction
        AddMovementInput(GetActorRightVector(), Val);
    }
}

This method works in much the same way as the previous yet, this time, we pass the character's right vector. The two functions together provide the user lateral control of the character.

Next we are going to define the TurnAtRate() and LookUpAtRate() methods. These methods are going be used so we can support controllers in BossMode:

void ABMCharacter::TurnAtRate(floatRate)
{
    // calculate delta for this frame from the rate information
    AddControllerYawInput(Rate * BaseTurnRate * 
    GetWorld()->GetDeltaSeconds());
}

Here we are calling the AddControllerYawInput() method. Again this method will utilize the settings specified in the UCharacterMovementComponent. We are passing the Rate value passed in (which will range from -1 to 1), we then multiply this value by BaseTurnRate (we specified as 45), and then multiply that by the delta tick. This will ensure that the character rotates at the appropriate per second rate regardless of frame rate:

void ABMCharacter::LookUpAtRate(floatRate)
{
    // calculate delta for this frame from the rate information
    AddControllerPitchInput(Rate * BaseLookUpRate * 
    GetWorld()->GetDeltaSeconds());
}

Again this function is very similar to the first however, this time, we are calling AddControllerPitchInput(). We only need to define functionality for the controller input specifically as we are expecting a variable Rate from that input device. The rate value will range between -1 and 1 based on how tilted a given analogue stick is. This is different to mouse input as the mouse input will provide us with a fixed delta value we can directly use for turn rate. Such a method is defined in the pawn class for us.

Alright! Compile and run this code now, we must set up our input mappings before we can continue working with the ABMCharacter.

Creating the input bindings

This will be the third time we establish input bindings so this time I will keep it brief. Once the code has compiled and the project is running, navigate to Project Settings | Input and set up the following Axis Mappings:

Creating the input bindings

Creating the input bindings
Creating the input bindings

The previous mappings will allow use of both keyboard/mouse and controller when moving/aiming the ABMCharacter. While we are here we should also set up some Action Mappings we are going to require later on in the project:

Creating the input bindings

We will use these mappings to get the character to jump, fire the gun, and track. Tracking will be described in the next chapter.

Binding the inputs

Navigate back to the BMCharacter.cpp and add the following code to the SetupPlayerInputComponent() method definition:

// Called to bind functionality to input
void ABMCharacter::SetupPlayerInputComponent(class UInputComponent* InputComponent)
{
    Super::SetupPlayerInputComponent(InputComponent);

    check(InputComponent);

    InputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);
    InputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping);
    InputComponent->BindAxis("MoveForward", this, &ABMCharacter::MoveForward);
    InputComponent->BindAxis("MoveRight", this, &ABMCharacter::MoveRight);

Here we are simply binding the action mapping Jump and the axis mappings MoveForward and MoveRight to the appropriate functions found in the ABMCharacter object. Underneath this code, add the following:

    InputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
    InputComponent->BindAxis("TurnRate", this, &ABMCharacter::TurnAtRate);
    InputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
    InputComponent->BindAxis("LookUpRate", this, &ABMCharacter::LookUpAtRate);
} // <-- closing SetupPlayerInputComponent()

It is important to note that the axis mappings that are expecting input from the mouse have been bound directly to AddControllerYawInput() and not the TurnAtRate() functions we created earlier. That is because we can expect an absolute delta from our mouse input. Meaning that the value that is returned from the drivers for the mouse can be directly parsed to the AddControllerYaw() method. The reason we have made our own TurnAtRate() is to handle input from the variable axis input we can expect from an analogue stick, as mentioned previously.

Abstracting a BM character into Blueprint

Ok, now we can compile the code we just wrote and run the project. We are now going to be creating a new Blueprint based from the ABMCharacter object. Within your Content browser create a new Blueprint now (again, the folder setup and tidiness of your project is up to you), you can do this by right-clicking within the Content browser and selecting Blueprints | Blueprint Class. This will open the Blueprint Wizard, search for our BMCharacter in the list of base classes available. Call this Blueprint FPCharacter.

We are not going to do much within the blueprint itself, simply associate and position some of the assets we defined in the ABMCharacter base class. With the Blueprint editor open, navigate to the Viewport panel. Under the Components panel on the left-hand side of the editor window you will see the FPMesh and FPGunMesh skeletal mesh components. We need to provide references to the correct assets for each of these components.

With the FPMesh component selected, address the Details panel on the right-hand side of the editor. Under the Animation section, change the Anim Blueprint Generated Class property to FirstPersonAnimBP from the provided dropdown. Next, change the Skeletal Mesh property under the Mesh section to SK_Mannequin_Arms. Then change the Z component of the Location property under the Transform section to -155.0cm.

Then select the FPGunMesh and address the Details panel again. Change the Skeletal Mesh property under the Mesh section to SK_FPGun. You will notice that the FPGunMesh will automatically be placed in the hands of the FPMesh thanks to our AttachTo() call we made in the base class. You should currently see something like this:

Abstracting a BM character into Blueprint

Setting a Blueprint object as the default pawn in a C++ game mode

Ok, now we need to set this Blueprint we just created as the default pawn within the game mode. Upon creation of this C++ project, a default C++ game mode was provided, we will modify this to support the FPCharacter as the default pawn class. We can do this by utilizing another construction helper. So far we have used the ObjectFinder construction helper. There is another that is very useful for this given situation. We can leverage the capabilities of the FClassFinder , which is also part of the ConstructHelpers namespace. This object will find the generated class for any blueprint asset, given the correct path to the asset location within the content folder. Let's do this now by modifying the ABossModeGameMode that will have been generated upon creating the project. Navigate to BossModeGameMode.h now and add the following code to the class definition:

UCLASS()
Class ABossModeGameMode : public AGameMode
{
    GENERATED_BODY()

public:
    ABossModeGameMode();
};

All we have done here is declare a default constructor that we can use to make the default pawn association. Navigate to the BossModeGameMode.cpp now and add the following code:

ABossModeGameMode::ABossModeGameMode()
    : Super()
{
    // set default pawn class to our Blueprinted character
    Static ConstructorHelpers::FClassFinder<APawn> PlayerPawnClassFinder(TEXT("/Game/FPCharacter"));

    DefaultPawnClass = PlayerPawnClassFinder.Class;
}

Here we are utilizing the aforementioned FClassFinder object of type APawn to find our FPCharacter Blueprint we just created. We have parsed a path to the class object as an input to the constructor for the FClassFinderObject. Be sure to use your own path here. If you have forgotten how to find the path, you can right-click on the FPCharacter asset in the Content browser and select Copy Reference. You can then paste the reference path into the code file. Be sure to remove the object type from the path as well as anything past the '.' Character, for example—Blueprint'/Game/FPCharacter.FPCharacter' should be '/Game/FPCharacter'.

Testing what we have!

Ok, now compile and run the project and we will test the movement/aim inputs we just created! Press the play button and you will see something similar to this:

Testing what we have!

We have finished creating the basics for our FPCharacter. You will be able to move the character around the level freely as well as aim with the mouse!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.112.210