Setting GetCharacterMovement()->Velocity is Laggy?

Ive been trying to track down the source of some lag in my game as of late, and have narrowed it down the following line: GetCharacterMovement()->Velocity = CurrentVelocity;
It is at the end of a chain of functions that is initially called within the Pawn’s Tick().

During stress testing I spawn pawns incrementally up to 100.
At around 45 pawns my FPS dips below 60, and continues to dip with each new pawn.
Commenting this line out allows the game to maintain 60 FPS up to all 100 pawns, but I am using that line to move the pawn so it is kind of important. :stuck_out_tongue:

Is there some appropriate way to manually set the velocity of the UCharacterMovementComponent, that is not so heavy on FPS?
Or is there just an expected FPS hit when UCharacterMovementComponent is moving a pawn?

As I said in the previous thread, Character Movement Component is computationally expensive.

Setting the Velocity variable isn’t expensive, but the code that moves the character is. If you want to simulate more than 20-25 characters (hell much less than that on some platforms), then you’ll need to create a faster character movement component. CMC unfortunately doesn’t scale well to large volumes of characters.

Paragon I believe has a simplified movement component for it’s minions, which snaps them to the NavMesh.

EDIT: If your’re spawning pawns with meshes, you’re also paying the cost of skeletal mesh updates and rendering - which has an inherent cost of it’s own.

Im not using SkeletalMeshes for Pawns, I mean in C++ I just put “Mesh = NULL” in the constructor.
In the editor the SkeletalMesh is still in the list of components(inherited), but clicking it doesn’t show any of the variable lists that would normally show up and it doesn’t appear to have anything visible in game or in the viewport. Is there still a cost for an inherited skeletal mesh that = NULL?

Im also not using a NavMesh for Movement. I set almost everything in the CMC to 0 except MaxFlySpeed(and simulation step related stuff), and set Default Movement to Flying. Im using CMC primarily because it handles Network movement. Ill go look, but do you know of any tutorials out there for building custom MoveComponents?

There’s a generic cost for having the Skeletal Mesh Component itself, but without any assigned mesh or anims etc it shouldn’t be very much.

CMC does indeed handle net movement but that’s where a big chunk of the cost comes from. Realistically you only really want to use it for ‘hero’ players probably (i.e. actual humans or AI bots, like UT bots etc). I don’t know of any good tutorials I’m afraid since it’s a pretty complex subject, but a (simple) movement component that doesn’t require human control isn’t too hard to implement.

Ok. This is a long one…

So I made a subclass of UCharacterMovementComponent so I could do some testing.
I started by overriding TickComponent(), and systematically commenting out sections to see which calls in that function caused the lag.
Once the lag causing call was found, I overrode that function and repeated the process to narrow down which call in each subsequent function was causing the lag.
In each function the lag could, fortunately, be narrowed down to a single call.

TickComponent()
> PerformMovement()
> StartNewPhysics()
> PhysFlying() (in this case)
> SafeMoveUpdatedComponent()
> MoveUpdatedComponent()
> MoveUpdatedComponentImpl()
> UpdatedComponent->MoveComponent()
> UpdatedComponent->MoveComponentImpl()
(in this case it was PhysFlying(), because thats the Move_Mode my pawn uses. The lag inducing call would be to which ever mode the pawn uses)

Simply commenting out just any one of these functions both stops movement, and fully removes the cost.
Which is fairly obvious. If the pawn aint moving then there shouldn’t be any lag.
But here is the interesting bit:
SafeMoveUpdatedComponent() is the last function in UCharacterMovementComponent.
MoveUpdatedComponent(), and MoveUpdatedComponentImpl() is a UMovementComponent function; MoveComponent() and MoveComponentImpl() are USceneComponent functions.
MoveUpdatedComponent(), MoveUpdatedComponentImpl(), and MoveComponent() don’t do much of anything other than call the next function in that line.

This suggests to me that there is nothing special about UCharacterMovementComponent in terms of cost.
It is no more “computationally expensive” than any other USceneComponent.
(In theory with the right checks I could just call UpdatedComponent->MoveComponent() directly from the pawn, circumventing the MoveComponent all together, and still get the lag)

Unfortunately I could not override MoveComponentImpl(), but looking at where the calls go, I think Ive narrowed it down to two functions:
ConditionalUpdateComponentToWorld()
UpdateOverlaps()

Both of which appear to involve looping through all the child Components of the UMovementComponent’s UpdatedComponent(otherwise known as my Pawn’s RootComponent) to either update their transform or check for overlaps.

Based on all that I did a quick test: reverted back to that standard CharacterMoveComponent and removed all the StaticMeshComponents from my Pawn except one.
(I dynamically spawn a bunch on it to visually show things like shields/armor/weapons/gear ect. I had around 11 of them)
Result:
60 FPS all the way up to 100 pawns, and they can all move normally.

While this is great in terms of getting good performance with movement, It isn’t so great for my gear because I had planned for the gear to have collision and be able to be shot off.
As it stands I can’t even make them visible. Any suggestions for making the Component checks less costly while retaining collision and quantity of StaticMeshComponents?

On the locally controlled client-side, Character movement is constantly replaying old moves from it’s saved move buffer, so even though you only see one frame worth of movement, it could be simulating up to 96 (default) frames of movement in one frame - this is because of network prediction and is required for smooth, instantaneous movement on a client.

Another expensive part of CMC is the movement smoothing. Clients smooth the movement of non-local pawns so that they don’t snap to different positions as they get updates from the server, but the smoothing can also be costly to run and take up a lot of cycles. There are loads of debug console commands and stat groups for profiling CMC, so you can drill down and find out what’s expensive.

MoveComponent in USceneComponent is where a large portion of cost comes from - since that’s where the capsule is moved and sweeps for collision. Also because Character Movement handles collision locally through functions like SlideAlongSurface and HandleImpact. Several movements and collision tests occur during a single movement, which slowly increases the cost. It also does other things that are biped-movement specific. Even something as simple as walking up ramps and stairs is taken for granted with CMC.

As for collision, the simplest way to improve collision performance is with filtering. Make sure the components don’t collide with each other and their character, and are only affected by what they need to be hit by.

Im testing SP at the moment.
MP will have significantly less pawn count considerations, so I don’t think movement smoothing or saved moves will be an issue.
Or at least it will be an issue I can work around. SP tho needs to have potentially a lot of pawns moving around to match up to the intended theme of the game.

The stress test map is a 320000 by 320000 square(and is considered on the small side for SP), and the test pawns are around 450 radius each and fly.
There is a single 1500 radius cylinder piece of map geometry that they could collide with, but aside from that the map is completely empty.

I doubt colliding with other actors/geometry is the issue…

I did some new testing.
In the code I set all UStaticMeshComponents(except one) to ignore all collisions via: SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);
This improved performance, maintaining 60 FPS to about 55 pawns.

Then in the Editor I removed all the collision from the StacticMeshes those components used.
This improved performance, maintaining 60 FPS to about 70 pawns. Even at 100 pawns FPS did not dip below 35.

Even if it means working out a different way to shoot off gear I think I can work with this level of performance.
(I have another performance related issue, but Im going to post that in a new thread as its more about player input than staticmeshes and Pawn movement)