So recently I wrote my own version of a networked movement component, based heavily on the CMC for my vehicles. See here.
it’s neither finished nor perfect, but something I noticed while going through CMC and trying to learn how it worked, was how convoluted the netcode is for Character Movement. Netcode is split between the Player Controller, Pawn and Movement Component itself - and trying to disect how it works was extremely difficult. It took me a solid couple of weeks of programming on that task alone non-stop, and slow study of the source code (while writing out my own component based on similar methods) to create a more diverse ‘networked movement component’ for my vehicles.
The result was worth it though - I now have a base movement component for any kind of vehicle that handles prediction and reconciliation, and can support different input. By overriding it and and a few functions within, I’ve got predicted movement for flying, hovering, tracked and walking vehicles with relative ease and minimal duplication of code, and I feel as though some of this can be applied to CMC to separate it into a more diverse and reusable ‘networked movement component’, which with little work from another developer could be extended to work with almost any custom movement. My code is by no means perfect, and I still want to split it up more - but it feels like a step in the right direction and Epic’s programmers could no doubt do a better job than me.
I don’t really have many specifics but I do feel like this could be an enormous benefit to the engine, so here are some ideas based on what I can remember from doing it:
- A LOT of the ‘Prediction Data’ can be reused for multiple objects, without the need for each one to process and verify it’s own timestamps and moves etc. A suitable place for this would be the GameNetworkManager, which could cover many of the tasks that CMC does, like ‘speedhack’ protection, timestamp verification, storing and updating ‘FNetworkPredictionData’ for both client and server etc. This would dramatically reduce the complexity of CMC and also enable the developer to more easily predict data for other objects, such as weapon projectiles etc. UT seems to have extensively modified CMC so it can store extra information like whether a projectile was fired that frame etc, which seems like a roundabout way to solve a problem and is again very one-dimensional.
- CMC ‘Assumes’ that it’s owner is a Character, with a capsule root component and a skeletal mesh. This is very limiting if you want to try to modify it for anything other than a Character of similar size and shape. I’ve managed to create mine so that it just requires a separate pair of primitives, one for collision and one for visual representation (required for smoothing updates). My vehicles use a ‘Skeletal Mesh’ as their root object, and it would be nice to change the smoothing code to be able to use the Physics Asset as the collision primitive and offset the visual mesh from that collision for smoothing (which is what I’m looking into). This would cut the amount of scene components required in half and alow for more complex shapes. Once again this would make it easier to diversify the component for any size and shape of object.
- CMC mixes reusable netcode and movement code, which could be easily split into a base ‘networked movement’ class and a child ‘character movmenet’ class. Functions like ‘PhysWalking’ and ‘PhysFlying’ for example would be part of the Character Movement class, while ‘Server_SimulateMove()’ could be part of the base class. The current ‘PhysCustom’ method is another roundabout way of solving the diversification problem. The fact that UT has had to modify the engine that was designed AROUND it should be a testament to how one-dimensional and non-extensible CMC currently is.
- Code is separated between the Player Controller, Pawn and Component. Lines 3879 > 3903 in PlayerController for example could easily be moved into the GameNetworkManager, which feels like a more suitable place for updating Server Prediction Data. Again it feels like the PC is very one-dimensional, assuming the players’ pawn is a humanoid.
- This is a personal gripe of mine - all netcode and AI code in Movement Components assume that “Rotation Follows Velocity”. This rule applies fine for any two-legged biped, but isn’t sufficient for almost anything else, even the most basic vehicles. Even the basic pawn class has a lot of ‘bloat’ because of this, with variables like the ‘InputVector’ and functions like ‘ConsumeInputVector()’, both of which assume the only input required is a world-space directional vector. ‘RequestDirectMove’ (called via UNavMovementComponent for the AI pathfinding), also assumes that all you need is a world-space ‘velocity’ to the next pathpoint - which is quite frustrating and limiting when creating your own movement. Passing an overrideable ‘struct’ instead of a vector to a lot of these functions would make them much more diverse.
In my case, I send an ‘FRepVehicleInput’ struct to the server for predicted movement, instead of a world-space quantized vector. That way if I have a vehicle that needs extra input data, I can just extend this struct and use the same functionality, instead of duplicating it.
- Finally, a lot of CMC can be separated into different files - the same way AActor is. I for example have ‘BZGame_VehicleMovement’ which is a 2,000-line class with all server/client functions and basic functionality for applying velocity and forces to an object. I then have ‘BZGame_VehicleMovementReplication’ which currently stores all struct functions, like ‘FSavedMove_vehicle’ or ‘FNetworkPredictionData_Client_Vehicle’ etc. Again some of this can be split out in to the GameNetworkManager as stated in point 1, which would allow multiple classes to reuse the same code.
- Bonus points are available for creating a physics-based movement component too, which easily supports move replaying etc. I had to give in when doing networked physics, and instead ‘simulate’ physics in my own code. PhysX does all this fancy SIMD stuff that makes it’s collision and solver code much faster than mine could ever be - and it would be great to benefit from that. My idea would be to run two ticks on the component, one pre-physics which calculates and applies all acceleration & forces to the existing physics velocity / angular velocity - then a second post-physics tick which sends the resulting data to the server / out to clients, and where clients compare recieved data from the Server. CMC currently also calculates it’s own collision rejection, and it’s really not that great (or fast) - it can only resolve for two walls maximum which doesn’t suit my vehicles and pays no respect to physical material properties, like restitution or friction etc. As someone making a vehicle based FPS / RTS hybrid - this is of critical importance to me, and the speed difference is required due to the potential for huge numbers of units (way more than Paragon can currently handle, which is discouraging!)
Not expecting this to happen overnight or even in the near future, but I feel like this would be a huge benefit to the engine and pull it away from the Stigma that it’s been designed entirely around making Shooter-Games. UE4 has such a strong heritage in FPS games, but I’d like to see it’s AI and Netcode diversified beyond playing as a two-legged human!
Food for thought