Setup for Kinematic MoverComponent to use Async Movement Modes

Note this is a similar question to -> Mover - running simulation on any thread issues

But not the same (as I am using Async movement modes, unlike in the question above)

I am evaluating the Mover Component for our character.

I’ve started with the basic CharacterMoverComponent as I couldn’t get the Physics based mover working in 5.7 Examples (the character would not move at all).

I’ve got the character working, and I wanted to evaulate the Async movement modes to see if we can use those at all.

So I swapped out the Walking, Falling & Flying modes for UAsyncFallingMode, UAsyncFlyingMode & UAsyncWalkingMode.

So far so good, but that doesn’t make them Async as they appear to need a different Liason to do that.

I then made my own Liason, that enables Async ticking.

- First problem is, I can’t call SetUseAsyncMovementSimulationTick() because it will not actually turn on Async movement as there is a global that forces it to be off? Not sure why that is useful? So I’ve had to copy the function bodies out into the constructor of my Liason (constructor and method pasted below) and remove the reference to the global/cvar.

UUMoverStandaloneLiaisonAsyncComponent::UUMoverStandaloneLiaisonAsyncComponent()

{

// NOTE:  Not calling \#SetUseAsyncProduceInput \& SetUseAsyncMovementSimulationTick because

// they check a global CVar and disable the async by default? Not sure why Async is all off by default and 

// requires a CVar command to turn on?



bUseAsyncProduceInput \= true;

ProduceInputTickFunction.bRunOnAnyThread \= bUseAsyncProduceInput;



bUseAsyncMovementSimulationTick \= true;

SimulateMovementTickFunction.bRunOnAnyThread \= bUseAsyncMovementSimulationTick;

}

bool UUMoverStandaloneLiaisonAsyncComponent::IsAsync() const

{

return true;

}

- Next problem, is the mover then complains that the movement modes are NOT async during data validation, so despite being called UAsyncFallingMode etc, they are marked as Async = false by default, so I had to go in and change all the mode to be async on each mode in the data on the character.

- So after that, I can run the game and I drop a few characters into a level to see what they will do. Pretty much instantly crashes at this point, the call stack is below.

It looks like the Mover is attempting to turn the component tick back on for the Mover, because the character now has a movement base and it wants a new tick to enable a tick pre-requisite? so the mover can get ticked before the base? (see callstack in the repro steps).

----

It’s crashing trying to remove the tick function, and it looks like it’s access a global data structure of ticks for the level?

if (bInEnabled == (TickState == ETickState::Disabled))

    {

        FTickTaskLevel\* TickTaskLevel \= InternalData\-\>TickTaskLevel;

        check(TickTaskLevel);

        TickTaskLevel\-\>RemoveTickFunction(this);

-----

So my question is? Is what I am doing a valid setup? or is this just not possible? And if so, can you setup a character with kinematic movement that actually works async?

Seems like if I am a kinematic mover and I go Async, then I’m always going to hit problems with this code below, which ends up running on worker threads.

And the linked question also said that MotionWarping isn’t compatible, so is that still the case? as then async might be a non-starter for us anyway, as we have too much to rely on that feature to give it up.

if (bSupportsKinematicBasedMovement)

{ 

    UpdateBasedMovementScheduling(SimOutput);

}

----

Many Thanks

Dave

Steps to Reproduce
This is the call stack from the crash I get when setting up the mover. The steps to get this are covered in the issue:

[Inlined] FSetElementId::IsValidId() SetUtilities.h:97

[Inlined] TSet::RemoveImpl(unsigned int, const FTickFunction *const &) Set.h:893

TSet::Remove(const FTickFunction *) Set.h:930

FTickFunction::SetTickFunctionEnable(bool) TickTaskManager.cpp:2497

UMoverComponent::UpdateBasedMovementScheduling(const FMoverTickEndData &) MoverComponent.cpp:1058

UMoverComponent::SimulationTick(const FMoverTimeStep &, const FMoverTickStartData &, FMoverTickEndData &) MoverComponent.cpp:657

UMoverStandaloneLiaisonComponent::TickMovementSimulation(float) MoverStandaloneLiaison.cpp:373

[Inlined] FMoverStandaloneSimulateMovementTickFunction::ExecuteTick::__l2::<lambda_1>::operator()(float) MoverStandaloneLiaison.cpp:467

FActorComponentTickFunction::ExecuteTickHelper<`FMoverStandaloneSimulateMovementTickFunction::ExecuteTick’::`2’::<lambda_1> >(UActorComponent *,bool,float,ELevelTick,const <lambda_1> &) Actor.h:4910

FMoverStandaloneSimulateMovementTickFunction::ExecuteTick(float, ELevelTick, Type, const TRefCountPtr<…> &) MoverStandaloneLiaison.cpp:465

FTickFunctionTask::DoTask(Type, const TRefCountPtr<…> &) TickTaskManager.cpp:348

..

LowLevelTasks::FScheduler::CreateWorker’::`2’::<lambda_1>::operator()() Scheduler.cpp:188

[Inlined] UE::Core::Private::Function::TFunctionRefBase::operator()() Function.h:471

FThreadImpl::Run() Thread.cpp:66

FRunnableThreadWin::Run() WindowsRunnableThread.cpp:156

FRunnableThreadWin::GuardedRun() WindowsRunnableThread.cpp:71

Hey Dave,

TLDR, yes kinematic async movement is possible with a Standalone game net mode (no server/client) and UMoverStandaloneLiaisonComponent. That said there are some things to keep in mind in addition to the net mode and liaison:

  1. Requires use of the async-friendly movement modes (like UAsyncWalkingMode)
  2. Requires enabling the global CVar mover.standalone.RunMovementSimOnAnyThread which gives backends the option of running async
  3. Each individual actor needs SetUseAsyncMovementSimulationTick(true) set on their backend. This can just be done in BeginPlay of the actor or component on actor by finding the MoverStandaloneLiaisonComponent and calling it there (can even be done in Blueprint). Setting async input production can also be done in a similar manner.
  4. At the moment Mover’s based movement is known to not work with a async movement simulation yet. You can set bSupportsKinematicBasedMovement to false to avoid this if needed.

MotionWarping still isn’t compatible out of the box with an async simulation but it could work with just a couple of modifications if you really want to pursue/explore it. Adding something like this: “Params.bSkipPostLoad = true;” to UMotionWarpingComponent::AddModifierFromTemplate alongside some changes to various ensures to enable reading CVars from any thread (to avoid ensures from GetValueOnGameThread) should get motion warping working. I haven’t tested it extensively but a colleague had some success running motion warping during async movement with these changes.

Thanks,

Nate

Thanks. I basically had already done points 1 to 3 that you mentioned.

As for point 4. The kinematic based movement. I can turn that off, but that means that based movement won’t work, which would be a showstopper for using this mover.

Is there any plan to support this? (I’m assuming it’s also the same in 5.8, which I’m downloading right now to evaluate).

Hey James,

Just confirming that we do plan to support this but there are some bugs with it we need to solve. I made a ticket for it so you can track the status of it: UE-391002. It should show up soon once it’s made public

Thanks,

Nate