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?