How to implement Custom Movement Functionality in Pawns

Hi,
I am currently working on a project for which I want to use a custom MovementComponent. At the end I would like to have a Pawn, that can be controlled by an AIController and that has custom Movement functionality. The reasons why I don’t want to inherit from the UCharacterMovementComponent is firstly, that for my project I simply do not need most of the functionality. More simplistic Movement would be better. Secondly because I like using self-made systems, since then I am much more familiar with them and can use them much more efficiently. And thirdly, simply because it will teach me a lot about UE and programming in general, especially when it comes to movement replication. However I have run into a few problems. I’ve spent two days browsing the source-code behind the AI-controller, MovementComponent hierarchy and the Character hierarchy, but since it is a lot of code and I only have a few years of coding experience, I only understand parts of it and this method doesn’t seem to work. So I would like to ask the following three questions. I inherit from APawn, AAIController and UPawnMovementComponent.

My first question is: Which function does the AIController use in order to move a Pawn? In other words: Which function do I have to override in oder to make the AIController move my Pawn? For me it looked like it calls “RequestDirectMove” or “RequestPathMove” in the UNavMovementComponent, but when I overrode those to display the vector they produce, I got nonsensical, weird results (Zeros in the case of the latter and very big numbers in the case of the former, which became 0 when I tried to normalise them. However the at least seemd to go in the right general direction). So which function of which class am I suppest to override when trying to make the AIController call custom movement functionality (During pathfinding primarily)?

My second question: When I try to move my a pawn, does a Movement function that implements collision already exist, or do I have to make one on my own? In the former case: Which function would that be? In the latter I would really appreciate a quick reference to where I can find further information about this topic. I understand the theory behind collision, but I have no idea which functions have to be used in practise and which source variables I can use.

My third question revolves around movement replication, or replication in general. Its fairly simple: If I set a variable to be “replicated”, how much can I rely on the engine to replicate it efficiently? If said variable changes every frame, updating it ever frame would be costly. Can I rely on the engine to space updates in a way, that doesn’t slow down the overall connection? What if I had 100 variables like that, obviously a single variable had to receive fewer updates, but does the engine think of that? Or do I have to make sure that the connection isn’t flooded with information? And a small bonus question: I know that I can set the repliaction-priority for Actors, but is there a way to do the same for single variables?

I have looked through countless tutorials and, as mentioned, I have spent many hours browsing the source code of ACharacter, AAIController, the classes they inherit from and their Components, but I got no useful results with that method. So I would really appreciate someone helping me out here. Thank you in advance.

1 Like

Hello!

  • Which function does the AIController use in order to move a Pawn?
    Here you can think of two possibilities - using Navigation and not using Navigation. For the first case AIController just use UPathFollowingComponent inside it to find a path and simulate inputs. This component use Navigation data for area and agent to make movement request. It is using StopMovementKeepPathing, UseAccelerationForPathFollowing, RequestPathMove, RequestDirectMove, GetMaxSpeed, GetPathFollowingBrakingDistance, CanStartPathFollowing methods of UNavMovementComponent. For the second case you can simply emulate AddInputVector in your APawn actor class…

  • When I try to move my a pawn, does a Movement function that implements collision already exist, or do I have to make one on my own?
    Most common thing is inside UMovementComponent::SafeMoveUpdatedComponent, however if you need stairs stepping and jumping off then take a look at UCharacterMovementComponent.

As for the third one, I havent worked a lot with replication, so lets wait for one who does =)

If bUseAccelerationForPathFollowing is TRUE then special Input Vector is calculated. As you know Input Vector is then used to calc Acceleration and only after that we get Velocity update. When bUseAccelerationForPathFollowing is FALSE all calculations are out, we simply get velocity from target point and current point, so we dont need to calculate input or acceleration in that case

Thank you, that was very helpful. The path following seems to work perfectly by overriding “RequestDirectMove” and using a normalized version of the “MoveVelocity” as input for “SafeMoveUpdatedComponent”. Just two quick question, what does “bUseAccelerationForPathFollowing” do and whats the difference between “RequestPathMove” and “RequestDirectMove”? All I found out, is that if “bUseAccelerationForPathFollowing” is true, “RequestPathMove” gets called instead of “RequestDirectMove”.

Makes sense, thank you

Ok I 've seen you have went far, but for argument’s sake, I am giving you the implementation of a project I made with some cartoon ships (it’s very simple, without replication).First I override the AIController’s Tick function to move to an acceptance radius from the Player’s Ship if it’s alive:

void AShipAIController::Tick(float DeltaSeconds)
{
Super::Tick(DeltaSeconds);
auto PlayerShip = Cast<AShipBase>(GetWorld()->GetFirstPlayerController()->GetPawn());
auto ControlledShip = Cast<AShipBase>(GetPawn());

if (PlayerShip->IsAlive()) 
{ // Move towards the player
MoveToActor(PlayerShip, AcceptanceRadius, true, true, false);
}
}

Now, in order for the AI Ship to Move “normally”, as if a Player controlled it, I have implemented a custom UNavMovementComponent, with the following functions:

.H

UFUNCTION(BlueprintCallable, Category = "RumRamShip|Input")
void MoveForward(float AxisValue);
UFUNCTION(BlueprintCallable, Category = "RumRamShip|Input") void Steer(float AxisValue);
virtual void RequestDirectMove(const FVector& MoveVelocity, bool bForceMaxSpeed) override;

.CPP

void UShipNavMovementComponent::RequestDirectMove(const FVector& MoveVelocity, bool bForceMaxSpeed)
{
	// No need to call Super as we're replacing the functionality
	// bForceMaxSpeed = true;
	auto ShipForward = GetOwner()->GetActorForwardVector().GetSafeNormal();
	auto AIForwardIntention = MoveVelocity.GetSafeNormal();

	auto ForwardThrow = FVector::DotProduct(ShipForward, AIForwardIntention);
	MoveForward(ForwardThrow); // Move Forward / Backward

	auto RightThrow = FVector::CrossProduct(ShipForward, AIForwardIntention).Z;
	Steer(RightThrow); // Turn Left/Right
	
}

340606-screenshot-2021-06-03-184403.png

Now if the Ship is Player controlled, It receives the Input Actions. If its AI, it just consumes MoveForward and Steer internally.

Hope that helps.

What if I had 100 variables like that, obviously a single variable had to receive fewer updates, but does the engine think of that?

Yes it does, and you can control it to some extent.

Replicated actors have a bunch of properties that let you define how important they are, and who they should replicate to.

340677-actor-rep.png

These parameters apply to all replicated variables within the class. Frequency let you update lesser important classes less often. Priority tells the engine which parts to stop updating when connection is getting flooded.

Typically classes like Controller/Character have a high priority and frequency because they are very important for gameplay experience, while class like GameState/PlayerState (which replicate more like scores and stats information) have a low frequency and priority.

Variables can also have their own replication conditions, but those are different. By default, variables are checked all the time and they are replicated if they have changed since last replication.

340678-variable-rep.png

Check the docs for the meaning of all these.

Okay, thank you. I’ve still got two or three question though
How does the Net Priority and Frequency impact PPCs?
Also (Thats more of a opinion-related answer maybe) but is it a good idea to have something like velocity (Which probably changes a lot) be replicated in order to check if client and server are still in synch (Not as primary replication method, more as a fail safe)?
And last but not least, how often is it possible to call RPCs generally. Is it possible to call a few RPCs per frame even (Especially in movement related replication there might be a few things where per frame replication is important. Input Vectors for example might change a lot) or should a developer try to keep the amount of RPC calls down at any price?