p.AsyncCharacterMovement

p.AsyncCharacterMovement

#Overview

name: p.AsyncCharacterMovement

This variable is created as a Console Variable (cvar).

It is referenced in 10 C++ source files.

#Summary

#Usage in the C++ source code

The purpose of p.AsyncCharacterMovement is to enable asynchronous simulation of character movement on the physics thread in Unreal Engine 5. This setting variable is primarily used for the character movement system.

The Unreal Engine subsystem that relies on this setting variable is the Character Movement Component, which is part of the Engine module. This can be seen from the file paths and function names in the callsites.

The value of this variable is set through a console variable (CVar) system. It’s initialized to 0 and can be changed at runtime, although toggling it at runtime is not recommended.

The associated variable AsyncCharacterMovement interacts directly with p.AsyncCharacterMovement. They share the same value and are used interchangeably in the code.

Developers must be aware of several important points when using this variable:

  1. This feature is not fully developed, and its use is discouraged, as stated in the comment.
  2. Toggling this setting at runtime is not recommended.
  3. When enabled (set to 1), it changes the behavior of several functions in the CharacterMovementComponent, including TickComponent, PostPhysicsTickComponent, and RegisterComponentTickFunctions.
  4. It enables asynchronous input processing and output application for character movement.

Best practices when using this variable include:

  1. Use it only for testing or development purposes, not in production code, due to its experimental nature.
  2. If used, set it at the beginning of the game and avoid changing it during runtime.
  3. Be prepared to handle potential inconsistencies or unexpected behavior in character movement when this is enabled.
  4. Always test thoroughly when this setting is enabled, as it significantly changes the character movement simulation pipeline.

Regarding the associated variable AsyncCharacterMovement:

The purpose of AsyncCharacterMovement is to serve as an internal flag within the CharacterMovementComponent to check if asynchronous character movement is enabled. It directly reflects the value of p.AsyncCharacterMovement.

This variable is used in various parts of the CharacterMovementComponent to conditionally execute code related to asynchronous movement. For example, it’s used to determine whether to consume input vectors immediately or defer them for asynchronous processing, and whether to register and use asynchronous callbacks for movement simulation.

Developers should be aware that this variable is declared as an extern variable, meaning it’s defined elsewhere (in this case, in the CVar system) and shared across multiple source files. Any changes to p.AsyncCharacterMovement will directly affect the behavior controlled by AsyncCharacterMovement.

Best practices for AsyncCharacterMovement are similar to those for p.AsyncCharacterMovement, as they are essentially the same setting. Developers should treat it as a read-only flag within their code and rely on the CVar system to modify its value if necessary.

#References in C++ code

#Callsites

This variable is referenced in the following C++ source code:

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:238

Scope (from outer to inner):

file
namespace    CharacterMovementCVars

Source code excerpt:

	int32 AsyncCharacterMovement = 0;
	FAutoConsoleVariableRef CVarAsyncCharacterMovement(
		TEXT("p.AsyncCharacterMovement"),
		AsyncCharacterMovement, TEXT("1 enables asynchronous simulation of character movement on physics thread. Toggling this at runtime is not recommended. This feature is not fully developed, and its use is discouraged."));

	int32 BasedMovementMode = 2;
	FAutoConsoleVariableRef CVarBasedMovementMode(
		TEXT("p.BasedMovementMode"),
		BasedMovementMode, TEXT("0 means always on regular tick (default); 1 means only if not deferring updates; 2 means update and save based movement both on regular ticks and post physics when on a physics base."));

#Associated Variable and Callsites

This variable is associated with another variable named AsyncCharacterMovement. They share the same value. See the following C++ source code.

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Classes/GameFramework/CharacterMovementComponent.h:41

Scope (from outer to inner):

file
namespace    CharacterMovementCVars

Source code excerpt:

{
	// Is Async Character Movement enabled?
	extern ENGINE_API int32 AsyncCharacterMovement;
	extern int32 ForceJumpPeakSubstep;
}

/** 
 * Tick function that calls UCharacterMovementComponent::PostPhysicsTickComponent
 **/

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:236

Scope (from outer to inner):

file
namespace    CharacterMovementCVars

