Udemy: Unreal Engine C The Ultimate Game Developer Course: Section 6 - jgoffeney/Cesium4Unreal GitHub Wiki

Back

Character Assets

Mixamo

  • Mixamo : a site for downloading free skeletonized character models with animations.

Character Imports

The file type to download is .fbx for Unreal. To import the model in Unreal right click a content folder and select the top import option which will then bring up an FBX Import Options dialog. It provides options such as overriding the import skeleton or material, importing animations and performing transforms. For the example everything is left as a default and Import All is pressed.

Note: the imported model will have a "no smoothing group" error. It can be ignored.

After importing the character model will have several different components in its folder and they are color coded as follows:

  • Pink bar: skeletal mesh
  • Yellow bar: physics asset
    • The physics assets consists of collider capsules around the models parts to handle internal movement and external collisions when it ragdolls.
  • Aqua bar: skeleton
    • It is the hierarchy of bones and the skeleton essentially morphs the mesh to follow the bones.
  • Green bar: material asset
  • Orange bar: texture sample

Animation Imports

The Maximo browser allows you to browse animations as applied to a given model. It has several options and the In Place option should be selected to have the character perform the animation without actually moving forward automatically. Our game will control the movement.

After downloading an animation for our model it is imported as above. The FBX Import Options open again with fewer options. To add the animation our character the under Mesh->Skeleton select our character model's skeleton from the drop down.

A dark green bar will denote an animation asset.

To import several animations at once just select them all within the import browser and then in the options set the skeleton and it will be applied to all of them.

The Character Class

The Character class is described as a Pawn that can move around.

By default the Character class is fairly barebones. It contains a Mesh object (as shown below) but it is optional. With its UPROPERTY the keyword meta is introduced. It is used within the Blueprint editor and below it indicates the private variables are made accessible. Also the

/** The main skeletal mesh associated with this Character (optional sub-object). */
UPROPERTY(Category=Character, VisibleAnywhere, BlueprintReadOnly, meta=(AllowPrivateAccess = "true"))
USkeletalMeshComponent* Mesh;

/** Movement component used for movement logic in various movement modes (walking, falling, etc), containing relevant settings and functions to control movement. */
UPROPERTY(Category=Character, VisibleAnywhere, BlueprintReadOnly, meta=(AllowPrivateAccess = "true"))
UCharacterMovementComponent* CharacterMovement;

/** The CapsuleComponent being used for movement collision (by CharacterMovement). Always treated as being vertically aligned in simple collision check functions. */
UPROPERTY(Category=Character, VisibleAnywhere, BlueprintReadOnly, meta=(AllowPrivateAccess = "true"))
UCapsuleComponent* CapsuleComponent;

Similar to the Critter example we will want to add a camera and a spring arm. When setting up the attachment between the camera and the spring arm one of the option is a socket. A socket is an object that can be attachment point for a skeleton to attach things like if a character can pick up a sword.

Once the character class is created and a blueprint is generated then in the editor we can go to the character's mesh component and set its skeletal mesh. Adjust the capsule to put the character's feet at the base and size it to fit the body. Going into the Mesh->Animation you can change the Animation Mode and set an animation to play to view it within the editor.

The following bit of code is for movement handling in the forward / backward direction. It grabs the yaw rotation since that is in the plane we are interested in and then gets the X vector component. The left/right function does the same but for the Y component and then moves the character appropriately.

/// <summary>
/// Handle front / back movement
/// </summary>
/// <param name="value">the input value</param>
void AMainCharacter::MoveForward(float value) {
	if (Controller != nullptr && value != 0.0f) {
		//Get the controller rotation and just grab the yaw
		const FRotator rotation = Controller->GetControlRotation();
		const FRotator yawRotation(0.f, rotation.Yaw, 0.f);

		// Convert the rotation object to a rotation matrix and gets its x component 
		// (which we have chosen as the direction our character faces)
		const FVector direction = FRotationMatrix(yawRotation).GetUnitAxis(EAxis::X);

		// Add the movement in the direction scaled by value
		AddMovementInput(direction, value);
	}
}

The next section described adding pitch and yaw for the camera via the arrow buttons. To map the buttons go to Edit->Project Settings->Engine->Input. The Action Mappings are for one shot actions like a single button press for jumping. The Axis Mappings are for continuous inputs like for movement.

When setting up the listeners in code the check() macro is introduced which checks if its parameter is a valid pointer and will halt execution if it is not.

// Called to bind functionality to input
void AMainCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);

	check(PlayerInputComponent);

	// Map the Engine mouse axis inputs to Pawn functions
	PlayerInputComponent->BindAxis("CameraPitch", this, &APawn::AddControllerPitchInput);
	PlayerInputComponent->BindAxis("CameraYaw", this, &APawn::AddControllerYawInput);

	// Map the Engine key axis inputs to our listener functions
	PlayerInputComponent->BindAxis("MoveForward", this, &AMainCharacter::MoveForward);
	PlayerInputComponent->BindAxis("MoveRight", this, &AMainCharacter::MoveRight);
	
	PlayerInputComponent->BindAxis("TurnRate", this, &AMainCharacter::TurnAtRate);
	PlayerInputComponent->BindAxis("LookUpRate", this, &AMainCharacter::LookUpRate);

	// Map the engine key action inputs to Character functions 
	PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);
	PlayerInputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping);
}

