A project using this tutorial series is tracked on GitHub. The revision after this part is d8b66fb.
Hej!
If you're using Unreal Engine and you have a character that you want to move then you've probably stumbled upon the built-in Character
and CharacterMovement
classes. Together these classes allow you to easily set up a game character complete with movement, gravity, physics, animations, and networking.
However this can also be very daunting as you might find yourself struggling to find the right combination of properties and values to get just the right feel for your game. Looking through the source code is extremely helpful, however between the two classes there are almost 19k lines of code, and CharacterMovement.cpp
alone takes up 13k (!) of those. While lots of it is chalked up to comments and formatting, the breadth is the meticulous engineering efforts of 50+ contributors.
But the pair of classes are not a one-size-fits-all. Notably they're probably too heavy for something like crowd controlling hundreds of units in an RTS, and integrating custom movement options (e.g. wall-climbing, vaulting etc.) might not seem straight-forward.
For me, I wanted to understand how exactly basic movement works and so I set out to build a basic first-person shooter game (similar in vein to old-school Quake and Doom games) featuring basic but common movement operations.
The list of movement operations I'm going to implement are:
Basic movement + camera look
Simple collision and slide along surfaces
Gravity
Jumping
Step down (walk down slopes/stairs)
Step up (walk up slopes/stairs)
Limit climbable angles (steepness)
Moving platforms
Friction (e.g. ice, mud)
Swimming
Note: This series does not go too much into depth on some of the Unreal and mathematical details, so it is best if you're already familiar with the engine in general, as well as basic vector math and physics.
To start off this tutorial series we will create a fresh project and setup all the boilerplate needed to get going with the movement code.
I will be using Unreal Engine 5.0.3 on Windows 10 however the instructions should be mostly the same for newer/other versions.
To start implementing movement code we start with a blank C++ project. The basic classes we need are:
A game mode class (AGameMode
, here named ACactusGameMode
)
A player controller class (APlayerController
, here named ACactusPlayerController
)
A pawn class (APawn
, here named ACactusPlayerPawn
)
A pawn movement component (UPawnMovementComponent
, here named USimpleMovementComponent
)
In addition I've also created the following boilerplate classes that are for now unrelated to movement:
ACactusGameState
derived from AGameStateBase
ACactusPlayerState
derived from APlayerState
Here are the required source files:
CactusGameMode.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "CactusGameMode.generated.h"
UCLASS()
class CACTUS_API ACactusGameMode final : public AGameModeBase
{
GENERATED_BODY()
};
CactusGameMode.cpp
#include "CactusGameMode.h"
CactusPlayerController.h
#pragma once
#include "CoreMinimal.h"
#include "CactusPlayerController.generated.h"
UCLASS()
class CACTUS_API ACactusPlayerController final : public APlayerController
{
GENERATED_BODY()
};
CactusPlayerController.cpp
#include "CactusPlayerController.h"
CactusPlayerPawn.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Pawn.h"
#include "CactusPlayerPawn.generated.h"
UCLASS()
class CACTUS_API ACactusPlayerPawn final : public APawn
{
GENERATED_BODY()
public:
UPROPERTY(EditDefaultsOnly)
class UCapsuleComponent* CapsuleComponent;
UPROPERTY(EditDefaultsOnly)
class UCameraComponent* CameraComponent;
UPROPERTY(EditDefaultsOnly)
class USimpleMovementComponent* MovementComponent;
ACactusPlayerPawn();
};
CactusPlayerPawn.cpp
#include "CactusPlayerPawn.h"
#include "SimpleMovementComponent.h"
#include "Camera/CameraComponent.h"
#include "Components/CapsuleComponent.h"
ACactusPlayerPawn::ACactusPlayerPawn()
{
CapsuleComponent = CreateDefaultSubobject<UCapsuleComponent>(TEXT("Collider"));
SetRootComponent(CapsuleComponent);
CameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
CameraComponent->SetupAttachment(CapsuleComponent);
MovementComponent = CreateDefaultSubobject<USimpleMovementComponent>(TEXT("Movement"));
}
SimpleMovementComponent.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/PawnMovementComponent.h"
#include "SimpleMovementComponent.generated.h"
UCLASS()
class CACTUS_API USimpleMovementComponent final : public UPawnMovementComponent
{
GENERATED_BODY()
};
SimpleMovementComponent.cpp
#include "SimpleMovementComponent.h"
Next up is creating blueprint classes based on these C++ classes. This is technically not required but helps for rapid iteration when testing properties and debugging.
Make sure to do this for every C++ class (except for the movement component which is C++ only).
Where you put it doesn't matter too much but I've separated the game mode and game state into a Core folder, and controller, pawn, and player state into a Player folder.
After you've created the BP classes go into your game mode BP (here named BP_CactusGameMode
) and set the default classes to your newly created BP classes:
Then go into Edit -> Project Settings -> Maps & Modes and set "Default GameMode" to your GameMode BP class.
Additionally to get more realistic character proportions I've edited the player pawn BP with the following properties:
Capsule component "Capsule Half Height" set to 88
Capsule component "Capsule Radius" set to 30
Camera location Z set to 60
It should look similar to this:
Finally create a test level, place a player start actor and start the game and you should be looking through the camera of your player pawn BP and be unable to move or look (if you can fly around then you're still using the default game mode base and controller).
For input this series of tutorials will utilize the experimental Enhanced Input plugin instead of the traditional built-in mapping system. Enabling it is a simple process. First go to Edit -> Plugins and enable the plugin:
Then restart the editor, go to Edit -> Project Settings -> Input, and set "Default Player Input Class" and "Default Input Component Class" to the Enhanced Input variants:
Now everything should be up and connected and we're ready to implement input and movement, which we will do in the next part.