Source code excerpt:

		ECVF_Default);
		
	int32 AsyncCharacterMovement = 0;
	FAutoConsoleVariableRef CVarAsyncCharacterMovement(
		TEXT("p.AsyncCharacterMovement"),
		AsyncCharacterMovement, TEXT("1 enables asynchronous simulation of character movement on physics thread. Toggling this at runtime is not recommended. This feature is not fully developed, and its use is discouraged."));

	int32 BasedMovementMode = 2;
	FAutoConsoleVariableRef CVarBasedMovementMode(
		TEXT("p.BasedMovementMode"),
		BasedMovementMode, TEXT("0 means always on regular tick (default); 1 means only if not deferring updates; 2 means update and save based movement both on regular ticks and post physics when on a physics base."));

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:576

Scope (from outer to inner):

file
function     UCharacterMovementComponent::UCharacterMovementComponent

Source code excerpt:

	PostPhysicsTickFunction.TickGroup = TG_PostPhysics;
	
	if (CharacterMovementCVars::AsyncCharacterMovement == 1)
	{
		PostPhysicsTickFunction.bStartWithTickEnabled = true;
		PostPhysicsTickFunction.SetTickFunctionEnable(true);
		
		PrePhysicsTickFunction.bCanEverTick = true;
		PrePhysicsTickFunction.bStartWithTickEnabled = true;

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:1511

Scope (from outer to inner):

file
function     void UCharacterMovementComponent::TickComponent

Source code excerpt:


	FVector InputVector = FVector::ZeroVector;
	bool bUsingAsyncTick = (CharacterMovementCVars::AsyncCharacterMovement == 1) && IsAsyncCallbackRegistered();
	if (!bUsingAsyncTick)
	{
		// Do not consume input if simulating asynchronously, we will consume input when filling out async inputs.
		InputVector = ConsumeInputVector();
	}

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:1678

Scope (from outer to inner):

file
function     void UCharacterMovementComponent::PostPhysicsTickComponent

Source code excerpt:

	if (bDeferUpdateBasedMovement)
	{
		if(CharacterMovementCVars::AsyncCharacterMovement == 1)
		{
			ensure(false); // Not supported
		}
		FScopedCapsuleMovementUpdate ScopedMovementUpdate(UpdatedComponent, bEnableScopedMovementUpdates);
		UpdateBasedMovement(DeltaTime);
		SaveBaseLocation();

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:11430

Scope (from outer to inner):

file
function     void UCharacterMovementComponent::RegisterComponentTickFunctions

Source code excerpt:

	if (bRegister)
	{
		if (CharacterMovementCVars::AsyncCharacterMovement == 1 && SetupActorComponentTickFunction(&PrePhysicsTickFunction))
		{
			PrePhysicsTickFunction.Target = this;
			PrePhysicsTickFunction.AddPrerequisite(this, this->PrimaryComponentTick);
		}
		if (SetupActorComponentTickFunction(&PostPhysicsTickFunction))
		{

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:13230

Scope (from outer to inner):

file
function     void UCharacterMovementComponent::BuildAsyncInput

Source code excerpt:

void UCharacterMovementComponent::BuildAsyncInput()
{
	if (CharacterMovementCVars::AsyncCharacterMovement == 1  && IsAsyncCallbackRegistered())
	{
		FCharacterMovementComponentAsyncInput* Input = AsyncCallback->GetProducerInputData_External();
		if (Input->bInitialized == false)
		{
			Input->Initialize<FCharacterMovementComponentAsyncInput::FCharacterInput, FCharacterMovementComponentAsyncInput::FUpdatedComponentInput>();
		}

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:13364

Scope (from outer to inner):

file
function     void UCharacterMovementComponent::ProcessAsyncOutput

Source code excerpt:

void UCharacterMovementComponent::ProcessAsyncOutput()
{
	if (CharacterMovementCVars::AsyncCharacterMovement == 1 && IsAsyncCallbackRegistered())
	{
		while (auto Output = AsyncCallback->PopOutputData_External())
		{
			ApplyAsyncOutput(*Output);
		}
	}

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:13375

Scope (from outer to inner):

file
function     void UCharacterMovementComponent::RegisterAsyncCallback

Source code excerpt:

void UCharacterMovementComponent::RegisterAsyncCallback()
{
	if (CharacterMovementCVars::AsyncCharacterMovement == 1)
	{
		if (UWorld* World = GetWorld())
		{
			if (FPhysScene* PhysScene = World->GetPhysicsScene())
			{
				AsyncCallback = PhysScene->GetSolver()->CreateAndRegisterSimCallbackObject_External<FCharacterMovementComponentAsyncCallback>();