This sample game project is deprecated, and is not supported for any version of the Unreal Engine after 4.24. Please make sure you have an appropriate engine version installed before loading this project. For more information on updating a project to the latest engine version, refer to Updating to the Latest Changes from Epic.
This document refers to a sample game project called Platformer Game. You can find this project by doing the following:
- Click the Learn tab in the Epic Launcher and scroll down to the Legacy Samples section.
- Click the image for Platformer Game to see a description of the project. Click the yellow button named Free.
- After a short time loading, the button will change to Create Project. Click this button and the launcher will prompt you to choose a project name and location.
- Click Create and the project will download to your designated folder.
The Platformer Game sample is an example of a PC/mobile side-scroller game. It includes basic implementations of different movement types along with a simple front end menu system.
A complete list of the featured concepts:
- Forced movement
- Custom movement: slide (PlatformerPlayerMovementComp)
- Root motion animations (climbing over cars)
- Position adjustment for playing animation at spot (climbing ledges)
Forced Movement
Movement of the player is handled through its CharacterMovementComponent
, which allows modifying acceleration
before every move update using ScaleInputAcceleration()
. This function is overridden in UPlatformerPlayerMovementComp
to force acceleration along X-axis when the round is in progress. Additionally, all axis bindings - look and move -
are removed in the APlatformerCharacter::SetupPlayerInputComponent()
to prevent the player from moving by themselves.

Custom Movement
The slide custom move allows the player to pass under obstacles, in a similar fashion to regular crouching.

Sliding is implemented as part of the walking mode of the UPlatformerPlayerMovementComp
in the PhysWalking()
function.
When the player begins sliding in UPlatformerPlayerMovementComp::StartSlide()
, the height of their collision
capsule is reduced via the SetSlideCollisionHeight()
function.
![]() |
![]() |
---|---|
Walking Height | Sliding Height |
The player's speed is reduced every tick while sliding in CalcCurrentSlideVelocityReduction()
and CalcSlideVelocity()
,
but can be boosted by sliding down a slope. The player's speed is not reduced below the MinSlideSpeed
threshold to
prevent the player from becoming stuck under an obstacle.
The player can forcefully exit a slide by jumping, or it will occur automatically when their speed is too low. However,
the reduced height of the collision capsule means they cannot just pop out of the slide until they reach a spot with enough
clearance to accommodate the default height of the collision capsule. The TryToEndSlide()
function calls the
RestoreCollisionHeightAfterSlide()
function which checks to see if restoring the default height of the collision
capsule would result in the player overlapping some of the world geometry. If no overlap occurs, the player is allowed
to exit the slide.
Root Motion Animation
When the character fails to jump and runs into an obstacle, it will attempt to climb, or mantle, over it automatically. This movement makes use of root motion animation where the animation sequence itself modifies the location of the root bone of the skeleton and that motion is then transferred to the Actor, causing the player's location to change as opposed to normal animation sequences that only affect the child bones of the skeleton.
APlatformerCharacter::MoveBlockedBy()
is called when the movement component detects the player has run into a wall.
The implementation of this function stops the forced movement of the player, plays the HitWallMontage 'bump reaction'
AnimMontage. Once that animation completes, APlatformerCharacter::ClimbOverObstacle()
is executed to do the actual climbing.
There are three climbing AnimMontages for different obstacles of various heights, and all obstacles in the level must match
one of those heights in order for climbing to work. The AnimationSequences in each AnimMontage apply motion to the root bone.
In order to transfer the root motion from the AnimMontage to the player's character, APlatformerCharacter::Tick()
calculates the change in position of the root bone each frame and applies that to the Actor's location.
Just before finishing animation ResumeMovement()
is called, restoring the default forced movement scheme. Any
remaining animation is blended out as the character moves forward.
Position-Adjusted Animation
Ledges are different from obstacles on the ground because the player's vertical position relative to the ledge will be different each time, as it depends on where in the player's jump it is when it hits the ledge.
To make animation syncing easier, climbing works only with a single, specialized Actor class, PlatformerClimbMarker
. As with
the climbing movement, the process starts when the player runs into a ledge and APlatformerCharacter::MoveBlockedBy()
is called. This function
checks to see whether the player is falling (as opposed to walking) and whether the object the player collided with was one of
the special PlatformerClimbMarker
Actors. If this is the case, the forced movement is disabled and APlatformerCharacter::ClimbToLedge()
is executed, passing it the location of the marker's top left corner - i.e. the location of the ledge to grab.
Because the ledge climbing animation needs to always start at the same location relative to the ledge, and the player can be
anywhere nearby, the character's location is interpolated with from the initial position of the impact with the ledge to the
location the animation expects it to be at. ClimbToLedge()
stores the initial position as AnimPositionAdjustment
and then immediately teleports the character to the spot where the climbing animation should finish and starts playing the
climbing AnimMontage. At the same time, APlatformerCharacter::Tick()
quickly interpolates the relative location of
the SkeletalMeshComponent
of the character to zero, causing the mesh to smoothly catch up to the animation.
Menu System
The menu system is created using the Slate UI framework. It consists of menus, menu widgets, and menu items.
Each menu has a single menu widget (SPlatformerMenuWidget
) that is responsible for layout, internal event handling, and animations
for all of the menu items. Menu items (SPlatformerMenuItem
) are compound objects that can perform actions and contain any number
of other menu items. These can be as simple as a label or button or "tabs" that contain complete submenus made up of other menu items.
This menu can be operated using keyboard or controller, but there is only limited mouse support at this time.
Each menu is constructed via the Construct()
function, which adds all of the necessary menu items, including sub-items,
and attaches delegates to them where necessary. This is done using the helper methods - AddMenuItem()
, AddMenuItemSP()
, etc. -
defined in the MenuHelper
namespace in the SPlatformerMenuWidget.h
file.
Navigation to previous menus is done using an array of shared pointers to menus and is stored in the MenuHistory
variable of
the menu widget. MenuHistory
acts like stack to hold previously entered menus and makes it easy to go back. By using this method,
no direct relationship is created between menus and the same menu can be reused in different places if necessary.
Animations are performed using interpolation curves defined in SPlatformerMenuWidget::SetupAnimations()
. Each curve has start time,
duration, and interpolation method. Animations can be played forward and in reverse and their attributes can be animated at specific time
using GetLerp()
, which returns a value from 0.0f to 1.0f. There are several different interpolation methods available defined in
ECurveEaseFunction::Type
in SlateAnimation.h
.
Main Menu

The main menu is opened automatically when the game starts by specifying the PlatformerMenu map as the default. It loads a special
GameMode, APlatformerGameMode
, that uses the APlatformerPlayerController_Menu
class which opens the main menu by creating a new
instance of the FPlatformerMainMenu
class in its PostInitializeComponents()
function.
In-Game Menu

The in-game menu is created in the PostInitializeComponents()
function of the APlatformerPlayerController
class, and opened or closed
via the OnToggleInGameMenu()
function.
Options Menu
The options menu is available as a submenu of both the main menu and in-game menu. The only difference is how changes are applied:
- Accessed from the main menu, changes are applied when the player starts the game.
- Accessed from the in-game menu, changes are applied immediately when the menu is closed.
The settings in the options menu are saved to GameUserSettings.ini
, and loaded automatically at startup.