When you created the sprinting movement modifier in Sprint Movement Modifier, you only needed to temporarily change an existing movement mode to make the character run faster. For grappling, you need the character to move in a completely new way: fly towards a grapple point in the environment and then fall once reaching that grapple point. To make the character move in this way, you’ll create a new Movement Mode that defines how the player character moves when using the Grappling Hook action.
When the player activates the Grappling Hook action, the new Movement Mode tests if the player is looking at a grapple point in range and starts interpolating the location of the player to the grapple point.
Create the Grapple Point Blueprint
The grapple point Blueprint is the Actor spawned in the level that the player grapples towards. Your grapple point will have an emissive Material that lights up when the player is looking at it and it’s in grappling range.
Create an Emissive Material
First, you’ll create a Material and Material Instance to use in the grapple point Blueprint.
A Material asset defines how a level object is rendered and a Material Instance is a lightweight version of that Material where you can customize exposed Material parameters. Grapple points use a Material Instance so Blueprints can increase the emissive intensity at runtime to indicate the player can grapple to that point.
To create the emissive Material and Material Instance, follow these steps:
In the Content Browser, in the Content > Parkour folder, create a new folder named
Materials.In the new Materials folder, right-click and create a new Material, and name it
M_Emissive. This will be the parent of the Material Instance used in the Grappling Hook.Open the Material. In the Material Graph, in the Material root node, change Roughness and Specular to 0.
Create a VectorParameter node and name it
BaseColor.Connect the BaseColor parameter node’s RGB value to the Material root node’s Base Color pin.
Create another VectorParameter and name it
EmissiveColor.Create a ScalarParameter and name it
Intensity. Set its Default Value to 3.Add a Multiply node to multiply EmissiveColor and Intensity. Connect the result to the Material root node’s Emissive Color pin.
Click the BaseColor parameter node. In the Details panel, expand the Default Value section and set R, G, and B to 1. Do the same for EmissiveColor.
The final Material Graph should look like this:
Click Apply and Save, and close the Material.
Return to the Content Browser. Right-click M_Emissive and select Create Material Instance. Name it
MI_EmissiveBlue.Open the Material Instance. Override and change these parameter values:
Set EmissiveColor to a blue color.
Set Intensity to 0. The grapple point should start dull and white and only become emissive and blue when active.
Save and close the Material Instance.
Set up Grapple Point Appearance
In this tutorial, the grapple point is a simple rectangular prism. You can follow this design or create your own look for the grapple points.
To set up the Grapple Point Blueprint, follow these steps:
In the Content Browser, in the Content > Parkour > Blueprints folder, right-click and create a new Blueprint Class.
Select Actor as the parent class.
Name the Blueprint
BP_GrapplePointand open it.In the Blueprint Editor, in the Components panel, select DefaultSceneRoot, click Add, and add a Static Mesh component. The mesh should be a child of the scene root.
Name the mesh component
GrapplePoint.In the Details panel, set the Static Mesh to SM_Ramp.
Click Compile.
If you can’t see the mesh in the Viewport, zoom out.
In the Materials section, set Element 0 to MI_EmissiveBlue.
Transform the prism shape so the flat, square side is facing up and is a 0.5m square:
Set the Rotation X value to -45.
Set the Scale X, Y, and Z values to 0.5.
Add Grapple Point Logic
The grapple point should test its distance from the player and if the player camera is pointed at the grapple point. If the conditions pass, it should increase its Material’s emissive intensity and start to glow.
Store the Player Pawn as a Variable
To get and save the player Pawn, follow these steps:
Go to BP_GrapplePoint’s EventGraph tab.
From Event BeginPlay, drag off the exec pin and create a Get Player Controller from Input Device node.
Drag off the Return Value pin and add a Get Controlled Pawn node.
Before saving the Pawn object, check if it’s the expected Pawn type. Drag off the Get Controlled Pawn node’s Return Value pin and add a Cast to BaseAnimatedMannyPawn node.
You cast to the Pawn’s parent
BaseAnimatedMannyPawnclass here because it’s the most general Pawn type that exposes the components you need in this Blueprint (the direction of the Pawn’s camera component in this case).Connect the exec flow from Get Player Controller to the Cast To node.
If the Cast To succeeds, store the typed reference to the Pawn:
In the Cast To node, right-click the As Base Animated Manny Pawn pin and select Promote to Variable.
In the My Blueprint panel, rename the new variable
PlayerPawn.
Change a Grapple Point’s Glow
Every frame, the Blueprint should lower or raise emissive intensity depending on whether the grapple point is within range and in the player’s line of sight.
To make the grapple point stop glowing when grappling conditions aren’t met, follow these steps:
In the My Blueprint panel, create a new variable of type float named CurrentIntensity. You’ll use this variable to store the grapple point Material’s current state.
From Event Tick, create a Branch node. This Branch will check if the distance between the grapple point Actor and player is less than 20 meters and returns False if out of range. You’ll set up its Condition later.
From the False exec pin, add a Set Current Intensity node and keep the value 0.
Drag off the Set node’s exec pin and add a Set Scalar Parameter Value on Materials node:
The Target should be the Grapple Point mesh.
For the Parameter Value pin, connect the Set node’s output pin.
For the Parameter Name, enter Intensity.
To make the grapple point glow when both grappling conditions are met, follow these steps:
From the Branch node’s True pin, add another Branch node.
This second Branch node compares the vectors of the grapple point and player camera. It returns True if the vectors are roughly aligned (player is looking at the grapple point) and False if they are not aligned (player is looking away). You’ll set up the condition later.
Connect the second Branch node’s False pin to the Set Current Intensity = 0.0 node so the False result of both Branch nodes turns off the grapple point’s glow.
Select the Set Current Intensity, Set Scalar Parameter, and Grapple Point nodes. Press Ctrl + D to duplicate them and move the copies above the originals, after the second Branch node.
Connect the second Branch node’s True pin to the new Set Current Intensity node, and change the value to
3.0.
Check for Distance to Player
Now you’ll build the conditions for each Branch node.
To test if the player is in range of the grapple point, subtract the player’s location from the grapple point’s location, get the length of that vector, and check if it is less than 20 meters (2000 units).
Instead of using a Distance (Vector) node, these instructions split the distance calculation into two steps so you can use the result of the subtraction operation for a future calculation.
To add the condition that tests player distance from a grapple point Actor, follow these steps:
To the left of the Event Tick node, add two Get Actor Location nodes, placing one above the other.
For the second Get Actor Location node, from the My Blueprint panel, drag the Player Pawn variable onto the Target pin.
Connect both Get Actor Location nodes to a new Subtract node.
Drag the Subtract node’s output pin and add a Vector Length node to calculate the distance.
Drag off the Return Value pin and add a Less (<) node.
For the lower value of the Less node, enter
2000.Connect the result to the first Branch node’s Condition pin.
Check for Player Camera Direction
To add the condition that tests player camera direction, follow these steps:
Drag off the Subtract node’s output pin and add a Normalize node above your Event Tick logic.
Normalizing the vector converts it to a direction only, no length.
Get the direction of the player camera:
Under the Normalize node, add a Get Player Pawn variable reference.
Drag off Player Pawn’s pin, search for
get camera variable, and add a Get Camera node.Drag off the Camera pin and add a Get Forward Vector node (in the Transformation section of the node actions list).
To compare two vector directions, get the dot product of those vectors. Add a Dot Product node and connect the Normalize and Get Forward Vector nodes to the Dot node’s inputs.
The dot product returns a 1 if the vectors are perfectly aligned, 0 if perpendicular, and -1 if the vectors point in the opposite direction. To make the grapple point activate when the player is roughly looking at it rather than aiming directly at it, add some tolerance to the dot product result.
Drag off the dot product’s result and add a Greater (>) node. Enter
0.88as the node’s second input.Connect the Greater node’s output pin to the second Branch node’s Condition pin.
If the dot product of the player and grapple point direction vectors is greater than 0.88, the Branch returns true.
Save and compile the Blueprint.
The finished event graph should look like this:
Test the Grapple Point
Return to the level editor, add a BP_GrapplePoint to your level, and play the level and check that it's a plain color when looking away, and emissive when looking at it.
Build the Grappling Hook Actor Component
When implementing mechanics with Mover, keep in mind that only one Movement Mode can be active at a time. When a Movement Mode isn’t active, its code isn’t running, so it can’t continuously check for the conditions it requires to activate. To switch between Movement Modes, you need another system that can act as a link between the game, player, and a Movement Mode. You can build a new Actor Component class and add it to your character to act as this link. The Actor Component checks for the conditions required to queue a Movement Mode.
Here, the Grappling Hook Actor Component checks whether the player is aiming at a grapple point in range. If they are, the character can queue the Grappling Hook Movement Mode with the grapple point information gathered by the Actor Component.
When building characters in Unreal Engine, implement character gameplay abilities as Actor Components to keep them modular and reusable across various characters.
Create the Actor Component Class
To set up a new Grappling Hook component, follow these steps:
In Unreal Editor, go to Tools > New C++ Class.
For the parent class, select Actor Component.
Name the class
GrapplingHookActorComponentand click Create Class.Similar to BP_GrapplePoint, the Actor Component also needs to check the player’s range and line of sight to the grapple point. In
GrapplingHookActorComponent.h, declare the following public variables:TSubclassOf<AActor> GrapplePointClass: The type of Actor the component should search for when looking for valid grapple targets in range.TSubclassOf<Type>is a variable that stores a class type that inherits from Type. You’ll set this to BP_GrapplePoint in the Pawn Blueprint.float MaxGrappleRange: How far away the component can detect a BP_GrapplePoint. The default value is2000.fto match BP_GrapplePoint.float DotProductThreshold: Adjusts how aligned the player camera view has to be with the grapple point for the Movement Mode to activate. Set to0.87f.
Add a
UPROPERTYmacro to all three properties withEditAnywhere,BlueprintReadWrite, and a category named“Grapple”to expose each to the level editor and Blueprints.C++UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) ) class PARKOURGAME_API UGrapplingHookActorComponent : public UActorComponent { GENERATED_BODY() public: // Sets default values for this component's properties UGrapplingHookActorComponent(); UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Grapple")Declare a public function named
FindClosestGrappleTarget. Give it aUFUNCTIONmacro withBlueprintCallableandCategory = "Grapple".C++public: // Sets default values for this component's properties UGrapplingHookActorComponent(); UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Grapple") TSubclassOf<AActor> GrapplePointClass; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Grapple") float MaxGrappleRange = 2000.f;
Your final GrapplingHookActorComponent.h should look like this:
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "GrapplingHookActorComponent.generated.h"
UCLASS(ClassGroup = (Custom), meta = (BlueprintSpawnableComponent))
Implement the FindClosestGrappleTarget Function
The Grappling Hook component is responsible for finding a grapple point in range, so all of its logic is handled in the FindClosestGrappleTarget function. Like BP_GrapplePoint, this function calculates the distance and line of sight between the player and grapple points. It returns false if there is no valid grapple target, or it returns information about the closest valid grapple target it found.
Check for Valid Input and Gather Actor Information
In
GrapplingHookActorComponent.cppfile, add the following#includestatements:C++#include "Camera/CameraComponent.h" #include "Kismet/GameplayStatics.h"Inside the
FindClosestGrappleTargetfunction, add anifstatement to check if theGrapplePointClassisn’t set in the component’s properties.In the
ifstatement, print a log message to warn the user there is noGrapplePointClass, and then returnfalse.C++bool UGrapplingHookActorComponent::FindClosestGrappleTarget(FVector& TargetLocation, AActor*& TargetGrapple) const { if (!GrapplePointClass) { UE_LOG(LogTemp, Warning, TEXT("GrappleTargetFinderComponent: GrapplePointClass not set!")); return false; }Add an
ifstatement to check that the component is attached to an owningAActor. If it doesn’t have an owner, returnfalse.C++bool UGrapplingHookActorComponent::FindClosestGrappleTarget(FVector& TargetLocation, AActor*& TargetGrapple) const { if (!GrapplePointClass) { UE_LOG(LogTemp, Warning, TEXT("GrappleTargetFinderComponent: GrapplePointClass not set!")); return false; } AActor* Owner = GetOwner(); if (!Owner)Get the forward vector of the player’s camera component or the player itself:
Declare a new
FVectornamedForward.Call
Owner->FindComponentByClass()on theCameraComponentand save a pointer to it.Add an
ifstatement to check if the camera component you saved is valid.If the camera component is valid, call
GetForwardVector().GetSafeNormal()on the camera component and assign that value to Forward.If the camera component isn’t valid, call
GetActorForwardVector().GetSafeNormal()on the owning character instead.
C++// Try to get the camera component UCameraComponent* Camera = Owner->FindComponentByClass<UCameraComponent>(); FVector Forward; if (Camera) { Forward = Camera->GetForwardVector().GetSafeNormal(); } else { // Fallback to Actor forward vectorTo find the closest grapple point, you’ll need to also save the player’s location and a list of all BP_GrapplePoint instances:
Declare a const
FVectornamedOwnerLocto store the owning character’s location. CallGetActorLocation()to set the value.Declare a
TArrayofAActorpointers namedGrapplePoints.To populate the array, call
UGameplayStatics::GetAllActorsOfClass(UWorld, SearchClass, StorageArray). UseGetWorld()to retrieve the currentUWorld, and pass theGrapplePointClassandGrapplePointsarray.
C++// Get owner location const FVector OwnerLoc = Owner->GetActorLocation(); // Gather grapple points TArray<AActor*> GrapplePoints; UGameplayStatics::GetAllActorsOfClass(GetWorld(), GrapplePointClass, GrapplePoints);Before you loop through all grapple points in the array, declare variables you’ll use and update inside the loop:
An
AActorpointer namedClosestGrapple. You’ll update this in the loop when you first find a grapple point in range and any time you find a closer grapple point.A
floatnamedClosestDistSq. Instead of using square root calculations to find the distance to each grapple point in the array, you’ll use the distance squared. Each time you find a new closest grapple point in the array, you’ll updateClosestDistSq. Since you haven’t found a grapple point yet, use theMaxGrappleRangeas the closest distance. You’ll compare the distance to each element in the array withClosestDistSq.
C++AActor* ClosestGrapple = nullptr; float ClosestDistSq = MaxGrappleRange * MaxGrappleRange;
Check all Grapple Points
To loop through all grapple points and check each for distance and line of sight, follow these steps:
Add a
forloop that loops through each element in theAActor*grapple point array.C++for (AActor* GrapplePoint : GrapplePoints) {Inside the loop, first check that the
GrapplePointelement is valid. If it’s not valid, skip that iteration of the loop.Just like with BP_GrapplePoint, you’ll need to calculate the dot product between the direction the player is facing and the direction from the player to the grapple point:
Declare a
const FVectorto subtract the player’s location from theGrapplePoint’s location and then get the safe normal of the result.C++const FVector ToGrapplePoint = (GrapplePoint->GetActorLocation() - OwnerLoc).GetSafeNormal();Use F
Vector::DotProduct()to calculate the dot product betweenForwardandToGrapplePointand save the result.C++const float Dot = FVector::DotProduct(Forward, ToGrapplePoint);
Get the
FVector::DistSquared()fromOwnerLocto thisGrapplePoint.C++const float DistSq = FVector::DistSquared(OwnerLoc, GrapplePoint->GetActorLocation());Move to the next iteration of the loop if either:
Dot(the alignment of player camera versus grapple point) is less than theDotProductThreshold.The distance to this grapple point is greater than the distance to the closest grapple point you’ve found so far.
C++// Dot product closer to 1 = looking directly at grappling point. 0.87 -> Arbitrary number that is more forgiving if (Dot < DotProductThreshold) continue; if (DistSq > ClosestDistSq) continue;Check if there are any obstructing objects like walls between the player and the grapple point:
Declare a
FHitResultnamedHitto store line trace hit result information.FHitResultis a struct in UE that stores information about the result of a collision query, including the Actor or component that was hit and where you hit it.Declare a
FCollisionQueryParamsstruct instance namedParamsto configure how the line trace behaves.The
FCollisionQueryParamsconstructor takes the following arguments:FCollisionQueryParams Params(TraceName, UseComplexCollision, IgnoreActor)TraceName: Add a debug tag withSCENE_QUERY_STAT(GrappleTrace).UseComplexCollision: Use true for more precise collision detection because grappling requires additional accuracy.IgnoreActor: UseOwnerto make the line trace ignore the character when performing the trace.
C++// Line trace to check whether there's a wall FHitResult Hit; FCollisionQueryParams Params(SCENE_QUERY_STAT(GrappleTrace), true, Owner);Perform a line trace from the character’s location to this grapple point’s location.
Use
GetWorld()->LineTraceSingleByChannel()passing:Hit: The FHitResult to store hit result information in:OwnerLoc: The start of the traceGrapplePoint->GetActorLocation(): The end of the trace.The collision channel for the trace:
ECC_VisibilityUse
ECC_Visibilityfor line-of-sight camera checks.A
FCollisionQueryParamsobject:Params
Save the result in a
boolnamedbHit.C++bool bHit = GetWorld()->LineTraceSingleByChannel( Hit, OwnerLoc, GrapplePoint->GetActorLocation(), ECC_Visibility, Params );
Add an
ifstatement that checks if the line trace hit an object and that object is not this grapple point:If
true, skip to the next loop iteration.If
false, that means you’ve found a close grapple point that is visible. UpdateClosestDistSqto this grapple point’s distance and updateClosestGrappleto this grapple point Actor.
C++if (bHit && Hit.GetActor() != GrapplePoint) { // Something blocking (wall) continue; } // Grapple Point valid and visible ClosestDistSq = DistSq; ClosestGrapple = GrapplePoint;If no grapple points are found in range,
ClosestGrappleis still null after the loop finishes. Add anifstatement to check ifClosestGrapplehas a value:If it has a value, set the
TargetLocationandTargetGrappleoutput variables, and return true. You’ll use these variables later in Blueprints to queue the Grappling Hook Movement Mode.Otherwise, return
false.
C++if (ClosestGrapple) { TargetLocation = ClosestGrapple->GetActorLocation(); TargetGrapple = ClosestGrapple; return true; } return false;Save and compile your code.
Now, the FindClosestGrappleTarget() function returns false if there are no targets in range, if the player is not looking at any targets, or if there’s a wall blocking all potential targets.
Your finished GrapplingHookActorComponent.cpp file should look like this:
// Copyright Epic Games, Inc. All Rights Reserved.
#include "GrapplingHookActorComponent.h"
#include "Camera/CameraComponent.h"
#include "Kismet/GameplayStatics.h"
// Sets default values for this component's properties
UGrapplingHookActorComponent::UGrapplingHookActorComponent()
{
// Set this component to be initialized when the game starts, and to be ticked every frame.
Create the Grappling Hook Movement Mode
The Grappling Hook Movement Mode is where the actual movement code resides, and it gets activated when the Grappling Hook component returns a valid grapple point and target location.
When active, this Movement Mode interpolates the location of the player to the target location, and then launches them in the direction of the movement. It also attaches the player’s Cable Component to the grapple point instance while grappling.
To see how the default Walking and Falling Movement Modes are implemented, you can find their classes in /All/EngineData/Plugins/Classes_Mover/Mover/Public/DefaultMovementSet/Modes. They provide a base template to work with when implementing custom movement modes. In the Grappling Hook Movement Mode, the player won’t have much control over the player character, so it functions somewhat similar to the Falling Movement Mode.
Create and Set Up a New Movement Mode Class
First, create the Movement Mode class and add the necessary includes, forward declarations, and overrides.
To set up a new Movement Mode class, follow these steps:
In Unreal Editor, go to Tools > New C++ Class.
In the Choose Parent Class window, click All Classes, search for
BaseMovementMode, and select that as the parent class.Name the class
GrapplingHookMovementModeand click Create Class.In the header file, add an include for
CableComponent:C++#include "CoreMinimal.h" #include "MovementMode.h" #include "CableComponent.h" #include "GrapplingHookMovementMode.generated.h"The Cable Component attaches itself to the grapple point when grappling.
To support the Movement Mode functionality, include a forward declaration for Legacy Movement Settings:
C++class UCommonLegacyMovementSettings;Movement Modes use Common Legacy Movement Settings to access shared movement configuration values, such as acceleration, gravity, and movement speed.
In the class declaration, add
BlueprintableandBlueprintTypespecifiers to theUClassmacro.Add a
publicsection and declare a constructor.C++UCLASS(Blueprintable, BlueprintType) class PARKOURGAME_API UGrapplingHookMovementMode : public UBaseMovementMode { GENERATED_BODY() public: UGrapplingHookMovementMode(const FObjectInitializer& ObjectInitializer); };Tip: The constructor includes
FObjectInitializerso the Movement Mode can register shared movement settings during initialization.Override
BaseMovementMode’s core Movement Mode functions:GenerateMove_Implementation: Builds the movement intent (what movement should happen).SimulationTick_Implementation: Applies the actual movement simulation each frame.
C++public: UGrapplingHookMovementMode(const FObjectInitializer& ObjectInitializer); virtual void GenerateMove_Implementation(const FMoverSimContext& SimContext, const FMoverTickStartData& StartState, const FMoverTimeStep& TimeStep, FProposedMove& OutProposedMove) const override; virtual void SimulationTick_Implementation(const FSimulationTickParams& Params, FMoverTickEndData& OutputState) override;In this Movement Mode, Simulation Tick will handle all movement since grappling input is only processed when the Movement Mode is activated, and no other input should affect the character when grappling.
GenerateMove_Implementationisn’t used for movement here, but is still required because Mover expects Movement Modes to define their own generate-move step.Add a
protectedsection and declare the following properties and overrides:C++protected: UPROPERTY() TObjectPtr<const UCommonLegacyMovementSettings> CommonLegacySettings; virtual void Deactivate(const FMoverEventContext& Context, FName NextModeName, const FMoverSimContext& SimContext) override; virtual void OnRegistered(const FName ModeName, const FMoverSimContext& SimContext) override; virtual void OnUnregistered(const FMoverSimContext& SimContext) override;The protected section of the Movement Mode’s header contains internal state and engine functions that are used by the Movement Mode.
In
GrapplingHookMovementMode.cpp, add the following includes:C++#include "GrapplingHookMovementMode.h" #include "MoveLibrary/MovementUtils.h" #include "MoveLibrary/FloorQueryUtils.h" #include "MoverComponent.h" #include "DefaultMovementSet/Settings/CommonLegacyMovementSettings.h" #include "DefaultMovementSet/InstantMovementEffects/BasicInstantMovementEffects.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(GrapplingHookMovementMode)UE_INLINE_GENERATED_CPP_BY_NAMEensures Unreal Engine includes the generated code for this class as part of this compilation unit. It’s a modern UE build pattern that manages .gen.cpp files for you.Add the constructor for the Movement Mode.
C++UGrapplingHookMovementMode::UGrapplingHookMovementMode(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { SharedSettingsClasses.Add(UCommonLegacyMovementSettings::StaticClass()); GameplayTags.AddTag(Mover_IsOnGround); }The constructor initializes the Movement Mode by registering shared settings and adding default gameplay tags. Here, you are registering this movement mode as “grounded” in the Mover system for state checks.
Implement GenerateMove
For your grappling Movement Mode, this function should retrieve the Pawn’s Mover Component, input data, current sync state, and delta time before any grapple-specific logic runs. GenerateMove runs every frame.
To implement the GenerateMove_Implementation override, follow these steps:
Implement the function and add the following standard set up for any Movement Mode:
C++//Movement will be handled by simulation tick void UGrapplingHookMovementMode::GenerateMove_Implementation(const FMoverSimContext& SimContext, const FMoverTickStartData& StartState, const FMoverTimeStep& TimeStep, FProposedMove& OutProposedMove) const { const UMoverComponent* MoverComp = GetMoverComponent(); const FCharacterDefaultInputs* CharacterInputs = StartState.InputCmd.InputCollection.FindDataByType<FCharacterDefaultInputs>(); const FMoverDefaultSyncState* StartingSyncState = StartState.SyncState.SyncStateCollection.FindDataByType<FMoverDefaultSyncState>(); check(StartingSyncState); const float DeltaSeconds = TimeStep.StepMs * 0.001f; }
Add Standard Setup Code to SimulationTick
SimulationTick_Implementation is where movement is handled. To start implementing this function, you’ll first need to add the following block of code that sets up any type of Movement Mode:
void UGrapplingHookMovementMode::SimulationTick_Implementation(const FSimulationTickParams& Params, FMoverTickEndData& OutputState)
{
/*
// --- Standard Movement Mode Setup Code STARTS ---
*/
UMoverComponent* MoverComp = GetMoverComponent();
const FMoverTickStartData& StartState = Params.StartState;
// The component this Movement Mode is responsible for moving
USceneComponent* UpdatedComponent = Params.MovingComps.UpdatedComponent.Get();
UPrimitiveComponent* UpdatedPrimitive = Params.MovingComps.UpdatedPrimitive.Get();
This code gathers the standard Movement Mode context, validates required components, pulls the starting sync and input data, converts delta time, and prepares working variables for movement and orientation updates. You’ll use this block of code again when setting up your wall running Movement Mode in the next module of this tutorial series.
This tutorial shows all standard setup code; however, after you’ve finished implementing the Movement Mode, you can optionally trim any local variables you don’t end up using.
Set Up and Validate Grappling-Specific Properties
Next, in SimulationTick_Implementation, perform a one-time initialization of the class’ properties when the Movement Mode becomes active. This initialization sets some variables only once at the start of the movement.
Because of how the Movement Mode is queued in Blueprints, the first frame is evaluated by the SimulationTick function before the TargetLocation is initialized. Therefore, it’s useful to have a boolean tracking if TargetLocation has already been initialized.
To declare and initialize grappling-related properties and components, follow these steps:
In the header file, declare the following public variables:
A
boolto track if initialization has been completed:C++// Tracks if grapple setup has already run bool bIsLocationInitialized = false;A
UPROPERTYfor the location of the target, the grapple point Actor at that target, and the character’s grapple Cable Component:C++// The location to move the character towards. Blueprints sets this to the target grapple point's location when switching to this Movement Mode. UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Mover") FVector TargetLocation = FVector::ZeroVector; // The BP_GrapplePoint Actor to attach the cable component to. Blueprints sets this when switching to this movement. UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Mover") AActor* TargetGrapplePoint; // The character's grapple cable component UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Grapple")Two
FVectorsfor the character’s starting position and direction towards the grapple point.C++// The character's position when the grapple begins FVector StartLocation = FVector(0, 0, 0); // Normalized direction from start location to grapple target FVector GrappleDirection = FVector(0, 0, 0);Two
floatvalues to control the speed and time of the grapple movement.C++// The character's travel speed (cm/s) while grappling UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Mover") float GrappleSpeed = 6000.f; // Total time of the grapple movement (or the time it takes for the Alpha to go from 0 to 1). float TotalTime = 0.5f;
You won’t use
TotalTime’s default value, but it’s best practice to set one to ensure the variable is initialized.In the
.cppfile, at the end ofSimulationTick_Implementation, add anifstatement to check ifTargetLocationis set and not 0.C++void UGrapplingHookMovementMode::SimulationTick_Implementation(const FSimulationTickParams& Params, FMoverTickEndData& OutputState) { /* // --- Standard Movement Mode Setup Code STARTS --- */ UMoverComponent* MoverComp = GetMoverComponent(); const FMoverTickStartData& StartState = Params.StartState; // The component this Movement Mode is responsible for moving USceneComponent* UpdatedComponent = Params.MovingComps.UpdatedComponent.Get(); UPrimitiveComponent* UpdatedPrimitive = Params.MovingComps.UpdatedPrimitive.Get();When the Movement Mode is queued,
TargetLocationand similar variables won’t be set in the first frame, so make sure values are set before changing movement.Add another
ifstatement to handle one-time variable initialization when the Movement Mode is first activated.C++if (TargetLocation != FVector::ZeroVector) { // Initialize grapple state, storing start location and direction to the target if (!bIsLocationInitialized) { } }Set the player’s
StartLocationby callingGetActorLocation()on the Mover Component’s owner.Set the player’s
GrappleCableby callingFindComponentByClass<UCableComponent>().C++if (TargetLocation != FVector::ZeroVector) { // Initialize grapple state, storing start location and direction to the target if (!bIsLocationInitialized) { StartLocation = MoverComp->GetOwner()->GetActorLocation(); GrappleCable = MoverComp->GetOwner()->FindComponentByClass<UCableComponent>(); } }In a new
ifstatement, check that theGrappleCableis valid.C++if (TargetLocation != FVector::ZeroVector) { // Initialize grapple state, storing start location and direction to the target if (!bIsLocationInitialized) { StartLocation = MoverComp->GetOwner()->GetActorLocation(); GrappleCable = MoverComp->GetOwner()->FindComponentByClass<UCableComponent>(); // --- New Code Start --- if (!GrappleCable)Add another
ifstatement to check that theTargetGrapplePointis valid.C++if (TargetLocation != FVector::ZeroVector) { // Initialize grapple state, storing start location and direction to the target if (!bIsLocationInitialized) { StartLocation = MoverComp->GetOwner()->GetActorLocation(); GrappleCable = MoverComp->GetOwner()->FindComponentByClass<UCableComponent>(); if (!GrappleCable) {Use a Time = Distance/Speed calculation to calculate the
TotalTime. Add0.1to the result as a minimum duration and ensure the grapple always takes a small amount of time to prevent snapping.C++if (!TargetGrapplePoint) { if (GEngine) GEngine->AddOnScreenDebugMessage(-1, 2.f, FColor::Red, TEXT("No valid grapple target found!")); return; } // --- New Code Start --- TotalTime = 0.1f + (TargetLocation - StartLocation).Size() / GrappleSpeed; // --- New Code End ---Set
GrappleDirectionto the safe normal of the vector fromStartLocationtoTargetLocation.C++TotalTime = 0.1f + (TargetLocation - StartLocation).Size() / GrappleSpeed; // --- New Code Start --- GrappleDirection = (TargetLocation - StartLocation).GetSafeNormal(); // --- New Code End ---Finish setting up the
GrappleCableso the player can see the cable attaching to the grapple point:Set the visibility of
GrappleCableto true.Call
SetAttachEndToComponenton theGrappleCableto set theTargetGrapplePoint’s root component as the attachment point.
C++TotalTime = 0.1f + (TargetLocation - StartLocation).Size() / GrappleSpeed; GrappleDirection = (TargetLocation - StartLocation).GetSafeNormal(); // --- New Code Start --- GrappleCable->SetVisibility(true); GrappleCable->SetAttachEndToComponent(TargetGrapplePoint->GetRootComponent()); // --- New Code End ---You’ve finished setting up and validating grappling variables, so at the end of if
(!bIsLocationInitialized), setbIsLocationInitializedto true so this code only runs once when the player first enters the Grappling Hook Movement Mode.C++if (TargetLocation != FVector::ZeroVector) { // Initialize grapple state, storing start location and direction to the target if (!bIsLocationInitialized) { StartLocation = MoverComp->GetOwner()->GetActorLocation(); GrappleCable = MoverComp->GetOwner()->FindComponentByClass<UCableComponent>(); if (!GrappleCable) {
Implement Grappling-Specific Movement Logic
To advance and track the grapple movement, follow these steps:
In the header file, in the
publicsection, declare two morefloatvariables:C++// How long the current grapple movement has been active float ElapsedTime = 0.f; // Velocity (cm/s) applied after grappling is complete UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Mover") float GrappleExitVelocity = 2500.f;In the
.cppfile, outside of the if (!bIsLocationInitialized) statement, update ElapsedTime, adding DeltaSeconds (the time that has passed since the last frame).You’ll use linear interpolation (lerp) to move the player from their start location to the grapple point over time. Declare a
floatnamedAlphato represent the blend value of the Lerp. The player is at their start position when the time is 0, and at the grapple point when the time isTotalTime.C++if (TargetLocation != FVector::ZeroVector) { // Initialize grapple state, storing start location and direction to the target if (!bIsLocationInitialized) { StartLocation = MoverComp->GetOwner()->GetActorLocation(); GrappleCable = MoverComp->GetOwner()->FindComponentByClass<UCableComponent>(); if (!GrappleCable) {Declare an FVector named
LocationToPlaceActor. Use theLerpfunction from Unreal Engine’sFMathutility library to calculate where the player should be in this frame.C++// Lerping location based on Alpha (Elapsed Time over Total Time) ElapsedTime += DeltaSeconds; float Alpha = ElapsedTime / TotalTime; // --- New Code Start --- FVector LocationToPlaceActor = FMath::Lerp(StartLocation, TargetLocation, Alpha); // --- New Code End ---The setup code at the beginning of
SimulationTickdeclared a scene component namedUpdatedComponent, which is the component the Movement Mode is responsible for moving (usually the Pawn’s root collision component).Call
SetWorldLocation()onUpdatedComponentto move it toLocationToPlaceActor.C++// Lerping location based on Alpha (Elapsed Time over Total Time) ElapsedTime += DeltaSeconds; float Alpha = ElapsedTime / TotalTime; FVector LocationToPlaceActor = FMath::Lerp(StartLocation, TargetLocation, Alpha); // --- New Code Start --- UpdatedComponent->SetWorldLocation(LocationToPlaceActor); // --- New Code End ---Set
UpdatedComponent’sComponentVelocitytoGrappleDirection * GrappleExitVelocity.C++// Lerping location based on Alpha (Elapsed Time over Total Time) ElapsedTime += DeltaSeconds; float Alpha = ElapsedTime / TotalTime; FVector LocationToPlaceActor = FMath::Lerp(StartLocation, TargetLocation, Alpha); UpdatedComponent->SetWorldLocation(LocationToPlaceActor); // --- New Code Start --- UpdatedComponent->ComponentVelocity = GrappleDirection * GrappleExitVelocity; // --- New Code End ---Now that you’ve moved the component, you have to report that movement to Mover using the
OutputSyncState. The position, rotation and velocity ofUpdatedComponentandOutputSyncStatemust match so the visual component and the Mover system’s internal state stay in sync.Call
OutputSyncState.SetTransforms_WorldSpace, which has the following mandatory arguments:OutputSyncState.SetTransforms_WorldSpace(FVector Location, FRotator Orientation, FVector WorldVelocity, FVector AngularVelocity, UPrimitiveComponent *RelativeBase = (UPrimitiveComponent*)nullptr)C++// Lerping location based on Alpha (Elapsed Time over Total Time) ElapsedTime += DeltaSeconds; float Alpha = ElapsedTime / TotalTime; FVector LocationToPlaceActor = FMath::Lerp(StartLocation, TargetLocation, Alpha); UpdatedComponent->SetWorldLocation(LocationToPlaceActor); UpdatedComponent->ComponentVelocity = GrappleDirection * GrappleExitVelocity; // --- New Code Start --- OutputSyncState.SetTransforms_WorldSpace(LocationToPlaceActor,For multiplayer games,
OutputSyncStatekeeps the server synced with clients.
End the Grappling Movement
To finish and transition out of the grapple movement when the player reaches the target, follow these steps:
After moving the player and updating
OutputSyncState, check if the player has reached the grapple point. Add anifstatement that checks ifAlphais close to1.C++// Lerping location based on Alpha (Elapsed time over Total Time) ElapsedTime += DeltaSeconds; float Alpha = ElapsedTime / TotalTime; FVector LocationToPlaceActor = FMath::Lerp(StartLocation, TargetLocation, Alpha); UpdatedComponent->SetWorldLocation(LocationToPlaceActor); UpdatedComponent->ComponentVelocity = GrappleDirection * GrappleExitVelocity; OutputSyncState.SetTransforms_WorldSpace(LocationToPlaceActor, UpdatedComponent->GetComponentRotation(),Inside the
ifstatement, reset the following variables:ElapsedTimebIsLocationInitializedTargetLocation
Set the visibility of
GrappleCabletofalseto hide the cable in game when the player has finished using it. You only want the cable to be visible when using the grappling hook.Normally, you’d also want to stop cable simulations and updates when it isn’t visible to reduce unnecessary calculations.
Queue the Falling Movement Mode.
C++//If Alpha is close to 1, Queue "Falling" Movement Mode and reset variables. if (Alpha >= 0.99f) { // --- New Code Start --- ElapsedTime = 0.f; bIsLocationInitialized = false; TargetLocation = FVector::ZeroVector; GrappleCable->SetVisibility(false); MoverComp->QueueNextMode("Falling", false); // --- New Code End ---
Your finished SimulationTick_Implementation function should look like this:
void UGrapplingHookMovementMode::SimulationTick_Implementation(const FSimulationTickParams& Params, FMoverTickEndData& OutputState)
{
/*
// --- Standard Movement Mode Setup Code STARTS ---
*/
UMoverComponent* MoverComp = GetMoverComponent();
const FMoverTickStartData& StartState = Params.StartState;
// The component this Movement Mode is responsible for moving
USceneComponent* UpdatedComponent = Params.MovingComps.UpdatedComponent.Get();
UPrimitiveComponent* UpdatedPrimitive = Params.MovingComps.UpdatedPrimitive.Get();
Implement Deactivate()
Deactivate() runs when the Movement Mode stops being active, or when the player switches to another Movement Mode. Use this function to clean up any state or references the Movement Mode was using.
If the game or player interrupts the grappling movement, you want to perform the same variable cleanup you did when the grapple movement finished successfully.
To implement Deactivate(), follow these steps:
Add the function header and call
Super::Deactivate();inside the function.Copy and paste the same four properties you reset at the end of
SimulationTick.C++// Reset variables when Movement Mode stops being active void UGrapplingHookMovementMode::Deactivate(const FMoverEventContext& Context, FName NextModeName, const FMoverSimContext& SimContext) // Same thing as if Alpha gets close to 1 - Cleaning references and resetting variables { Super::Deactivate(Context, NextModeName, SimContext); TargetLocation = FVector::ZeroVector; ElapsedTime = 0.f; GrappleCable->SetVisibility(false); bIsLocationInitialized = false; }
Implement OnRegistered() and OnUnregistered()
OnRegistered() runs once when the Movement Mode is connected to the Mover system and ready to use. Use it to perform one-time set up of references like shared settings.
To implement OnRegistered(), follow these steps:
Add the function header and call
Super::OnRegistered(ModeName)inside the function.Get a reference to
CommonLegacyMovementSettings.Get a reference to the player character’s
MoverComponent.C++void UGrapplingHookMovementMode::OnRegistered(const FName ModeName, const FMoverSimContext& SimContext) { Super::OnRegistered(ModeName, SimContext); CommonLegacySettings = GetMoverComponent()->FindSharedSettings<UCommonLegacyMovementSettings>(); ensureMsgf(CommonLegacySettings, TEXT("Failed to find instance of CommonLegacyMovementSettings on %s. Movement may not function properly."), *GetPathNameSafe(this)); }
OnUnregistered() runs when the Movement Mode is removed from the MoverComponent, such as when the MoverComponent is destroyed. Use it for final cleanup, such as unbinding events or clearing external references. For example, if this Movement Mode had subscribed to an event on another component, you would unbind it here. The grappling Movement Mode doesn’t require any cleanup here, but this is an example to show the full Movement Mode lifecycle.
To implement OnUnregistered(), follow these steps:
Call the parent function.
Reset any references set in
OnRegistered()(likeCommonLegacySettings).C++void UGrapplingHookMovementMode::OnUnregistered(const FMoverSimContext& SimContext) { CommonLegacySettings = nullptr; Super::OnUnregistered(SimContext); }Compile and save your code.
Complete Code
Your finished GrapplingHookMovementMode.h file should look like this:
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "MovementMode.h"
#include "CableComponent.h"
#include "GrapplingHookMovementMode.generated.h"
class UCommonLegacyMovementSettings;
Your finished GrapplingHookMovementMode.cpp file should look like this:
// Copyright Epic Games, Inc. All Rights Reserved.
#include "GrapplingHookMovementMode.h"
#include "MoveLibrary/MovementUtils.h"
#include "MoveLibrary/FloorQueryUtils.h"
#include "MoverComponent.h"
#include "DefaultMovementSet/Settings/CommonLegacyMovementSettings.h"
#include "DefaultMovementSet/InstantMovementEffects/BasicInstantMovementEffects.h"
Add Grapple to the Character Blueprint
Now that you’ve implemented the movement behavior, you can add the Movement Mode to BP_ParkourPawn.
Add the Movement Mode
To add the grappling Movement Mode to the player character, follow these steps:
In Unreal Editor, open your Pawn Blueprint.
In the Components panel, click Add to add a GrapplingHookActor component.
Select the ExtendedCharacterMoverComponent.
In the Details panel, in the Mover section, expand Movement Modes.
Click (+) next to the Movement Modes category heading to add another map element.
Enter
GrapplingHookas the mode name. You’ll reference this name in Event Graph logic.In the new map element’s dropdown list, select Grappling Hook Movement Mode.
Connect the Grapple Point Class
Your character’s Grappling Hook Actor Component handles finding grapple targets. When you implemented this Actor Component, you exposed a Grapple Point Class property to Blueprints to tell the component what type of Actor and level object to use for grappling.
To tell the Grappling Hook Actor Component to search for BP_GrapplePoint level objects, follow these steps:
In the Components panel, select GrapplingHookActor.
In the Details panel, in the Grapple section, for Grapple Point Class, select BP_GrapplePoint.
Add the Cable Component
To add the Cable Component to the character, follow these steps:
In the Components panel, select the SK_Mannequin component.
Click + Add, and select Cable. Name it
GrapplingCable.With the Cable Component selected, in the Details panel, in the Sockets section, for Parent Socket, click the folder search icon and select the hand_r bone.
In the Cable section, set:
Cable Length to
300.Num Segments to
5.Cable > Advanced > Skip Cable Update when Not Visible to True.
In the Cable Rendering section, set Cable Width to
8.In the Rendering section, turn off Visible by default.
Queue the Movement Mode
The input action logic in the Pawn’s event graph should search for a valid grapple target and queue the GrapplingHookMovementMode if FindClosestGrappleTarget() finds a target.
When working in node graphs, ensure Context Sensitive is enabled in the node actions list as often as possible so Unreal Engine can automatically add appropriate targets to function nodes for you.
To queue the Movement Mode, follow these steps:
Go to the Pawn’s EventGraph tab and go to the groups of nodes that capture various movement inputs.
Right-click in the empty area of the graph, search for
IA_GrapplingHook, and create a new IA_GrapplingHook node (in the Enhanced Action Event category).Click the arrow at the bottom of the node to see more pin options. From the Started exec pin, create a Branch node. You only want to queue grappling if there is a valid grapple point in range.
Drag off the Condition pin and add a Find Closest Grapple Target function node. Unreal Engine automatically adds the GrapplingHookActor component as the Target.
You created
FindClosestGrappleTarget()in theGrapplingHookActorcomponent class.Drag off the Branch node’s True pin and add a Queue Next Movement Mode node. Unreal Engine automatically adds the Pawn’s Mover Component as the Target.
For the Desired Mode Name pin, enter
GrapplingHook. This must match the name you entered in the Movement Modes list in the Mover Component’s Details panel.Save and compile the Blueprint.
Send Grapple Point Information to the Movement Mode
Your game can now queue the new Movement Mode, but before the first frame of the Movement Mode runs, you’ll need to tell it about the grapple target. Use the OnMovementModeChanged event to set up a Movement Mode instance with the information it needs to perform the movement behavior.
To execute logic when the Grappling Hook Movement Mode becomes active, follow these steps:
In the My Blueprint panel, in the Graphs section, expand EventGraph and double-click Event OnMovementModeChanged_Event to go to that node in the graph.
Delete all nodes connected to the event node.
Drag off the event node’s exec pin and add a Switch on Name node.
Set up the Switch on Name node to have an output for your Grappling Hook movement mode:
In the node, click Add pin.
In the Details panel, uncheck Has Default Pin.
Expand the Pin Names array and rename the first element to
GrapplingHook.
With a Switch on Name node, you can branch off to different configurations depending on which Movement Mode has activated. You’ll add a Wall Running option to this Switch in the next part of this tutorial series.
Connect the event’s New Movement Mode Name pin to the Switch node’s Selection pin.
Now that you know grappling is active, first get the character’s Grappling Hook Movement Mode object from the Mover Component so you can access that instance’s TargetLocation and TargetGrapplePoint variables. FindMovementModeByName returns a base class Movement Mode object, so you need to use a Cast To node on that object to treat it as a Grappling Hook Movement Mode object and access variables specific to that Movement Mode class.
To access the character’s Grappling Hook Movement Mode object, follow these steps:
Drag off the Switch node’s Grappling Hook pin and add a Cast To GrapplingHookMovementMode node.
To the left of the Cast To node, add a Find Movement Mode by Name function node (where the Target is your Extended Character Mover Component).
Set up the Find Movement Mode by Name node:
Connect the Return Value to the Cast To node’s Object pin.
For the Movement Mode Name, enter
GrapplingHook.
To find the closest grapple target and send its information to the Movement Mode instance, follow these steps:
Below the Cast To node, add a Find Closest Grapple Target function node (where the target is GrapplingHookActor).
Right-click the Target Location pin and click Split Struct Pin.
You’ll need to apply an offset to the location’s Z value to lower the character relative to the grapple point, so split the vector into three separate values.
Drag off the Cast To’s top exec pin and add a Set TargetLocation node (under Class > Grappling Hook Movement Mode in the node actions list).
Set up the Set TargetLocation node:
Right-click the Target Location pin and select Split Struct Pin.
Connect the Cast To node’s As Grappling Hook Movement Mode pin to the Set node’s Target pin.
Connect the Find Closest Grapple Target node’s Target Location X and Y pins to the corresponding pins in the Set node.
Connect the function node’s Target Location Z pin to a new Subtract node, and connect the result to the Set node’s Z pin.
In the My Blueprint panel, create a new float variable named GrappleZAxisOffset. Compile the Blueprint, set its default value to
100, and drag the variable onto the Subtract node’s second input pin.This offset should lower the target location the character is flying towards so that their head lines up with the grapple point Actor instead of the middle of their body.
Drag off the Set Target Location node’s exec output pin and add a Set TargetGrapplePoint node.
Set up the new Set TargetGrapplePoint node:
Connect the Cast To node’s As Grappling Hook Movement Mode pin to the new Set node’s Target pin.
Connect the Find Closest Grapple Target node’s Target Grapple output to the Set node.
Save and compile the Blueprint.
The final OnMovementModeChanged_Event logic should look like this:
Test the Grapple Hook Movement
Play your game, aim the camera at your grapple point, and press G to test the grappling movement. You may need to adjust the size of the floor or height of your walls to keep the character in the level.
Watch how the cable attaches to the grapple point. You may need to open BP_GrapplePoint, select the mesh component, and translate the mesh up or down the X axis until the grapple cable always hits the mesh. For example, in the sample level shown in this tutorial, the grapple point’s mesh has a Location transform of (60, 0, 0).
As you test the grappling movement, also pay attention to how the character moves towards the grapple point. Check that the Z offset lines the cable up with the grapple point, and pay attention to the feel of the grappling speed and distance.
If you decide to change the maximum grappling range, remember to update the calculation in BP_GrapplePoint that changes the emissive intensity of the grapple point to indicate when it’s in or out of range of the player.
Adjust Grapple Hook Behavior
To adjust grapple speed and exit velocity, select your Pawn’s ExtendedCharacterMoverComponent and expand the GrappleHook Movement Mode properties.
Next Up
Next, you’ll create a Wall Running Component and Movement Mode that performs line traces to the right and left of the character to search for walls, checks if the player is approaching a wall at an appropriate speed, and then moves the player along the wall.