Now to test it the character needs to be set via Project Settings->Project->Maps & Modes->Default Pawn Class.

The final part is setting parameters within the the constructor to keep the model from rotating with the mouse, automatically orient the model to face the direction of movement and set how the character jumps.

// Keep the controller from rotating the character model directly
bUseControllerRotationYaw = false;
bUseControllerRotationPitch = false;
bUseControllerRotationRoll = false;

// Automatically set character to face movement direction
GetCharacterMovement()->bOrientRotationToMovement = true;
GetCharacterMovement()->RotationRate = FRotator(0.f, 540.f, 0.0f); // Set the yaw rate
	
// Setup jump speed and in-air control
GetCharacterMovement()->JumpZVelocity = 325.f;
GetCharacterMovement()->AirControl = 0.2f;

The Animation Blueprint

Blend Spaces

A Blend Space is an asset for blending animation to perform transitions from one state to another. You create a Blend Space 1D under Animations and then add different animations at points on the timeline to control the animation blending.

By opening the Blend Space editor you can set up a set of blended together animations. There is a time line under the model and first the idle animation is dragged from the content panel to the left most (0.0) line in the timeline. The running animation is added to the right most (100.0) line and the walk at the 75% mark. By going to the Asset Details panel and selecting Horizontal Axis you can give is a new name as Speed and set the Maximum Axis Value to 375.0. This automatically adjust the timeline so from 0.0 to the 75% mark the animation blends between standing still and walking and past that it blends between walking and running.

Animation Blueprint

The real logic for the animation is handled in an Animation Blueprint. Rather than directly creating a blueprint from a class an animation blueprint can be created directly (Animation->AnimationBlueprint) and given a parent of type AnimInstance and the skeleton of the main character selected. But since this is focusing on C++ we create a new C++ class based on AnimInstance called MainAnimInstance.

The header file contains variables related to the movement state of the player and function declarations to poll the character to fill these variables.

#pragma once

#include "CoreMinimal.h"
#include "Animation/AnimInstance.h"
#include "MainAnimInstance.generated.h"

/// <summary>
/// The base class for converting the character movement attributes to animations 
/// </summary>
UCLASS()
class CESIUMCPPSTARTER_API UMainAnimInstance : public UAnimInstance
{
	GENERATED_BODY()
public:
    /// <summary>
    /// The current movement speed of the player in the x/y plane
    /// </summary>
    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category=Movement)
	float movementSpeed;

	/// <summary>
	/// Is the player currently in the air?
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Movement)
	bool bIsInAir;

	/// <summary>
	/// The reference to the player character
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Movement)
	class APawn* pawn;

	/// <summary>
	/// Sets the character pawn if it has not been set
	/// </summary>
	virtual void NativeInitializeAnimation() override;

	/// <summary>
	/// Sets the character pawn if it has not been set and then determines the 
	/// current movement speed and if the character is falling to set the above variables.
	/// </summary>
        UFUNCTION(BlueprintCallable, Category=AnimationProperties)
	void UpdateAnimationProperties();
};

The source file has the implementations.

#include "MainAnimInstance.h"
#include "GameFramework/CharacterMovementComponent.h"

void MainAnimInstance::NativeInitializeAnimation() {

	if (pawn == nullptr) {
		pawn = TryGetPawnOwner();
	}
}

void MainAnimInstance::UpdateAnimationProperties() {
	if (pawn == nullptr) {
		pawn = TryGetPawnOwner();
	}

	if (pawn) {
		FVector speedVector = pawn->GetVelocity();
		FVector lateralSpeedVector = FVector(speedVector.X, speedVector.Y, 0.f);
		movementSpeed = lateralSpeedVector.Size();

		bIsInAir = pawn->GetMovementComponent()->IsFalling();
	}
}

Now we can create an Animation Blueprint but we want it to be a child of our class MainAnimInstance within the blueprint editor so go to File->Reparent Blueprint and select our C++ class. Now the blueprint has access to all the functions and properties that were made blueprint accessible within the C++ class but also has the skeleton already set. In the Event Graph the default Event Blueprint Update Animation is hooked up to our function UpdateAnimationProperties.

In the Anim Graph tab we add a State Machine and click on it to open it up. From the Entry we drag out and add a state node. It is given the same name as the blend space we created. Then we can open it up and drag the blend space into the state. Then we add the getter for our movement speed from C++ and hook it up to the speed input in the blend node. So now the blend space knows the current character speed and can set the animation appropriately. Then the blend space output is hooked up to the Output Animation Pose input.

Going back into the main character blueprint for the mesh the Animation Mode can be set to Use Animation Blueprint and then select the blueprint we just created. Now within the game the character will transition between idle and running for just keys and can be setup with variable speed with a gamepad stick.

To handle jumping another node is added in our state machine from our idle/walk/run node. A transition button appears on the connecting edge and can be opened to supply logic. The logic is if the character is in the air. Then nodes for start jump, jumping and jumping are added with logic to flow between them (1)is the start jump far enough along and 2) are we no longer in the air). Then the state has to return to the idle/walk/run node with the logic that enough of the falling animation has completed.

⚠️ **GitHub.com Fallback** ⚠️